From a0122e177ef541f50163383e36ca9bc0dbc624e4 Mon Sep 17 00:00:00 2001 From: Sergey Petrov Date: Mon, 13 Dec 2021 21:49:40 +0000 Subject: [PATCH] v1.25.0 release --- .gitignore | 2 + .gitlab-ci.yml | 20 +- CHANGELOG.md | 13 + SUPPORT.md | 3 +- contributing/README.md | 2 +- contributing/process_release.md | 4 + docs/conf.py | 2 +- docs/output-example.rst | 3 + docs/revision-history.rst | 8 +- docs/setting-up-consumer.rst | 463 ++- docs/telemetry-system.rst | 2 + .../declarations/aws_cloudwatch_logs.json | 5 +- .../declarations/aws_cloudwatch_metrics.json | 5 +- examples/declarations/aws_s3.json | 5 +- examples/declarations/data_dog.json | 10 +- .../google_cloud_logging_sat.json | 12 + .../google_cloud_monitoring_sat.json | 11 + .../declarations/open_telemetry_exporter.json | 3 +- examples/declarations/statsd.json | 3 +- examples/output/system_poller/output.json | 5 + package-lock.json | 3581 +++++++++++++---- package.json | 33 +- src/lib/config.js | 30 +- src/lib/constants.js | 3 - src/lib/consumers.js | 13 +- src/lib/consumers/AWS_CloudWatch/index.js | 2 +- src/lib/consumers/AWS_S3/index.js | 8 +- .../Azure_Application_Insights/index.js | 2 +- .../consumers/Azure_Log_Analytics/index.js | 5 +- src/lib/consumers/DataDog/index.js | 33 +- src/lib/consumers/ElasticSearch/index.js | 4 +- src/lib/consumers/F5_Cloud/index.js | 2 +- src/lib/consumers/Generic_HTTP/index.js | 2 +- .../consumers/Google_Cloud_Logging/index.js | 6 +- .../Google_Cloud_Monitoring/index.js | 7 +- .../consumers/OpenTelemetry_Exporter/index.js | 14 +- src/lib/consumers/Splunk/dataMapping.js | 10 +- src/lib/consumers/Splunk/index.js | 9 +- .../Splunk/multiMetricEventConverter.js | 8 +- src/lib/consumers/Statsd/index.js | 6 +- src/lib/consumers/shared/awsUtil.js | 26 +- src/lib/consumers/shared/azureUtil.js | 33 +- src/lib/consumers/shared/gcpUtil.js | 126 +- src/lib/consumers/shared/metricsUtil.js | 9 + src/lib/customKeywords.js | 11 +- src/lib/dataFilter.js | 1 - src/lib/dataPipeline.js | 9 +- src/lib/dataTagging.js | 3 +- src/lib/declarationValidator.js | 3 +- src/lib/endpointLoader.js | 2 +- src/lib/eventListener/baseDataReceiver.js | 5 +- src/lib/eventListener/dataPublisher.js | 4 +- src/lib/eventListener/index.js | 23 +- src/lib/eventListener/messageStream.js | 7 +- src/lib/eventListener/tcpUdpDataReceiver.js | 9 +- src/lib/forwarder.js | 3 +- src/lib/ihealth.js | 23 +- src/lib/ihealthPoller.js | 29 +- src/lib/normalize.js | 9 +- src/lib/persistentStorage.js | 2 - src/lib/properties.json | 2 +- src/lib/pullConsumers.js | 23 +- src/lib/pullConsumers/Prometheus/index.js | 5 +- src/lib/pullConsumers/default/index.js | 4 +- src/lib/requestHandlers/declareHandler.js | 2 +- .../requestHandlers/eventListenerHandler.js | 2 +- .../requestHandlers/ihealthPollerHandler.js | 4 +- src/lib/requestHandlers/infoHandler.js | 2 +- .../requestHandlers/pullConsumerHandler.js | 2 +- src/lib/requestHandlers/router.js | 3 +- .../requestHandlers/systemPollerHandler.js | 4 +- src/lib/systemPoller.js | 37 +- src/lib/systemStats.js | 14 +- src/lib/teemReporter.js | 2 +- src/lib/utils/config.js | 306 +- src/lib/utils/data.js | 10 +- src/lib/utils/datetime.js | 8 +- src/lib/utils/device.js | 14 +- src/lib/utils/eventEmitter.js | 2 +- src/lib/utils/ihealth.js | 5 +- src/lib/utils/metadata.js | 2 +- src/lib/utils/misc.js | 2 - src/lib/utils/monitor.js | 4 +- src/lib/utils/normalize.js | 16 +- src/lib/utils/promise.js | 6 +- src/lib/utils/requests.js | 1 - src/lib/utils/systemStats.js | 1 - src/lib/utils/timers.js | 7 +- src/lib/utils/tracer.js | 20 +- src/nodejs/restWorker.js | 1 - src/schema/1.25.0/actions_schema.json | 187 + src/schema/1.25.0/base_schema.json | 310 ++ src/schema/1.25.0/consumer_schema.json | 1357 +++++++ src/schema/1.25.0/controls_schema.json | 52 + src/schema/1.25.0/endpoints_schema.json | 158 + src/schema/1.25.0/ihealth_poller_schema.json | 238 ++ src/schema/1.25.0/listener_schema.json | 85 + src/schema/1.25.0/namespace_schema.json | 92 + src/schema/1.25.0/pull_consumer_schema.json | 101 + src/schema/1.25.0/shared_schema.json | 50 + src/schema/1.25.0/system_poller_schema.json | 242 ++ src/schema/1.25.0/system_schema.json | 121 + src/schema/latest/base_schema.json | 6 +- src/schema/latest/consumer_schema.json | 124 +- stryker.conf.js | 29 + test/README.md | 9 + test/functional/cloud/awsTests.js | 14 +- test/functional/cloud/azureTests.js | 14 +- test/functional/consumerSystemTests.js | 3 +- .../azureApplicationInsightsTests.js | 3 +- .../consumersTests/azureLogAnalyticsTests.js | 9 +- .../consumersTests/elasticsearchTests.js | 6 +- .../functional/consumersTests/f5CloudTests.js | 14 +- .../functional/consumersTests/fluentdTests.js | 7 +- .../googleCloudMonitoringTests.js | 4 +- test/functional/consumersTests/kafkaTests.js | 5 +- .../openTelemetryExporterTests.js | 5 +- test/functional/consumersTests/splunkTests.js | 19 +- test/functional/consumersTests/statsdTests.js | 17 +- test/functional/deployment/declaration.yml | 28 +- test/functional/dutTests.js | 45 +- test/functional/pullConsumerSystemTests.js | 2 +- .../pullConsumersTests/defaultTests.js | 4 +- .../pullConsumersTests/prometheusTests.js | 4 +- test/functional/shared/azureUtil.js | 1 - test/functional/shared/util.js | 15 +- test/unit/.mocha.opts | 1 - test/unit/configTests.js | 38 +- .../consumers/awsCloudWatchConsumerTests.js | 45 +- test/unit/consumers/awsS3ConsumerTests.js | 30 +- test/unit/consumers/awsUtilTests.js | 14 +- .../azureApplicationInsightsConsumerTests.js | 13 +- .../azureLogAnalyticsConsumerTests.js | 4 +- test/unit/consumers/azureUtilTests.js | 10 +- test/unit/consumers/data/awsUtilTestsData.js | 1 - .../azureLogAnalyticsConsumerTestsData.js | 7 +- .../openTelemetryExporterConsumerTestsData.js | 140 + .../consumers/data/statsdConsumerTestsData.js | 35 + test/unit/consumers/dataDogConsumerTests.js | 166 +- test/unit/consumers/gcpUtilTests.js | 27 +- .../unit/consumers/googleCloudLoggingTests.js | 22 +- .../googleCloudMonitoringConsumerTests.js | 22 +- test/unit/consumers/metricsUtilTests.js | 121 + .../consumers/openTelemetryExporterTests.js | 48 +- test/unit/consumers/splunkConsumerTests.js | 8 +- test/unit/consumers/statsdConsumerTests.js | 29 +- test/unit/consumersTests.js | 2 +- test/unit/customEndpointsTests.js | 2 +- .../configUtilTests/getComponentsTestsData.js | 8 +- .../hasEnabledComponentsTestsData.js | 8 +- ...malizeDeclarationIHealthPollerTestsData.js | 4 +- ...rmalizeDeclarationPullConsumerTestsData.js | 54 +- ...rmalizeDeclarationSystemPollerTestsData.js | 12 +- test/unit/data/customEndpointsTestsData.js | 1 - .../propertiesJsonTests/collectContext.js | 6 +- .../data/propertiesJsonTests/collectPools.js | 2 + .../propertiesJsonTests/collectSystemStats.js | 16 +- .../propertiesJsonTests/collectTmstats.js | 14 +- test/unit/declarationTests.js | 233 +- .../eventListener/baseDataReceiverTests.js | 6 +- test/unit/eventListener/dataPublisherTests.js | 2 +- test/unit/eventListener/eventListenerTests.js | 20 +- test/unit/eventListener/messageStreamTests.js | 16 +- .../eventListener/tcpUdpDataReceiverTests.js | 6 +- test/unit/iHealthTests.js | 44 +- test/unit/ihealthPollerTests.js | 46 +- test/unit/normalizeTests.js | 1 - test/unit/persistentStorageTests.js | 27 +- test/unit/propertiesJsonTests.js | 2 +- .../data/system_poller_datasets.json | 18 +- .../prometheusPullConsumerTests.js | 2 +- test/unit/pullConsumersTests.js | 10 +- .../requestHandlers/declareHandlerTests.js | 2 +- .../unit/requestHandlers/errorHandlerTests.js | 2 +- .../eventListenerHandlerTests.js | 4 +- .../ihealthPollerHandlerTests.js | 6 +- test/unit/requestHandlers/routerTests.js | 1 - .../systemPollerHandlerTests.js | 1 - test/unit/restWorkerTests.js | 6 +- test/unit/shared/assert.js | 8 +- test/unit/shared/dummies.js | 2 +- test/unit/shared/schemaValidation/index.js | 10 +- .../propertyTests/defaultValue.js | 2 +- .../schemaValidation/propertyTests/enum.js | 4 +- .../propertyTests/optionalProps.js | 8 +- .../propertyTests/required.js | 2 +- test/unit/shared/schemaValidation/utils.js | 1 - test/unit/shared/stubs.js | 8 +- test/unit/shared/util.js | 6 +- test/unit/systemPollerTests.js | 16 +- test/unit/systemStatsTests.js | 8 +- test/unit/teemReporterTests.js | 3 +- test/unit/utils/configTests.js | 331 +- test/unit/utils/deviceTests.js | 2 +- test/unit/utils/ihealthTests.js | 13 +- test/unit/utils/miscTests.js | 4 +- test/unit/utils/monitorTests.js | 1 - test/unit/utils/normalizeTests.js | 1 - test/unit/utils/promiseTests.js | 2 +- test/unit/utils/timersTests.js | 22 +- test/unit/utils/tracerTests.js | 22 +- test/winstonLogger.js | 7 +- versions.json | 2 +- 203 files changed, 8596 insertions(+), 1841 deletions(-) create mode 100644 examples/declarations/google_cloud_logging_sat.json create mode 100644 examples/declarations/google_cloud_monitoring_sat.json create mode 100644 src/schema/1.25.0/actions_schema.json create mode 100644 src/schema/1.25.0/base_schema.json create mode 100644 src/schema/1.25.0/consumer_schema.json create mode 100644 src/schema/1.25.0/controls_schema.json create mode 100644 src/schema/1.25.0/endpoints_schema.json create mode 100644 src/schema/1.25.0/ihealth_poller_schema.json create mode 100644 src/schema/1.25.0/listener_schema.json create mode 100644 src/schema/1.25.0/namespace_schema.json create mode 100644 src/schema/1.25.0/pull_consumer_schema.json create mode 100644 src/schema/1.25.0/shared_schema.json create mode 100644 src/schema/1.25.0/system_poller_schema.json create mode 100644 src/schema/1.25.0/system_schema.json create mode 100644 stryker.conf.js diff --git a/.gitignore b/.gitignore index 06c49046..86742633 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,8 @@ _build/ # coverage coverage/ .nyc_output/ +# mutation +reports/ # local test directory/files test/functional/logs/ test/functional/*_harness_file.json diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 70a1bb08..df73599d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -29,6 +29,7 @@ include: .install_unittest_packages_cmd: &install_unittest_packages_cmd - nodeFullVer=$(node --version) - echo "Node.js version - $nodeFullVer" +- echo "NPM origin version - $(npm --version)" - ver=$(echo $nodeFullVer | head -c 3) - if [[ "$ver" == "v4." ]]; then - npm install --global npm@5.10.0 @@ -45,6 +46,7 @@ include: - else - npm run install-test - fi +- echo "NPM updated version - $(npm --version)" .run_unittest_cmd: &run_unittest_cmd - npm run test-only @@ -149,7 +151,7 @@ include: lint: extends: - .test_job_definition - image: ${ARTIFACTORY_DOCKER_HUB}/node:8 + image: ${ARTIFACTORY_DOCKER_HUB}/node:12 stage: lint script: - *install_unittest_packages_cmd @@ -185,8 +187,22 @@ test_node12: - .run_unittest image: ${ARTIFACTORY_DOCKER_HUB}/node:12 +# Node 14 LTS (active, maintenance) +test_node14: + extends: + - .run_unittest + image: ${ARTIFACTORY_DOCKER_HUB}/node:14 + +# Node 16 LTS (active, maintenance) +test_node16: + extends: + - .run_unittest + image: ${ARTIFACTORY_DOCKER_HUB}/node:16 + # mostly for containers, unittests only (without coverage check) -test_node_latest: +# disabled for now, npm has one issue with DNS (IPv6), probably +# docker image issue. Node version 17.x, npm version 8.1.0 +.test_node_latest: extends: - .run_unittest image: ${ARTIFACTORY_DOCKER_HUB}/node:latest diff --git a/CHANGELOG.md b/CHANGELOG.md index ae8fe425..fe3dcbdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,19 @@ # Changelog Changes to this project are documented in this file. More detail and links can be found in the Telemetry Streaming [Document Revision History](https://clouddocs.f5.com/products/extensions/f5-telemetry-streaming/latest/revision-history.html). +## 1.25.0 +### Added +- AUTOTOOL-2798: [GitHub #154](https://github.com/F5Networks/f5-telemetry-streaming/issues/154): Allow keyless (service account based) authorization for GCP Metrics and Logging consumers. +- AUTOTOOL-2794: [GitHub #158](https://github.com/F5Networks/f5-telemetry-streaming/issues/158): Added "metricPrefix" property to DataDog consumer, which allows a custom prefix to be added to each DataDog metric +- AUTOTOOL-2815: Added the "convertBooleansToMetrics" property to the DataDog, Statsd and OpenTelemetry_Exporter consumers, which determines whether boolean values are converted to metrics +- AUTOTOOL-2842: [GitHub #173](https://github.com/F5Networks/f5-telemetry-streaming/issues/173): Added optional service endpoint URL property to AWS consumers. Used to overwrite the implicit endpoints based on the region. +- AUTOTOOL-2795: [GitHub #160](https://github.com/F5Networks/f5-telemetry-streaming/issues/160): Added "customTags" property to DataDog consumer, which allows specifying custom tags to attach to DataDog metrics and logs +- AUTOTOOL-2800: [GitHub #170](https://github.com/F5Networks/f5-telemetry-streaming/issues/170): Added "poolName" tag to pool member metrics. +### Fixed +### Changed +- AUTOTOOL-2769: Update npm packages (@grpc/grpc-js from v1.3.4 to v1.4.2, @opentelemetry/exporter-collector-proto from v0.24.0 to 0.25.0, @opentelemetry/sdk-metrics-base from 0.24.1-alpha.4 to 0.26.0, aws-sdk from v2.991.0 to v2.1018.0, eventemitter2 from v6.4.4 to v6.4.5) +### Removed + ## 1.24.0 ### Added - AUTOTOOL-2752: [GitHub #159](https://github.com/F5Networks/f5-telemetry-streaming/issues/159) and [GitHub #162](https://github.com/F5Networks/f5-telemetry-streaming/issues/162): Added "region" property to DataDog consumer diff --git a/SUPPORT.md b/SUPPORT.md index 8c4672ad..dd3e46fa 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -18,9 +18,9 @@ Currently supported versions: | Software Version | Release Type | First Customer Ship | End of Support | |------------------|---------------|---------------------|-----------------| | TS 1.20.1 | LTS | 30-Jun-2021 | 30-Jun-2022 | -| TS 1.22.0 | Feature | 09-Aug-2021 | 09-Nov-2021 | | TS 1.23.0 | Feature | 21-Sep-2021 | 21-Dec-2021 | | TS 1.24.0 | Feature | 02-Nov-2021 | 02-Feb-2022 | +| TS 1.25.0 | Feature | 14-Dec-2021 | 14-Mar-2022 | Versions no longer supported: @@ -47,5 +47,6 @@ Versions no longer supported: | TS 1.19.0 | Feature | 06-Apr-2021 | 06-Jul-2021 | | TS 1.20.0 | Feature | 18-May-2021 | 18-Aug-2021 | | TS 1.21.0 | Feature | 28-Jun-2021 | 28-Sep-2021 | +| TS 1.22.0 | Feature | 09-Aug-2021 | 09-Nov-2021 | See the [Release notes](https://github.com/F5Networks/f5-telemetry-streaming/releases) and [Telemetry Streaming documentation](https://clouddocs.f5.com/products/extensions/f5-telemetry-streaming/latest/revision-history.html) for new features and issues resolved for each release. diff --git a/contributing/README.md b/contributing/README.md index 0a5649bf..f9ac142f 100644 --- a/contributing/README.md +++ b/contributing/README.md @@ -108,7 +108,7 @@ How does the project handle a typical `POST` request? "trace": false, "format": "default" }, - "schemaVersion": "1.24.0" + "schemaVersion": "1.25.0" } } ``` diff --git a/contributing/process_release.md b/contributing/process_release.md index 42acc1ec..52a05faa 100644 --- a/contributing/process_release.md +++ b/contributing/process_release.md @@ -58,6 +58,8 @@ * 1.21.0 - 15.5 MB * 1.22.0 - 15.6 MB * 1.23.0 - 17.8 MB (NOTE: inclusion of OpenTelemetry libraries) + * 1.24.0 - 19.2 MB + * 1.25.0 - 17.7 MB * Install build to BIG-IP, navigate to folder `/var/config/rest/iapps/f5-telemetry/` and check following: * Run `du -sh` and check that folder's size (shouldn't be much greater than previous versions): * 1.4.0 - 65 MB @@ -80,6 +82,8 @@ * 1.21.0 - 111 MB * 1.22.0 - 112 MB * 1.23.0 - 132 MB (NOTE: inclusion of OpenTelemetry libraries) + * 1.24.0 - 134 MB + * 1.25.0 - 130 MB * Check `node_modules` folder - if you see `eslint`, `mocha` or something else from [package.json](package.json) `devDependencies` section - something wrong with build process. Probably some `npm` flags are work as not expected and it MUST BE FIXED before publishing. * Ensure that all tests (unit tests and functional tests passed) * Optional: Ensure that your local tags match remote. If not, remove all and re-fetch: diff --git a/docs/conf.py b/docs/conf.py index fc3daeae..ae3cfc1d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -79,7 +79,7 @@ # The short X.Y version. version = u'' # The full version, including alpha/beta/rc tags. -release = u'1.24.0' +release = u'1.25.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/output-example.rst b/docs/output-example.rst index 3a9ddfb4..cf15523a 100644 --- a/docs/output-example.rst +++ b/docs/output-example.rst @@ -21,6 +21,9 @@ Telemetry Streaming 1.23 introduced the following, which do not appear in earlie - Virtual server output: **isAvailable** and **isEnabled** (both boolean), which monitor virtual server availability, particularly for the Prometheus consumer. - System output: **throughtputPerformance**, which provides throughput performance information. +In Telemetry Streaming 1.24 and later, properties that require the bash endpoint are now skipped if bash is not available on the target BIG-IP: system.diskStorage, system.diskLatency, system.apmState, and all tmstats properties (used with “Splunk legacy” format). + + .. literalinclude:: ../examples/output/system_poller/output.json :language: json diff --git a/docs/revision-history.rst b/docs/revision-history.rst index 3c3dd6b1..aa3ea3ca 100644 --- a/docs/revision-history.rst +++ b/docs/revision-history.rst @@ -11,12 +11,16 @@ Document Revision History - Description - Date + * - 1.25.0 + - Updated the documentation for Telemetry Streaming v1.25.0. This release contains the following changes: |br| * Added support for IAM roles for Google Cloud Platform (see Google :ref:`Cloud Monitoring` and :ref:`Cloud Logging`) |br| * Added the **metricPrefix** and **customTags** properties to the DataDog consumer (see :ref:`DataDog`) |br| * Added the **convertBooleansToMetrics** property to the DataDog, StatsD, and OpenTelemetry consumers (see :ref:`DataDog`, :ref:`StatsD`, and :ref:`OpenTelemetry`) |br| * Added the **endpointUrl** property to AWS S3 and CloudWatch consumers (see :ref:`AWS S3` and :ref:`AWS CloudWatch`) |br| * Removed the experimental label from the autoTag property for StatsD (see :ref:`StatsD addTags`) |br| * Removed the experimental label from Splunk multi-metric format (see :ref:`Splunk multi-metric`) |br| |br| Issues Resolved: |br| * + - 12-14-21 + * - 1.24.0 - - Updated the documentation for Telemetry Streaming v1.24.0. This release contains the following changes: |br| * Added support for the **region** and **service** properties for the Experimental DataDog consumer (see :ref:`DataDog`) |br| * Added support for the **format** property for Azure Log Analytics (see :ref:`Azure Log Analytics`) |br| |br| Issues Resolved: |br| * + - Updated the documentation for Telemetry Streaming v1.24.0. This release contains the following changes: |br| * Added the **region** and **service** properties for the DataDog consumer (see :ref:`DataDog`) |br| * Removed the Experimental label from the DataDog consumer (see :ref:`DataDog`) |br| * Added support for the **format** property for Azure Log Analytics (see :ref:`Azure Log Analytics`) |br| * Added support for ElasticSearch 7 (see :ref:`ElasticSearch`) |br| |br| Issues Resolved: |br| * Fixed an issue where Telemetry Streaming would not collect System Poller data if **bash** was disabled on the BIG-IP device. Properties that require the bash endpoint are now skipped if bash is not available on the target BIG-IP (see :ref:`System information`). - 11-2-21 * - 1.23.0 - - Updated the documentation for Telemetry Streaming v1.23.0. This release contains the following changes: |br| * Added a new EXPERIMENTAL Push consumer for OpenTelemetry Exporter (see :ref:`OpenTelemetry Exporter`) |br| * Added **isAvailable** and **isEnabled** to virtual server output (see :ref:`System information output`), `GitHub #152 `_ |br| * Added **throughtputPerformance** to System output (see :ref:`System information output`), `GitHub #129 `_ |br| * Added the **compressionType** property to the experimental DataDog consumer (see :ref:`DataDog`), `GitHub #157 `_ |br| * Added functionality to handle responses from iControlREST that contain duplicate JSON keys |br| * Added support for TS Namespaces (was experimental), see :doc:`namespaces` |br| * Added support for specifying fallback hosts for Generic HTTP consumers (was experimental) :ref:`Fallback hosts` |br| |br| Issues Resolved: |br| * Fixed issue where Prometheus consumer did not return the correct Content-Type HTTP Header, `GitHub #148 `_ |br| * Fixed issue where asmState could report incorrect state value. asmState and lastAsmChange properties are now retrieved from iControlREST, `GitHub #151 `_ + - Updated the documentation for Telemetry Streaming v1.23.0. This release contains the following changes: |br| * Added a new EXPERIMENTAL Push consumer for OpenTelemetry Exporter (see :ref:`OpenTelemetry Exporter`) |br| * Added **isAvailable** and **isEnabled** to virtual server output (see :ref:`System information output`), `GitHub #152 `_ |br| * Added **throughputPerformance** to System output (see :ref:`System information output`), `GitHub #129 `_ |br| * Added the **compressionType** property to the experimental DataDog consumer (see :ref:`DataDog`), `GitHub #157 `_ |br| * Added functionality to handle responses from iControlREST that contain duplicate JSON keys |br| * Added support for TS Namespaces (was experimental), see :doc:`namespaces` |br| * Added support for specifying fallback hosts for Generic HTTP consumers (was experimental) :ref:`Fallback hosts` |br| |br| Issues Resolved: |br| * Fixed issue where Prometheus consumer did not return the correct Content-Type HTTP Header, `GitHub #148 `_ |br| * Fixed issue where asmState could report incorrect state value. asmState and lastAsmChange properties are now retrieved from iControlREST, `GitHub #151 `_ - 9-21-21 * - 1.22.0 diff --git a/docs/setting-up-consumer.rst b/docs/setting-up-consumer.rst index 2e5cc17f..25730d7d 100644 --- a/docs/setting-up-consumer.rst +++ b/docs/setting-up-consumer.rst @@ -23,12 +23,27 @@ If you want to specify proxy settings for Splunk consumers in TS 1.17 and later, .. NOTE:: When using the :doc:`custom endpoints feature`, be sure to include **/mgmt/tm/sys/global-settings** in your endpoints for Telemetry Streaming to be able to find the hostname. -**NEW in TS 1.19** |br| -Be sure to see :ref:`Memory usage spikes` in the Troubleshooting section for information on the **compressionType** property introduced in TS 1.19. When set to **none**, this property stops TS from compressing data before sending it to Splunk, which can help reduce memory usage. +Additions to the Splunk consumer +```````````````````````````````` +The following items have been added to the Splunk consumer since it was introduced. -Example Declaration: +.. list-table:: + :widths: 25 25 200 + :header-rows: 1 + + * - TS Version + - Property + - Description + + * - 1.19 + - **compressionType** + - Sets the type of compression. Be sure to see :ref:`Memory usage spikes` in the Troubleshooting section for information on the **compressionType** property. When set to **none**, this property stops TS from compressing data before sending it to Splunk, which can help reduce memory usage. + - + +**IMPORTANT**: The following declaration includes the additional properties shown in the table. If you attempt to use this declaration on a previous version, it will fail. On previous versions, remove the highlighted line(s), and the comma from the previous line. -.. IMPORTANT:: This example has been updated with the **compressionType** property introduced in TS 1.19. If you are using a previous version of Telemetry Streaming, this declaration will fail. When using a previous version, remove the **compressionType** line and the preceding comma (highlighted in yellow). + +Example Declaration: .. literalinclude:: ../examples/declarations/splunk.json :language: json @@ -40,7 +55,7 @@ Example Declaration: Splunk Legacy format (Deprecated) ````````````````````````````````` -.. IMPORTANT:: The Splunk Legacy format has been deprecated as of Telemetry Streaming 1.17, and has entered maintenance mode. This means there will be no further TS development for the Splunk Legacy format. |br| We recommend using the :ref:`Splunk default format`, or :ref:`multi-metric` (currently experimental). +.. IMPORTANT:: The Splunk Legacy format has been deprecated as of Telemetry Streaming 1.17, and has entered maintenance mode. This means there will be no further TS development for the Splunk Legacy format. |br| We recommend using the :ref:`Splunk default format`, or :ref:`multi-metric`. The **format** property can be set to **legacy** for Splunk users who wish to convert the stats output similar to the |splunk app|. To see more information, see |Analytics|. To see more information about using the HEC, see |HEC|. See the following example. @@ -69,15 +84,15 @@ Splunk multi-metric format `````````````````````````` .. sidebar:: :fonticon:`fa fa-info-circle fa-lg` Version Notice: - Splunk multi-metric format is available in TS v1.17 and later + Splunk multi-metric format is available in TS v1.17 and later, and requires Splunk 8.0.0 or later. -.. WARNING:: Splunk multi-metric format is currently EXPERIMENTAL. It requires Splunk version 8.0.0 or later. +.. IMPORTANT:: Splunk multi-metric format requires Splunk version 8.0.0 or later. -Telemetry Streaming 1.17 introduces the ability to use Splunk multi-metric format (currently experimental) for Splunk 8.0.0 and later. Splunk multi-metric format allows each JSON object to contain measurements for multiple metrics, which generate multiple-measurement metric data points, taking up less space on disk and improving search performance. +Telemetry Streaming 1.17 introduced the ability to use Splunk multi-metric format (experimental in TS 1.17-1.24) for Splunk 8.0.0 and later. Splunk multi-metric format allows each JSON object to contain measurements for multiple metrics, which generate multiple-measurement metric data points, taking up less space on disk and improving search performance. See the |splunkmm| for more information. -.. IMPORTANT:: Only canonical (default) system poller output is supported. Custom endpoints are NOT supported with the multi-metric format. +.. WARNING:: Only canonical (default) system poller output is supported. Custom endpoints are NOT supported with the multi-metric format. To use this feature, the **format** of the Splunk Telemetry_Consumer must be set to **multiMetric** as shown in the example. @@ -100,32 +115,41 @@ Required Information: .. IMPORTANT:: The Azure Log Analytics Consumer only supports sending 500 items. Each configuration item (such as virtual server, pool, node) uses part of this limit. -Format property -``````````````` -Telemetry Streaming 1.24 adds the **format** property for Azure Log Analytics. This was added to reduce the number of columns in the output which prevents a potential Azure error stating ``Data of Type F5Telemetry was dropped because number of field is above the limit of 500``. +Additions to the Azure Log Analytics consumer +````````````````````````````````````````````` +The following items have been added to the Azure Log Analytics consumer since it was introduced. -The values for **format** are: +.. list-table:: + :widths: 25 25 200 + :header-rows: 1 -- **default** - This is the default value, and does not change the behavior from previous versions. In this mode, each unique item gets a set of columns. With some properties such as Client and Server SSL profiles, the number of columns exceeds the maximum allowed by Azure. |br| For example, with a CA bundle certificate, there may be fields for expirationDate, expirationString, issuer, name, and subject. TS creates a column named **ca-bundle_crt_expirationDate** and four additional columns for the other four properties. The **name** value is a prefix for every column. -- **propertyBased** - This value causes Telemetry Streaming to create fewer columns by using the property name for the column. In the example above, the column (property) name is just **expirationDate**, and all certificates use this column for the expiration dates. |br| Note this happens only if the property **name** exists, and it matches the declared object name at the top. Otherwise, the naming mode goes back to default. + * - TS Version + - Property + - Description + + * - 1.24 + - **format** + - This was added to reduce the number of columns in the output which prevents a potential Azure error stating ``Data of Type F5Telemetry was dropped because number of field is above the limit of 500``. The values for **format** are: -| + * - + - + - **default**: This is the default value, and does not change the behavior from previous versions. In this mode, each unique item gets a set of columns. With some properties such as Client and Server SSL profiles, the number of columns exceeds the maximum allowed by Azure. |br| For example, with a CA bundle certificate, there may be fields for expirationDate, expirationString, issuer, name, and subject. TS creates a column named **ca-bundle_crt_expirationDate** and four additional columns for the other four properties. The **name** value is a prefix for every column. -To see more information about sending data to Log Analytics, see |HTTP Data Collector API|. + * - + - + - **propertyBased** - This value causes Telemetry Streaming to create fewer columns by using the property name for the column. In the example above, the column (property) name is just **expirationDate**, and all certificates use this column for the expiration dates. |br| Note this happens only if the property **name** exists, and it matches the declared object name at the top. Otherwise, the naming mode goes back to default. + + * - 1.24 + - **region** + - The **region** property for Azure Log Analytics and Application Insights was added in part to support the Azure Government regions. |br| - This optional property is used to determine cloud type (public/commercial, govcloud) so that the correct API URLs can be used (example values: westeurope, japanwest, centralus, usgovvirginia, and so on). |br| - If you do not provide a region, Telemetry Streaming attempts to look it up from the instance metadata. |br| - If it is unable to extract metadata, TS defaults to public/commercial |br| - Check the |azregion| for product/region compatibility for Azure Government. |br| - See the Azure documentation for a valid list of regions (resource location), and :ref:`Region list` for example values from the Azure CLI. -Region property -``````````````` -The **region** property for Azure Log Analytics and Application Insights was added in part to support the Azure Government regions. -- This optional property is used to determine cloud type (public/commercial, govcloud) so that the correct API URLs can be used (example values: westeurope, japanwest, centralus, usgovvirginia, and so on). -- If you do not provide a region, Telemetry Streaming attempts to look it up from the instance metadata. -- If it is unable to extract metadata, TS defaults to public/commercial -- Check the |azregion| for product/region compatibility for Azure Government. -- See the Azure documentation for a valid list of regions (resource location), and :ref:`Region list` for example values from the Azure CLI. + +To see more information about sending data to Log Analytics, see |HTTP Data Collector API|. | -.. NOTE:: The following example has been updated with the **useManagedIdentity**, **region**, and **format** properties. You must be using a TS version that supports these properties (TS 1.24 for **format**) |br| See :ref:`Using Managed Identities` following the example for information about using Azure Managed Identities and Telemetry Streaming. +.. IMPORTANT:: The following example has been updated with the **useManagedIdentity**, **region**, and **format** properties. You must be using a TS version that supports these properties (TS 1.24 for **format**) |br| See :ref:`Using Managed Identities` following the example for information about using Azure Managed Identities and Telemetry Streaming. Example Declaration: @@ -188,21 +212,31 @@ To see more information about Azure Application Insights, see |appinsight|. .. _region: -Region property -``````````````` -Telemetry Streaming v1.11 adds the **region** property for Azure Log Analytics and Application Insights. This is in part to support the Azure Government regions. +Additions to the Application Insights consumer +`````````````````````````````````````````````` +The following items have been added to the Azure Application Insights consumer since it was introduced. -- This optional property is used to determine cloud type (public/commercial, govcloud) so that the correct API URLs can be used (example values: westeurope, japanwest, centralus, usgovvirginia, and so on). -- If you do not provide a region, Telemetry Streaming attempts to look it up from the instance metadata. -- If it is unable to extract metadata, TS defaults to public/commercial -- Check the |azregion| for product/region compatibility for Azure Government. -- See the Azure documentation for a valid list of regions (resource location), and :ref:`Region list` for example values from the Azure CLI. +.. list-table:: + :widths: 25 25 200 + :header-rows: 1 + + * - TS Version + - Property + - Description + + * - 1.24 + - **region** + - The **region** property for Azure Log Analytics and Application Insights was added in part to support the Azure Government regions. |br| - This optional property is used to determine cloud type (public/commercial, govcloud) so that the correct API URLs can be used (example values: westeurope, japanwest, centralus, usgovvirginia, and so on). |br| - If you do not provide a region, Telemetry Streaming attempts to look it up from the instance metadata. |br| - If it is unable to extract metadata, TS defaults to public/commercial |br| - Check the |azregion| for product/region compatibility for Azure Government. |br| - See the Azure documentation for a valid list of regions (resource location), and :ref:`Region list` for example values from the Azure CLI. + +| +**IMPORTANT**: The following declaration includes the additional properties shown in the table. If you attempt to use this declaration on a previous version, it will fail. On previous versions, remove the highlighted line(s), and the comma from the previous line. Example Declaration: .. literalinclude:: ../examples/declarations/azure_application_insights.json :language: json + :emphasize-lines: 16 | @@ -232,12 +266,33 @@ AWS CloudWatch -------------- |aws_img| -AWS CloudWatch has two consumers: CloudWatch Logs, and :ref:`CloudWatch Metrics` (new in TS 1.14). If you do not use the new **dataType** property, the system defaults to CloudWatch Logs. +AWS CloudWatch has two consumers: CloudWatch Logs, and :ref:`CloudWatch Metrics`. If you do not use the new **dataType** property, the system defaults to CloudWatch Logs. .. IMPORTANT:: In TS 1.9.0 and later, the **username** and **passphrase** for CloudWatch are optional. This is because a user can send data from a BIG-IP that has an appropriate IAM role in AWS to AWS CloudWatch without a username and passphrase. In TS 1.18 and later, the root certificates for AWS services are now embedded within Telemetry Streaming and are the only root certificates used in requests made to AWS services per AWS's move to its own Certificate Authority, noted in https://aws.amazon.com/blogs/security/how-to-prepare-for-aws-move-to-its-own-certificate-authority/. +Additions to the AWS CloudWatch consumer +```````````````````````````````````````` +The following items have been added to the CloudWatch consumer since it was introduced. + + +.. list-table:: + :widths: 25 25 200 + :header-rows: 1 + + * - TS Version + - Property + - Description + + * - 1.25 + - **endpointUrl** + - This optional property for AWS CloudWatch (Logs and Metrics) allows you to specify the full AWS endpoint URL for service requests. In particular, it can be an interface VPC endpoint for AWS Direct Connect. See the following CloudWatch Logs and Metrics examples for usage. + + +| + + AWS CloudWatch Logs (default) ````````````````````````````` @@ -250,10 +305,13 @@ Required information: To see more information about creating and using IAM roles, see the |IAM roles|. +**IMPORTANT**: The following declaration includes the additional properties shown in the Additions to CloudWatch consumer table. If you attempt to use this declaration on a previous version, it will fail. On previous versions, remove the highlighted line(s), and the comma from the previous line. + Example Declaration: .. literalinclude:: ../examples/declarations/aws_cloudwatch_logs.json :language: json + :emphasize-lines: 13 | @@ -277,10 +335,13 @@ Required Information: - Passphrase: Navigate to :guilabel:`IAM > Users` +**IMPORTANT**: The following declaration includes the additional properties shown in the Additions to CloudWatch consumer table. If you attempt to use this declaration on a previous version, it will fail. On previous versions, remove the highlighted line(s), and the comma from the previous line. + Example Declaration: .. literalinclude:: ../examples/declarations/aws_cloudwatch_metrics.json :language: json + :emphasize-lines: 13 | @@ -299,14 +360,35 @@ Required Information: To see more information about creating and using IAM roles, see the |IAM roles|. -.. IMPORTANT:: In TS 1.12.0 and later, the **username** and **passphrase** for S3 are optional. This is because a user can send data from a BIG-IP that has an appropriate IAM role in AWS to AWS S3 without a username and passphrase. +.. IMPORTANT:: Rhe **username** and **passphrase** for S3 are optional. This is because a user can send data from a BIG-IP that has an appropriate IAM role in AWS to AWS S3 without a username and passphrase. -In TS 1.18 and later, the root certificates for AWS services are now embedded within Telemetry Streaming and are the only root certificates used in requests made to AWS services per AWS's move to its own Certificate Authority, noted in https://aws.amazon.com/blogs/security/how-to-prepare-for-aws-move-to-its-own-certificate-authority/. +The root certificates for AWS services are embedded within Telemetry Streaming and are the only root certificates used in requests made to AWS services per AWS's move to its own Certificate Authority, noted in https://aws.amazon.com/blogs/security/how-to-prepare-for-aws-move-to-its-own-certificate-authority/. + +Additions to the AWS S3 consumer +```````````````````````````````` +The following items have been added to the S3 consumer since it was introduced. + + +.. list-table:: + :widths: 25 25 200 + :header-rows: 1 + + * - TS Version + - Property + - Description + + * - 1.25 + - **endpointUrl** + - This optional property for AWS S3 allows you to specify the full AWS endpoint URL for service requests. In particular, it can be an interface VPC endpoint for AWS Direct Connect. + + +**IMPORTANT**: The following declaration includes the additional properties shown in the table. If you attempt to use this declaration on a previous version, it will fail. On previous versions, remove the highlighted line(s), and the comma from the previous line. Example Declaration: .. literalinclude:: ../examples/declarations/aws_s3.json :language: json + :emphasize-lines: 12 | @@ -347,26 +429,34 @@ Required Information: .. NOTE:: To see more information about installing Kafka, see |Installing Kafka|. -New in TS 1.17 -`````````````` -Telemetry Streaming 1.17 and later adds the ability to add TLS client authentication to the Kafka consumer using the **TLS** authentication protocol. This protocol configures Telemetry Streaming to provide the required private key and certificate(s) when the Kafka broker is configured to use SSL/TLS Client authentication. +Additions to the Kafka consumer +``````````````````````````````` +The following items have been added to the Kafka consumer since it was introduced. + + +.. list-table:: + :widths: 25 25 200 + :header-rows: 1 + + * - TS Version + - Property + - Description + + * - 1.17 + - **privateKey** + - This and the following properties provide the ability to add TLS client authentication to the Kafka consumer using the **TLS** authentication protocol. This protocol configures Telemetry Streaming to provide the required private key and certificate(s) when the Kafka broker is configured to use SSL/TLS Client authentication. You can find more information on Kafka's client authentication on the Confluent pages: https://docs.confluent.io/5.5.0/kafka/authentication_ssl.html. |br| |br| **privateKey** is the Private Key for the SSL certificate. Must be formatted as a 1-line string, with literal new line characters. -You can find more information on Kafka's client authentication on the Confluent pages: https://docs.confluent.io/5.5.0/kafka/authentication_ssl.html. + * - + - **clientCertificate** + - The client certificate chain. Must be formatted as a 1-line string, with literal new line characters. -There are 3 new properties on the Kafka consumer: + * - + - **rootCertificate** + - The Certificate Authority root certificate, used to validate the client certificate. Certificate verification can be disabled by setting allowSelfSignedCert=true. Must be formatted as a 1-line string, with literal new line characters. -+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| Property | Description | -+====================+============================================================================================================================================================================================================================================+ -| privateKey | The Private Key for the SSL certificate. Must be formatted as a 1-line string, with literal new line characters. | -+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| clientCertificate | The client certificate chain. Must be formatted as a 1-line string, with literal new line characters. | -+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| rootCertificate | The Certificate Authority root certificate, used to validate the client certificate. Certificate verification can be disabled by setting allowSelfSignedCert=true. Must be formatted as a 1-line string, with literal new line characters. | -+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -.. IMPORTANT:: The following declaration has been updated to include the new TLS authentication protocol introduced in TS 1.17. If you attempt to use this declaration on a previous version, it will fail. To use this declaration on previous versions, remove the highlighted lines (and the comma from line 23). +**IMPORTANT**: The following declaration includes the additional properties shown in the table. If you attempt to use this declaration on a previous version, it will fail. On previous versions, remove the highlighted line(s), and the comma from the previous line. Example Declaration: @@ -383,7 +473,7 @@ ElasticSearch ------------- |ElasticSearch| -.. IMPORTANT:: TS currently does not support sending data to ElasticSearch 7. +.. NOTE:: TS 1.24 adds support for sending data to ElasticSearch 7. Required Information: - Host: The address of the ElasticSearch system. @@ -394,12 +484,16 @@ Optional Parameters: - Protocol: The protocol of the ElasticSearch system. Options: http or https. Default is http. - Allow Self Signed Cert: allow TS to skip Cert validation. Options: true or false. Default is false. - Path: The path to use when sending data to the ElasticSearch system. - - Data Type: The type of data posted to the ElasticSearch system. Default is f5.telemetry - - API Version: The API version of the ElasticSearch system. + - Data Type: The type of data posted to the ElasticSearch system. + - API Version: The API version of the ElasticSearch system. Options: Any version string matching the ElasticSearch node(s) version. The default is 6.0. - Username: The username to use when sending data to the ElasticSearch system. - Passphrase: The secret/password to use when sending data to the ElasticSearch system. -.. NOTE:: To see more information about installing ElasticSearch, see |Installing ElasticSearch|. +.. IMPORTANT:: Telemetry Streaming 1.24 and later use the API Version value to determine the appropriate defaults to use for the Data Type parameter. |br| When the API Version is 6.X or earlier, **f5.telemetry** is used as the default Data Type. |br| When the API Version is 7.0 until the last 7.X version, **_doc** is used as the default Data Type. |br| In API Version 8.0 and later, the Data Type value is not supported, and will not be accepted in the Telemetry Streaming declaration. + +| + +To see more information about installing ElasticSearch, see |Installing ElasticSearch|. Example Declaration: @@ -445,27 +539,37 @@ Required Information: .. NOTE:: When using the :doc:`custom endpoints feature`, be sure to include **/mgmt/tm/sys/global-settings** in your endpoints for Telemetry Streaming to be able to find the hostname. -To see more information about installing StatsD, see |StatsDWiki|. +For more information about installing StatsD, see |StatsDWiki|. .. _addtags: -addTags (experimental) -`````````````````````` -.. sidebar:: :fonticon:`fa fa-info-circle fa-lg` Version Notice: +Additions to the StatsD consumer +```````````````````````````````` +The following items have been added to the StatsD consumer since it was introduced. - The EXPERIMENTAL feature addTags for StatsD is available in Telemetry Streaming 1.21 and later. +.. list-table:: + :widths: 25 25 200 + :header-rows: 1 -Telemetry Streaming 1.21 adds a new property for the StatsD consumer: **addTags**. This feature causes TS to parse the incoming payload for values to automatically add as tags. Currently only the **sibling** method is supported. + * - TS Version + - Property + - Description -To see an example and the output from **addTags**, see :ref:`addTags example`. + * - 1.21 + - **addTags** + - This feature (experimental in TS 1.21-1.24) causes Telemetry Streaming to parse the incoming payload for values to automatically add as tags. Currently only the **sibling** method is supported. To see an example and the output from **addTags**, see :ref:`addTags example`. -| + * - 1.25 + - **convertBooleansToMetrics** + - This property allows you to choose whether or not to convert boolean values to metrics (true becomes 1, false (default0) becomes 0). |br| By default, Telemetry Streaming uses Boolean values as tag values that are attached to individual metrics. If **convertBooleansToMetrics** is set to **true**, any Boolean values are instead converted to numeric values, which are then sent to the consumer(s) as a metric. |br| Note: Telemetry Streaming does not send a Boolean as both a tag and a metric; a Boolean value is sent to the consumer(s) as either a tag or as a metric. + - + +**IMPORTANT**: The following declaration includes the additional properties shown in the table. If you attempt to use this declaration on a previous version, it will fail. On previous versions, remove the highlighted line(s), and the comma from the previous line. -Example Declaration (updated with the **experimental** feature *addTags* introduced in TS 1.21. If using a prior version, **remove** the highlighted lines, and the comma in line 9): .. literalinclude:: ../examples/declarations/statsd.json :language: json - :emphasize-lines: 10-19 + :emphasize-lines: 9, 10-19 | @@ -490,23 +594,40 @@ Optional Properties: .. NOTE:: Since this consumer is designed to be generic and flexible, how authentication is performed is left up to the web service. To ensure the secrets are encrypted within Telemetry Streaming please note the use of JSON pointers. The secret to protect should be stored inside ``passphrase`` and referenced in the desired destination property, such as an API token in a header as shown in this example. -New in TS 1.18 -`````````````` -Telemetry Streaming 1.17 and later adds the ability to add TLS client authentication to the Generic HTTP consumer using the **TLS** authentication protocol. This protocol configures Telemetry Streaming to provide the required private key and certificate(s) when the specified HTTP endpoint is configured to use SSL/TLS Client authentication. +Additions to the Generic HTTP consumer +`````````````````````````````````````` +The following items have been added to the Generic HTTP consumer since it was introduced. -You can find more information on Kafka's client authentication on the Confluent pages: https://docs.confluent.io/5.5.0/kafka/authentication_ssl.html. -There are 3 new properties: +.. list-table:: + :widths: 25 25 200 + :header-rows: 1 + + * - TS Version + - Property + - Description + + * - 1.18 + - **privateKey** + - This and the following properties provide the ability to add TLS client authentication to the Generic HTTP consumer using the **TLS** authentication protocol. This protocol configures Telemetry Streaming to provide the required private key and certificate(s) when the Generic HTTP consumer is configured to use SSL/TLS Client authentication. |br| |br| **privateKey** is the Private Key for the SSL certificate. Must be formatted as a 1-line string, with literal new line characters. + + * - + - **clientCertificate** + - The client certificate chain. Must be formatted as a 1-line string, with literal new line characters. + + * - + - **rootCertificate** + - The Certificate Authority root certificate, used to validate the client certificate. Certificate verification can be disabled by setting allowSelfSignedCert=true. Must be formatted as a 1-line string, with literal new line characters. + +| + +**IMPORTANT**: The following declaration includes the additional properties shown in the table. If you attempt to use this declaration on a previous version, it will fail. On previous versions, remove the highlighted line(s), and the comma from the previous line. + +Example Declaration: + +.. literalinclude:: ../examples/declarations/generic_http.json + :language: json -+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| Property | Description | -+====================+============================================================================================================================================================================================================================================+ -| privateKey | The Private Key for the SSL certificate. Must be formatted as a 1-line string, with literal new line characters. | -+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| clientCertificate | The client certificate chain. Must be formatted as a 1-line string, with literal new line characters. | -+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| rootCertificate | The Certificate Authority root certificate, used to validate the client certificate. Certificate verification can be disabled by setting allowSelfSignedCert=true. Must be formatted as a 1-line string, with literal new line characters. | -+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | @@ -516,18 +637,13 @@ There are 3 new properties: - Generic HTTP with proxy settings in TS 1.17 and later, see :ref:`proxy`. - An EXPERIMENTAL feature where you can specify fallback IP address(es) for the Generic HTTP consumer, see :ref:`fallback`. - Generic HTTP with TLS authentication, see :ref:`httptls`. - + | -Example Declaration: - -.. literalinclude:: ../examples/declarations/generic_http.json - :language: json - .. _beacon-ref: F5 Beacon -````````` +--------- |beaconlogo| F5 Beacon, a SaaS offering, provides visibility and actionable insights into the health and performance of applications. @@ -584,24 +700,62 @@ Google Cloud Operations Suite's Cloud Monitoring Required Information: - projectId: The ID of the GCP project. - serviceEmail: The email for the Google Service Account. To check if you have an existing Service Account, from the left menu of GCP, select **IAM & admin**, and then click **Service Accounts**. If you do not have a Service Account, you must create one. - - privateKeyId: The ID of the private key that the user created for the Service Account (if you do not have a key, from the account page, click **Create Key** with a type of **JSON**. The Private key is in the file that was created when making the account). - - privateKey: The private key given to the user when a private key was added to the service account. + - privateKeyId: The ID of the private key that the user created for the Service Account (if you do not have a key, from the account page, click **Create Key** with a type of **JSON**. The Private key is in the file that was created when making the account). If you are using IAM roles, introduced in 1.25 do not use this property and see :ref:`gcmiam`. + - privateKey: The private key given to the user when a private key was added to the service account. If you are using IAM roles, introduced in 1.25 do not use this property and see :ref:`gcmiam`. For complete information on deploying Google Cloud Operations Suite, see |sddocs|. -**New in TS 1.22** |br| -Telemetry Streaming 1.22 adds a new property **reportInstanceMetadata** to this consumer. This allows you to enable or disable metadata reporting. The default is **false**. - **Finding the Data** |br| Once you have configured the Google Cloud Monitoring consumer and sent a Telemetry Streaming declaration, Telemetry Streaming creates custom MetricDescriptors to which it sends metrics. These metrics can be found under a path such as **custom/system/cpu**. To make it easier to find data that is relevant to a specific device, TS uses the **Generic Node** resource type, and assigns machine ID to the **node_id** label to identify which device the data is from. .. IMPORTANT:: There is a quota of 500 custom MetricDescriptors for Google Cloud Monitoring. Telemetry Streaming creates these MetricDescriptors, and if this quota is ever reached, you must delete some of these MetricDescriptors. -Example Declaration (updated to include **reportInstanceMetadata** in 1.22, remove this line in previous versions): +Additions to the Cloud Monitoring consumer +`````````````````````````````````````````` +The following items have been added to the Google Cloud Operations Suite's Cloud Monitoring consumer since it was introduced. + + +.. list-table:: + :widths: 25 25 200 + :header-rows: 1 + + * - TS Version + - Property + - Description + + * - 1.22 + - **reportInstanceMetadata** + - This property allows you to enable or disable metadata reporting. The default is **false**. + + +**IMPORTANT**: The following declaration includes the additional properties shown in the table. If you attempt to use this declaration on a previous version, it will fail. On previous versions, remove the highlighted line(s), and the comma from the previous line. + + +Example Declaration: .. literalinclude:: ../examples/declarations/google_cloud_monitoring.json :language: json +| + +.. _gcmiam: + +Using IAM roles for Google Cloud Monitoring +``````````````````````````````````````````` +.. sidebar:: :fonticon:`fa fa-info-circle fa-lg` Version Notice: + + Support for GCP IAM roles is available in TS v1.25 and later + +Telemetry Streaming 1.25 added support for using IAM roles for Google Cloud Monitoring. This means the Cloud Monitoring consumer can send data without specifying credentials if IAM roles are properly configured for the BIG-IP instance in GCP. + +IAM roles are enabled by using the new **useServiceAccountToken** property set to **true** (the default is **false**). When set to true, the **privateKey** and **privateKeyId** properties are not used. + +When using this feature, the authentication token is fetched from metadata at ``${METADATA_URL}/v1/instance/service-accounts/${serviceAccount.serviceEmail}/token``, meaning that **serviceEmail** in the declaration should match the service account email associated with the VM. + +Example Declaration: + +.. literalinclude:: ../examples/declarations/google_cloud_monitoring_sat.json + :language: json | @@ -617,8 +771,8 @@ Google Cloud Logging Required Information: - serviceEmail: The email for the Google Service Account. To check if you have an existing Service Account, from the GCP left menu, select **IAM & admin**, and then click **Service Accounts**. If you do not have a Service Account, you must create one. - - privateKeyId: The ID of the private key the user created for the Service Account (if you do not have a key, from the Account page, click **Create Key** with a type of **JSON**. The Private key is in the file that was created when making the account). - - privateKey: The private key given to the user when a private key was added to the service account. + - privateKeyId: The ID of the private key the user created for the Service Account (if you do not have a key, from the Account page, click **Create Key** with a type of **JSON**. The Private key is in the file that was created when making the account). If you are using IAM roles introduced in 1.25, do not use this property and see :ref:`gcmiam`. + - privateKey: The private key given to the user when a private key was added to the service account. If you are using IAM roles introduced in 1.25, do not use this property and see :ref:`gcliam`. - logScopeId: The ID of the scope specified in the **logScope** property. If using a logScope of **projects**, this is the ID for your project. - logId: The Google Cloud logging LOG_ID where log entries will be written. @@ -633,6 +787,26 @@ Example Declaration: .. literalinclude:: ../examples/declarations/google_cloud_logging.json :language: json +| + +.. _gcliam: + +Using IAM roles for Google Cloud Logging +```````````````````````````````````````` +.. sidebar:: :fonticon:`fa fa-info-circle fa-lg` Version Notice: + + Support for GCP IAM roles is available in TS v1.25 and later + +Telemetry Streaming 1.25 added support for using IAM roles for Google Cloud Logging. This means the Cloud Logging consumer can send data without specifying credentials if IAM roles are properly configured for the BIG-IP instance in GCP. + +IAM roles are enabled by using the new **useServiceAccountToken** property set to **true** (the default is **false**). When set to true, the **privateKey** and **privateKeyId** properties are not used. + +When using this feature, the authentication token is fetched from metadata at ``${METADATA_URL}/v1/instance/service-accounts/${serviceAccount.serviceEmail}/token``, meaning that **serviceEmail** in the declaration should match the service account email associated with the VM. + +Example Declaration: + +.. literalinclude:: ../examples/declarations/google_cloud_logging_sat.json + :language: json | @@ -644,12 +818,28 @@ The F5 Cloud Consumer is a part of F5's internal, digital experience operating s .. IMPORTANT:: This F5 Cloud consumer is for **F5 internal use only**, and its API is subject to change. We are including it on this page of Push consumers because you may see it in a Telemetry Streaming declaration. -**New in TS 1.22** |br| -Telemetry Streaming 1.22 adds the **eventSchemaVersion** property. This allows you to select the appropriate event schema instead of using a hard-coded value. +Additions to the F5 Cloud consumer +`````````````````````````````````` +The following items have been added to the F5 Cloud consumer since it was introduced. -| -Example Declaration (if using a version prior to 1.22, remove line 94 (highlighted in yellow): +.. list-table:: + :widths: 25 25 200 + :header-rows: 1 + + * - TS Version + - Property + - Description + + * - 1.22 + - **eventSchemaVersion** + - This allows you to select the appropriate event schema instead of using a hard-coded value. + + +**IMPORTANT**: The following declaration includes the additional properties shown in the table. If you attempt to use this declaration on a previous version, it will fail. On previous versions, remove the highlighted line(s), and the comma from the previous line. + + +Example Declaration: .. literalinclude:: ../examples/declarations/f5_cloud.json @@ -660,31 +850,62 @@ Example Declaration (if using a version prior to 1.22, remove line 94 (highlight .. _datadog: -DataDog (EXPERIMENTAL) ----------------------- +DataDog +------- |datadog| .. sidebar:: :fonticon:`fa fa-info-circle fa-lg` Version Notice: - The DataDog consumer was introduced as an EXPERIMENTAL consumer in TS 1.22. |br| The **compressionType** property was introduced in 1.23. |br| The **region** and **service** properties were introduced in TS 1.24 + The DataDog consumer was introduced as an experimental consumer in TS 1.22. |br| The **compressionType** property was added in 1.23. |br| The **region** and **service** properties were added in TS 1.24 |br| The **metricPrefix** property was added in 1.25. -Telemetry Streaming 1.23 added the **compressionType** property to the DataDog consumer. The acceptable values are **none** for no compression (the default), or **gzip**, where the payload will be compressed using gzip. +Required Information: + - apiKey: The DataDog API key required to submit metrics and events to DataDog -Telemetry Streaming 1.24 added the following properties to the DataDog consumer: +Additions to the DataDog consumer +````````````````````````````````` +The following items have been added to the DataDog consumer since it was introduced. + +.. list-table:: + :widths: 25 25 200 + :header-rows: 1 + + * - TS Version + - Property + - Description + + * - 1.23 + - **compressionType** + - Sets the type of compression. The acceptable values are **none** for no compression (the default), or **gzip**, where the payload will be compressed using gzip. + + * - 1.24 + - **region** + - Sets the region. The acceptable values are **US1** (the default), **US3**, **EU1**, and **US1-FED**. + + * - + - **service** + - The name of the service generating telemetry data (string). The default is **f5-telemetry**. This property exposes the **DATA_DOG_SERVICE_FIELD** value that is sent to the DataDog API. + + * - 1.25 + - **metricPrefix** + - This property allows you to provide a string value to be used as a metric prefix. For example, in the following declaration, the **metricPrefix** is set to **"f5", "bigip"**, which would add **f5.bigip** as a prefix (i.e. **system.cpu** would become **f5.bigip.system.cpu**). + + * - + - **convertBooleansToMetrics** + - This property allows you to choose whether or not to convert boolean values to metrics (true becomes 1, false (default0) becomes 0). |br| By default, Telemetry Streaming uses Boolean values as tag values that are attached to individual metrics. If **convertBooleansToMetrics** is set to **true**, any Boolean values are instead converted to numeric values, which are then sent to the consumer(s) as a metric. |br| Note: Telemetry Streaming does not send a Boolean as both a tag and a metric; a Boolean value is sent to the consumer(s) as either a tag or as a metric. -- **region**: The acceptable values are **US1** (the default), **US3**, **EU1**, and **US1-FED**. -- **service**: The name of the service generating telemetry data (string). The default is **f5-telemetry**. This property exposes the **DATA_DOG_SERVICE_FIELD** value that is sent to the DataDog API. + * - + - **customTags** + - This property allows you to add custom tags that are appended to the dynamically generated telemetry tags. You specify tags as an array of **name** and **value** pairs. You can set more than one tag in a declaration, but if you use this property, you must specify at least one custom tag. -.. IMPORTANT:: Be aware of the following before deploying this consumer: |br| * The DataDog consumer is an experimental feature and will change in future releases based on feedback. |br| * There is a possibility this consumer might crash Telemetry Streaming, and send incorrectly formatted data or incomplete metrics. |br| * Some metrics might lack tags or context (such as iRule events) that will be addressed in future updates. |br| * The DataDog consumer was not tested on configurations with thousands of objects, so it is unknown if the DataDog API will accept or reject such huge payloads. |br| * Telemetry Streaming expects the Data Dog service will be responsible for formatting the names and values of metrics and tags. -**NOTE**: The following declaration includes the **region** and **service** properties introduced in TS 1.24. If you attempt to use this declaration on a previous version, it will fail. On previous versions, remove the lines highlighted in yellow (and the comma from line 7). +**IMPORTANT**: The following declaration includes all of the additional properties shown in the table. If you attempt to use this declaration on a previous version, it will fail. On previous versions, remove the lines highlighted in yellow (and the comma from line 7). Example Declaration: .. literalinclude:: ../examples/declarations/data_dog.json :language: json - :emphasize-lines: 8-9 + :emphasize-lines: 8-17 | @@ -715,12 +936,32 @@ Note: As of Telemetry Streaming 1.23, this consumer: - Only exports OpenTelemetry metrics (logs and traces are not supported) - Exports telemetry data using protobufs over HTTP - Extracts metrics from Event Listener log messages. Any integer or float values in Event Listener log messages will be converted to an OpenTelemetry metric, and exported as a metric. + + +Additions to the OpenTelemetry Exporter consumer +```````````````````````````````````````````````` +The following items have been added to the OpenTelemetry consumer since it was introduced. + +.. list-table:: + :widths: 25 25 200 + :header-rows: 1 + + * - TS Version + - Property + - Description + * - 1.25 + - **convertBooleansToMetrics** + - This property allows you to choose whether or not to convert boolean values to metrics (true becomes 1, false (default0) becomes 0). |br| By default, Telemetry Streaming uses Boolean values as tag values that are attached to individual metrics. If **convertBooleansToMetrics** is set to **true**, any Boolean values are instead converted to numeric values, which are then sent to the consumer(s) as a metric. |br| Note: Telemetry Streaming does not send a Boolean as both a tag and a metric; a Boolean value is sent to the consumer(s) as either a tag or as a metric. + + +**IMPORTANT**: The following declaration includes all of the additional properties shown in the table. If you attempt to use this declaration on a previous version, it will fail. On previous versions, remove the highlighted line. Example Declaration: .. literalinclude:: ../examples/declarations/open_telemetry_exporter.json :language: json + :emphasize-lines: 15 | @@ -1014,6 +1255,4 @@ In the following table, we list the Azure Government regions. .. |gcldocs| raw:: html - Google Cloud Logging documentation - - \ No newline at end of file + Google Cloud Logging documentation \ No newline at end of file diff --git a/docs/telemetry-system.rst b/docs/telemetry-system.rst index 0e1e1052..1d6b5b78 100644 --- a/docs/telemetry-system.rst +++ b/docs/telemetry-system.rst @@ -7,6 +7,8 @@ The Telemetry System class sets up the system poller and optionally, the iHealth The minimal declaration defines ``My_System_Minimal`` targeted to ``localhost`` on port ``8100``, using the ``http`` protocol, and the user is ``admin``. +.. _syspoller: + System poller ````````````` The system poller collects and normalizes statistics from a system, such as BIG-IP, on a configurable interval for information such as device statistics, virtual server statistics, pool statistics, individual pool member statistics, and more. diff --git a/examples/declarations/aws_cloudwatch_logs.json b/examples/declarations/aws_cloudwatch_logs.json index 8400c619..8992f329 100644 --- a/examples/declarations/aws_cloudwatch_logs.json +++ b/examples/declarations/aws_cloudwatch_logs.json @@ -9,6 +9,7 @@ "username": "accesskey", "passphrase": { "cipherText": "secretkey" - } + }, + "endpointUrl": "https://your_VPC_endpoint_ID.logs.us-west-1.vpce.amazonaws.com" } -} \ No newline at end of file +} diff --git a/examples/declarations/aws_cloudwatch_metrics.json b/examples/declarations/aws_cloudwatch_metrics.json index 58a680cb..03e732b5 100644 --- a/examples/declarations/aws_cloudwatch_metrics.json +++ b/examples/declarations/aws_cloudwatch_metrics.json @@ -9,6 +9,7 @@ "username": "accesskey", "passphrase": { "cipherText": "secretkey" - } + }, + "endpointUrl": "https://your_VPC_endpoint_ID.monitoring.us-west-1.vpce.amazonaws.com" } -} \ No newline at end of file +} diff --git a/examples/declarations/aws_s3.json b/examples/declarations/aws_s3.json index 432c8002..1f9931b1 100644 --- a/examples/declarations/aws_s3.json +++ b/examples/declarations/aws_s3.json @@ -8,6 +8,7 @@ "username": "accesskey", "passphrase": { "cipherText": "secretkey" - } + }, + "endpointUrl": "your_VPC_endpoint_ID.s3.us-west-1.vpce.amazonaws.com" } -} \ No newline at end of file +} diff --git a/examples/declarations/data_dog.json b/examples/declarations/data_dog.json index 9a59d4d7..06b3f185 100644 --- a/examples/declarations/data_dog.json +++ b/examples/declarations/data_dog.json @@ -6,6 +6,14 @@ "apiKey": "api_key", "compressionType": "gzip", "region": "US1", - "service": "f5-telemetry" + "service": "f5-telemetry", + "metricPrefix": ["f5", "bigip"], + "convertBooleansToMetrics": true, + "customTags": [ + { + "name": "instanceId", + "value": "instance-1" + } + ] } } diff --git a/examples/declarations/google_cloud_logging_sat.json b/examples/declarations/google_cloud_logging_sat.json new file mode 100644 index 00000000..9eab8243 --- /dev/null +++ b/examples/declarations/google_cloud_logging_sat.json @@ -0,0 +1,12 @@ +{ + "class": "Telemetry", + "My_Consumer": { + "class": "Telemetry_Consumer", + "type": "Google_Cloud_Logging", + "logScope": "projects", + "logScopeId": "yourProjectId", + "logId": "yourLogId", + "serviceEmail": "yourServiceEmail", + "useServiceAccountToken": true + } +} diff --git a/examples/declarations/google_cloud_monitoring_sat.json b/examples/declarations/google_cloud_monitoring_sat.json new file mode 100644 index 00000000..f860c7fe --- /dev/null +++ b/examples/declarations/google_cloud_monitoring_sat.json @@ -0,0 +1,11 @@ +{ + "class": "Telemetry", + "My_Consumer": { + "class": "Telemetry_Consumer", + "type": "Google_Cloud_Monitoring", + "projectId": "yourGoogleCloudMonitoringProjectId", + "serviceEmail": "yourServiceEmail", + "reportInstanceMetadata": false, + "useServiceAccountToken": true + } +} diff --git a/examples/declarations/open_telemetry_exporter.json b/examples/declarations/open_telemetry_exporter.json index de2d9966..9b87f0ac 100644 --- a/examples/declarations/open_telemetry_exporter.json +++ b/examples/declarations/open_telemetry_exporter.json @@ -11,6 +11,7 @@ "name": "x-access-token", "value": "YOUR_TOKEN" } - ] + ], + "convertBooleansToMetrics": false } } \ No newline at end of file diff --git a/examples/declarations/statsd.json b/examples/declarations/statsd.json index 9197adf2..87b92a92 100644 --- a/examples/declarations/statsd.json +++ b/examples/declarations/statsd.json @@ -5,7 +5,8 @@ "type": "Statsd", "host": "192.0.2.1", "protocol": "udp", - "port": 8125 + "port": 8125, + "convertBooleansToMetrics": false }, "My_Consumer_with_AutoTagging": { "class": "Telemetry_Consumer", diff --git a/examples/output/system_poller/output.json b/examples/output/system_poller/output.json index 98b6292c..2da6b4d6 100644 --- a/examples/output/system_poller/output.json +++ b/examples/output/system_poller/output.json @@ -504,6 +504,7 @@ "/Common/10.0.3.5:80": { "addr": "10.0.3.5", "monitorStatus": "up", + "poolName": "/Common/app.app/app_pool", "port": 0, "serverside.bitsIn": 0, "serverside.bitsOut": 0, @@ -537,6 +538,7 @@ "/Common/10.0.1.100:6514": { "addr": "10.0.1.100", "monitorStatus": "down", + "poolName": "/Common/telemetry-local", "port": 0, "serverside.bitsIn": 0, "serverside.bitsOut": 0, @@ -570,6 +572,7 @@ "/Example_Tenant/192.168.120.6:514": { "addr": "192.168.120.6", "monitorStatus": "up", + "poolName": "/Example_Tenant/A1/hsl_pool", "port": 0, "serverside.bitsIn": 0, "serverside.bitsOut": 0, @@ -603,6 +606,7 @@ "/Example_Tenant/192.0.2.12:80": { "addr": "192.0.2.12", "monitorStatus": "up", + "poolName": "/Example_Tenant/A1/web_pool", "port": 0, "serverside.bitsIn": 0, "serverside.bitsOut": 0, @@ -618,6 +622,7 @@ "/Example_Tenant/192.0.2.13:80": { "addr": "192.0.2.13", "monitorStatus": "up", + "poolName": "/Example_Tenant/A1/web_pool", "port": 0, "serverside.bitsIn": 0, "serverside.bitsOut": 0, diff --git a/package-lock.json b/package-lock.json index 87955908..3c665459 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "f5-telemetry", - "version": "1.24.0-3", + "version": "1.25.0-2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -13,6 +13,162 @@ "@babel/highlight": "^7.12.13" } }, + "@babel/compat-data": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.15.0.tgz", + "integrity": "sha512-0NqAC1IJE0S0+lL1SWFMxMkz1pKCNCjI4tr2Zx4LJSXxCLAdr6KyArnY+sno5m3yH9g737ygOyPABDsnXkpxiA==", + "optional": true + }, + "@babel/core": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.8.tgz", + "integrity": "sha512-3UG9dsxvYBMYwRv+gS41WKHno4K60/9GPy1CJaH6xy3Elq8CTtvtjT5R5jmNhXfCYLX2mTw+7/aq5ak/gOE0og==", + "optional": true, + "requires": { + "@babel/code-frame": "^7.15.8", + "@babel/generator": "^7.15.8", + "@babel/helper-compilation-targets": "^7.15.4", + "@babel/helper-module-transforms": "^7.15.8", + "@babel/helpers": "^7.15.4", + "@babel/parser": "^7.15.8", + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.6", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", + "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", + "optional": true, + "requires": { + "@babel/highlight": "^7.14.5" + } + }, + "@babel/generator": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.8.tgz", + "integrity": "sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g==", + "optional": true, + "requires": { + "@babel/types": "^7.15.6", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-function-name": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", + "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", + "optional": true, + "requires": { + "@babel/helper-get-function-arity": "^7.15.4", + "@babel/template": "^7.15.4", + "@babel/types": "^7.15.4" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", + "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", + "optional": true, + "requires": { + "@babel/types": "^7.15.4" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz", + "integrity": "sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==", + "optional": true, + "requires": { + "@babel/types": "^7.15.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "optional": true + }, + "@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "optional": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", + "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", + "optional": true + }, + "@babel/template": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", + "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", + "optional": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4" + } + }, + "@babel/traverse": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz", + "integrity": "sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==", + "optional": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.15.4", + "@babel/helper-function-name": "^7.15.4", + "@babel/helper-hoist-variables": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", + "optional": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" + } + }, + "json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "optional": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "optional": true + } + } + }, "@babel/generator": { "version": "7.14.3", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.3.tgz", @@ -24,6 +180,151 @@ "source-map": "^0.5.0" } }, + "@babel/helper-annotate-as-pure": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.15.4.tgz", + "integrity": "sha512-QwrtdNvUNsPCj2lfNQacsGSQvGX8ee1ttrBrcozUP2Sv/jylewBP/8QFe6ZkBsC8T/GYWonNAWJV4aRR9AL2DA==", + "optional": true, + "requires": { + "@babel/types": "^7.15.4" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "optional": true + }, + "@babel/types": { + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", + "optional": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-compilation-targets": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz", + "integrity": "sha512-rMWPCirulnPSe4d+gwdWXLfAXTTBj8M3guAf5xFQJ0nvFY7tfNAFnWdqaHegHlgDZOCT4qvhF3BYlSJag8yhqQ==", + "optional": true, + "requires": { + "@babel/compat-data": "^7.15.0", + "@babel/helper-validator-option": "^7.14.5", + "browserslist": "^4.16.6", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "optional": true + } + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.15.4.tgz", + "integrity": "sha512-7ZmzFi+DwJx6A7mHRwbuucEYpyBwmh2Ca0RvI6z2+WLZYCqV0JOaLb+u0zbtmDicebgKBZgqbYfLaKNqSgv5Pw==", + "optional": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.15.4", + "@babel/helper-function-name": "^7.15.4", + "@babel/helper-member-expression-to-functions": "^7.15.4", + "@babel/helper-optimise-call-expression": "^7.15.4", + "@babel/helper-replace-supers": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", + "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", + "optional": true, + "requires": { + "@babel/highlight": "^7.14.5" + } + }, + "@babel/helper-function-name": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", + "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", + "optional": true, + "requires": { + "@babel/helper-get-function-arity": "^7.15.4", + "@babel/template": "^7.15.4", + "@babel/types": "^7.15.4" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", + "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", + "optional": true, + "requires": { + "@babel/types": "^7.15.4" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz", + "integrity": "sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==", + "optional": true, + "requires": { + "@babel/types": "^7.15.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "optional": true + }, + "@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "optional": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", + "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", + "optional": true + }, + "@babel/template": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", + "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", + "optional": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4" + } + }, + "@babel/types": { + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", + "optional": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" + } + } + } + }, "@babel/helper-function-name": { "version": "7.14.2", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.2.tgz", @@ -44,6 +345,399 @@ "@babel/types": "^7.12.13" } }, + "@babel/helper-hoist-variables": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.15.4.tgz", + "integrity": "sha512-VTy085egb3jUGVK9ycIxQiPbquesq0HUQ+tPO0uv5mPEBZipk+5FkRKiWq5apuyTE9FUrjENB0rCf8y+n+UuhA==", + "optional": true, + "requires": { + "@babel/types": "^7.15.4" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "optional": true + }, + "@babel/types": { + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", + "optional": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.4.tgz", + "integrity": "sha512-cokOMkxC/BTyNP1AlY25HuBWM32iCEsLPI4BHDpJCHHm1FU2E7dKWWIXJgQgSFiu4lp8q3bL1BIKwqkSUviqtA==", + "optional": true, + "requires": { + "@babel/types": "^7.15.4" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "optional": true + }, + "@babel/types": { + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", + "optional": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-module-imports": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz", + "integrity": "sha512-jeAHZbzUwdW/xHgHQ3QmWR4Jg6j15q4w/gCfwZvtqOxoo5DKtLHk8Bsf4c5RZRC7NmLEs+ohkdq8jFefuvIxAA==", + "optional": true, + "requires": { + "@babel/types": "^7.15.4" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "optional": true + }, + "@babel/types": { + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", + "optional": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-module-transforms": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.8.tgz", + "integrity": "sha512-DfAfA6PfpG8t4S6npwzLvTUpp0sS7JrcuaMiy1Y5645laRJIp/LiLGIBbQKaXSInK8tiGNI7FL7L8UvB8gdUZg==", + "optional": true, + "requires": { + "@babel/helper-module-imports": "^7.15.4", + "@babel/helper-replace-supers": "^7.15.4", + "@babel/helper-simple-access": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/helper-validator-identifier": "^7.15.7", + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.6" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", + "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", + "optional": true, + "requires": { + "@babel/highlight": "^7.14.5" + } + }, + "@babel/generator": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.8.tgz", + "integrity": "sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g==", + "optional": true, + "requires": { + "@babel/types": "^7.15.6", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-function-name": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", + "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", + "optional": true, + "requires": { + "@babel/helper-get-function-arity": "^7.15.4", + "@babel/template": "^7.15.4", + "@babel/types": "^7.15.4" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", + "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", + "optional": true, + "requires": { + "@babel/types": "^7.15.4" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz", + "integrity": "sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==", + "optional": true, + "requires": { + "@babel/types": "^7.15.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "optional": true + }, + "@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "optional": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", + "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", + "optional": true + }, + "@babel/template": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", + "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", + "optional": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4" + } + }, + "@babel/traverse": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz", + "integrity": "sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==", + "optional": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.15.4", + "@babel/helper-function-name": "^7.15.4", + "@babel/helper-hoist-variables": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", + "optional": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz", + "integrity": "sha512-E/z9rfbAOt1vDW1DR7k4SzhzotVV5+qMciWV6LaG1g4jeFrkDlJedjtV4h0i4Q/ITnUu+Pk08M7fczsB9GXBDw==", + "optional": true, + "requires": { + "@babel/types": "^7.15.4" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "optional": true + }, + "@babel/types": { + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", + "optional": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-plugin-utils": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz", + "integrity": "sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==", + "optional": true + }, + "@babel/helper-replace-supers": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.15.4.tgz", + "integrity": "sha512-/ztT6khaXF37MS47fufrKvIsiQkx1LBRvSJNzRqmbyeZnTwU9qBxXYLaaT/6KaxfKhjs2Wy8kG8ZdsFUuWBjzw==", + "optional": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.15.4", + "@babel/helper-optimise-call-expression": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.4" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", + "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", + "optional": true, + "requires": { + "@babel/highlight": "^7.14.5" + } + }, + "@babel/generator": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.8.tgz", + "integrity": "sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g==", + "optional": true, + "requires": { + "@babel/types": "^7.15.6", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-function-name": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", + "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", + "optional": true, + "requires": { + "@babel/helper-get-function-arity": "^7.15.4", + "@babel/template": "^7.15.4", + "@babel/types": "^7.15.4" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", + "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", + "optional": true, + "requires": { + "@babel/types": "^7.15.4" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz", + "integrity": "sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==", + "optional": true, + "requires": { + "@babel/types": "^7.15.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "optional": true + }, + "@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "optional": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", + "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", + "optional": true + }, + "@babel/template": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", + "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", + "optional": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4" + } + }, + "@babel/traverse": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz", + "integrity": "sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==", + "optional": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.15.4", + "@babel/helper-function-name": "^7.15.4", + "@babel/helper-hoist-variables": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", + "optional": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-simple-access": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.15.4.tgz", + "integrity": "sha512-UzazrDoIVOZZcTeHHEPYrr1MvTR/K+wgLg6MY6e1CJyaRhbibftF6fR2KU2sFRtI/nERUZR9fBd6aKgBlIBaPg==", + "optional": true, + "requires": { + "@babel/types": "^7.15.4" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "optional": true + }, + "@babel/types": { + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", + "optional": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" + } + } + } + }, "@babel/helper-split-export-declaration": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", @@ -59,6 +753,135 @@ "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==", "dev": true }, + "@babel/helper-validator-option": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", + "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", + "optional": true + }, + "@babel/helpers": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.15.4.tgz", + "integrity": "sha512-V45u6dqEJ3w2rlryYYXf6i9rQ5YMNu4FLS6ngs8ikblhu2VdR1AqAd6aJjBzmf2Qzh6KOLqKHxEN9+TFbAkAVQ==", + "optional": true, + "requires": { + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.4" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", + "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", + "optional": true, + "requires": { + "@babel/highlight": "^7.14.5" + } + }, + "@babel/generator": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.8.tgz", + "integrity": "sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g==", + "optional": true, + "requires": { + "@babel/types": "^7.15.6", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-function-name": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", + "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", + "optional": true, + "requires": { + "@babel/helper-get-function-arity": "^7.15.4", + "@babel/template": "^7.15.4", + "@babel/types": "^7.15.4" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", + "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", + "optional": true, + "requires": { + "@babel/types": "^7.15.4" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz", + "integrity": "sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==", + "optional": true, + "requires": { + "@babel/types": "^7.15.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "optional": true + }, + "@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "optional": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", + "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", + "optional": true + }, + "@babel/template": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", + "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", + "optional": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4" + } + }, + "@babel/traverse": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz", + "integrity": "sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==", + "optional": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.15.4", + "@babel/helper-function-name": "^7.15.4", + "@babel/helper-hoist-variables": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", + "optional": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" + } + } + } + }, "@babel/highlight": { "version": "7.14.0", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz", @@ -76,6 +899,77 @@ "integrity": "sha512-7MpZDIfI7sUC5zWo2+foJ50CSI5lcqDehZ0lVgIhSi4bFEk94fLAKlF3Q0nzSQQ+ca0lm+O6G9ztKVBeu8PMRQ==", "dev": true }, + "@babel/plugin-proposal-class-properties": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.14.5.tgz", + "integrity": "sha512-q/PLpv5Ko4dVc1LYMpCY7RVAAO4uk55qPwrIuJ5QJ8c6cVuAmhu7I/49JOppXL6gXf7ZHzpRVEUZdYoPLM04Gg==", + "optional": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-proposal-decorators": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.15.8.tgz", + "integrity": "sha512-5n8+xGK7YDrXF+WAORg3P7LlCCdiaAyKLZi22eP2BwTy4kJ0kFUMMDCj4nQ8YrKyNZgjhU/9eRVqONnjB3us8g==", + "optional": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.15.4", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-decorators": "^7.14.5" + } + }, + "@babel/plugin-proposal-private-methods": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.14.5.tgz", + "integrity": "sha512-838DkdUA1u+QTCplatfq4B7+1lnDa/+QMI89x5WZHBcnNv+47N8QEj2k9I2MUU9xIv8XJ4XvPCviM/Dj7Uwt9g==", + "optional": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-decorators": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.14.5.tgz", + "integrity": "sha512-c4sZMRWL4GSvP1EXy0woIP7m4jkVcEuG8R1TOZxPBPtp4FSM/kiPZub9UIs/Jrb5ZAOzvTUSGYrWsrSu1JvoPw==", + "optional": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.14.5.tgz", + "integrity": "sha512-u6OXzDaIXjEstBRRoBCQ/uKQKlbuaeE5in0RvWdA4pN6AhqxTIwUsnHPU1CFZA/amYObMsuWhYfRl3Ch90HD0Q==", + "optional": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-typescript": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.15.8.tgz", + "integrity": "sha512-ZXIkJpbaf6/EsmjeTbiJN/yMxWPFWvlr7sEG1P95Xb4S4IBcrf2n7s/fItIhsAmOf8oSh3VJPDppO6ExfAfKRQ==", + "optional": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.15.4", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-typescript": "^7.14.5" + } + }, + "@babel/preset-typescript": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.15.0.tgz", + "integrity": "sha512-lt0Y/8V3y06Wq/8H/u0WakrqciZ7Fz7mwPDHWUJAXlABL5hiUG42BNlRXiELNjeWjO5rWmnNKlx+yzJvxezHow==", + "optional": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-validator-option": "^7.14.5", + "@babel/plugin-transform-typescript": "^7.15.0" + } + }, "@babel/template": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", @@ -113,20 +1007,82 @@ "to-fast-properties": "^2.0.0" } }, + "@eslint/eslintrc": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.4.tgz", + "integrity": "sha512-h8Vx6MdxwWI2WM8/zREHMoqdgLNXEL4QX3MWSVMdyNJGvXVOs+6lp+m2hc3FnuMHDc4poxFNI20vCk0OmI4G0Q==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.0.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "globals": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", + "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "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 + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, "@f5devcentral/atg-storage": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@f5devcentral/atg-storage/-/atg-storage-1.1.0.tgz", "integrity": "sha512-fU2fyFfYMtENsmhuE12PgrX5uu9SnPoBl2CkVYOhIadvhIxb8FZr/ycgLa8XWQ+VfWsTK/UmW2uLTayqG+dSRA==" }, "@f5devcentral/eslint-config-f5-atg": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@f5devcentral/eslint-config-f5-atg/-/eslint-config-f5-atg-0.1.1.tgz", - "integrity": "sha512-oMv6vkBZHug9uo8lZacngPlY+ZdtxOAfkJg+RQVFTCpLpd7Zeg9If9SIlGgISLtZBz7rFViuud6VeoF6YyJuQQ==", + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@f5devcentral/eslint-config-f5-atg/-/eslint-config-f5-atg-0.1.5.tgz", + "integrity": "sha512-uyiCRwch69oZHiMLhh/D6Mn1fEI8/tl0OkO9Yd5L1CcvnNlRdOr3zzpFguzfrW6dvydHR1LLBlutgC88AjbE9g==", "dev": true, "requires": { - "eslint": "^5.16.0", - "eslint-config-airbnb-base": "^13.1.0", - "eslint-plugin-import": "^2.17.3" + "eslint-config-airbnb-base": "^14.2.1" } }, "@f5devcentral/f5-teem": { @@ -139,17 +1095,128 @@ } }, "@grpc/grpc-js": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.3.4.tgz", - "integrity": "sha512-AxtZcm0mArQhY9z8T3TynCYVEaSKxNCa9mVhVwBCUnsuUEe8Zn94bPYYKVQSLt+hJJ1y0ukr3mUvtWfcATL/IQ==", + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.4.4.tgz", + "integrity": "sha512-a6222b7Dl6fIlMgzVl7e+NiRoLiZFbpcwvBH2Oli56Bn7W4/3Ld+86hK4ffPn5rx2DlDidmIcvIJiOQXyhv9gA==", "requires": { + "@grpc/proto-loader": "^0.6.4", "@types/node": ">=12.12.47" }, "dependencies": { + "@grpc/proto-loader": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.6.tgz", + "integrity": "sha512-cdMaPZ8AiFz6ua6PUbP+LKbhwJbFXnrQ/mlnKGUyzDUZ3wp7vPLksnmLCBX6SHgSmjX7CbNVNLFYD5GmmjO4GQ==", + "requires": { + "@types/long": "^4.0.1", + "lodash.camelcase": "^4.3.0", + "long": "^4.0.0", + "protobufjs": "^6.10.0", + "yargs": "^16.1.1" + } + }, "@types/node": { - "version": "16.3.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.3.1.tgz", - "integrity": "sha512-N87VuQi7HEeRJkhzovao/JviiqKjDKMVKxKMfUvSKw+MbkbW8R0nA3fi/MQhhlxV2fQ+2ReM+/Nt4efdrJx3zA==" + "version": "16.11.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.6.tgz", + "integrity": "sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==" + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.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==", + "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==" + }, + "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==" + }, + "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==" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" } } }, @@ -158,29 +1225,46 @@ "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.3.0.tgz", "integrity": "sha512-9b8S/V+3W4Gv7G/JKSZ48zApgyYbfIR7mAC9XNnaSWme3zj57MIESu0ELzm9j5oxNIpFG8DgO00iJMIUZ5luqw==", "requires": { - "@types/lodash": "^4.14.104", - "@types/node": "^9.4.6", - "lodash": "^4.17.5", - "protobufjs": "^6.8.6" + "@types/lodash": "^4.14.104", + "@types/node": "^9.4.6", + "lodash": "^4.17.5", + "protobufjs": "^6.8.6" + } + }, + "@humanwhocodes/config-array": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.6.0.tgz", + "integrity": "sha512-JQlEKbcgEUjBFhLIF4iqM7u/9lwgHRBcpHrmUNCALK0Q3amXN6lxdoXLnF0sm11E9VqTmBALR87IlUg1bZ8A9A==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" } }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, "@opentelemetry/api": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.0.3.tgz", "integrity": "sha512-puWxACExDe9nxbBB3lOymQFrLYml2dVOrd7USiVRnSbgXE+KwBu+HxFvxrzfqsiSda9IWsXJG1ef7C1O2/GmKQ==" }, "@opentelemetry/api-metrics": { - "version": "0.24.1-alpha.31", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-metrics/-/api-metrics-0.24.1-alpha.31.tgz", - "integrity": "sha512-WYB42reUHLjQHRuTBjKhRkxcXG92zcp8cYRngF8VsCjjBJrO82Q2lewbfUms1A4RSvl2hs4ODH7+L5qICax75g==" + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-metrics/-/api-metrics-0.25.0.tgz", + "integrity": "sha512-9T0c9NQAEGRujUC7HzPa2/qZ5px/UvB2sfSU5CAKFRrAlDl2gn25B0oUbDqSRHW/IG1X2rnQ3z2bBQkJyJvE4g==" }, "@opentelemetry/core": { - "version": "0.24.1-alpha.31", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-0.24.1-alpha.31.tgz", - "integrity": "sha512-ssPWP6c+jXRc+zaf3W/7pumBKNP/11EU5rjFKpBE3/5ZKNolZwX6e+UhhXAX8FhdpuFEvtwumUmqryiTKuYK6A==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-0.25.0.tgz", + "integrity": "sha512-8OTWF4vfCENU112XB5ElLqf0eq/FhsY0SBvvY65vB3+fbZ2Oi+CPsRASrUZWGtC9MJ5rK2lBlY+/jI4a/NPPBg==", "requires": { - "@opentelemetry/semantic-conventions": "^0.24.1-alpha.31+fd2410cc", - "semver": "^7.1.3" + "@opentelemetry/semantic-conventions": "0.25.0", + "semver": "^7.3.5" }, "dependencies": { "semver": { @@ -194,73 +1278,48 @@ } }, "@opentelemetry/exporter-collector": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-collector/-/exporter-collector-0.24.0.tgz", - "integrity": "sha512-lI/oW7JkGisUofY4g+YmgGSlHuHgjteqRj539IIugmd3/RlY8j2mmAfRK4bwcODTBrxzA8X8Ty/Yh8Ljg18c8Q==", - "requires": { - "@opentelemetry/api-metrics": "0.24.0", - "@opentelemetry/core": "0.24.0", - "@opentelemetry/metrics": "0.24.0", - "@opentelemetry/resources": "0.24.0", - "@opentelemetry/tracing": "0.24.0" + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-collector/-/exporter-collector-0.25.0.tgz", + "integrity": "sha512-xZYstLt4hz1aTloJaepWdjMMf9305MqwqbUWjcU/X9pOxvgFWRlchO6x/HQTw7ow0i/S+ShzC+greKnb+1WvLA==", + "requires": { + "@opentelemetry/api-metrics": "0.25.0", + "@opentelemetry/core": "0.25.0", + "@opentelemetry/resources": "0.25.0", + "@opentelemetry/sdk-metrics-base": "0.25.0", + "@opentelemetry/sdk-trace-base": "0.25.0" }, "dependencies": { - "@opentelemetry/api-metrics": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-metrics/-/api-metrics-0.24.0.tgz", - "integrity": "sha512-hdpkMeVlRGTuMshD2ZFaDjA/U0cZTkxUkJFvS/4yOiWfw+kEASmGE+U0/i9lbdQKuCR7X1rXSjbcYumlHcMG+A==" - }, - "@opentelemetry/core": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-0.24.0.tgz", - "integrity": "sha512-KpsfxBbFTZT9zaB4Es/fFLbvSzVl9Io/8UUu/TYl4/HgqkmyVInNlWTgRiKyz9nsHzFpGP1kdZJj+YIut0IFsw==", - "requires": { - "@opentelemetry/semantic-conventions": "0.24.0", - "semver": "^7.1.3" - } - }, - "@opentelemetry/resources": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-0.24.0.tgz", - "integrity": "sha512-uEr2m13IRkjQAjX6fsYqJ21aONCspRvuQunaCl8LbH1NS1Gj82TuRUHF6TM82ulBPK8pU+nrrqXKuky2cMcIzw==", + "@opentelemetry/sdk-metrics-base": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics-base/-/sdk-metrics-base-0.25.0.tgz", + "integrity": "sha512-7fwPlAFB5Xw8mnVQfq0wqKNw3RXiAMad9T1bk5Sza9LK/L6hz8RTuHWCsFMsj+1OOSAaiPFuUMYrK1J75+2IAg==", "requires": { - "@opentelemetry/core": "0.24.0", - "@opentelemetry/semantic-conventions": "0.24.0" - } - }, - "@opentelemetry/semantic-conventions": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-0.24.0.tgz", - "integrity": "sha512-a/szuMQV0Quy0/M7kKdglcbRSoorleyyOwbTNNJ32O+RBN766wbQlMTvdimImTmwYWGr+NJOni1EcC242WlRcA==" - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "requires": { - "lru-cache": "^6.0.0" + "@opentelemetry/api-metrics": "0.25.0", + "@opentelemetry/core": "0.25.0", + "@opentelemetry/resources": "0.25.0", + "lodash.merge": "^4.6.2" } } } }, "@opentelemetry/exporter-collector-proto": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-collector-proto/-/exporter-collector-proto-0.24.0.tgz", - "integrity": "sha512-R50QDmz8Ufswj40qfSBDhXUToAdRfwIPMXCmw04pTkcIDv+9aBA6G5MemZgFddUaUyX8Bmf7FEvWqPqigEGrww==", - "requires": { - "@grpc/proto-loader": "^0.6.0", - "@opentelemetry/core": "0.24.0", - "@opentelemetry/exporter-collector": "0.24.0", - "@opentelemetry/metrics": "0.24.0", - "@opentelemetry/resources": "0.24.0", - "@opentelemetry/tracing": "0.24.0", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-collector-proto/-/exporter-collector-proto-0.25.0.tgz", + "integrity": "sha512-yNNXze8hYNdRVLBfhCmKhRY4siFLXpzUFbIQq6pjtXsLC5gCf/P/Ba0KUDfQIReqSEJkayi7WvTe74b3EeilTg==", + "requires": { + "@grpc/proto-loader": "^0.6.4", + "@opentelemetry/core": "0.25.0", + "@opentelemetry/exporter-collector": "0.25.0", + "@opentelemetry/resources": "0.25.0", + "@opentelemetry/sdk-metrics-base": "0.25.0", + "@opentelemetry/sdk-trace-base": "0.25.0", "protobufjs": "^6.9.0" }, "dependencies": { "@grpc/proto-loader": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.4.tgz", - "integrity": "sha512-7xvDvW/vJEcmLUltCUGOgWRPM8Oofv0eCFSVMuKqaqWJaXSzmB+m9hiyqe34QofAl4WAzIKUZZlinIF9FOHyTQ==", + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.6.tgz", + "integrity": "sha512-cdMaPZ8AiFz6ua6PUbP+LKbhwJbFXnrQ/mlnKGUyzDUZ3wp7vPLksnmLCBX6SHgSmjX7CbNVNLFYD5GmmjO4GQ==", "requires": { "@types/long": "^4.0.1", "lodash.camelcase": "^4.3.0", @@ -269,33 +1328,21 @@ "yargs": "^16.1.1" } }, - "@opentelemetry/core": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-0.24.0.tgz", - "integrity": "sha512-KpsfxBbFTZT9zaB4Es/fFLbvSzVl9Io/8UUu/TYl4/HgqkmyVInNlWTgRiKyz9nsHzFpGP1kdZJj+YIut0IFsw==", - "requires": { - "@opentelemetry/semantic-conventions": "0.24.0", - "semver": "^7.1.3" - } - }, - "@opentelemetry/resources": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-0.24.0.tgz", - "integrity": "sha512-uEr2m13IRkjQAjX6fsYqJ21aONCspRvuQunaCl8LbH1NS1Gj82TuRUHF6TM82ulBPK8pU+nrrqXKuky2cMcIzw==", + "@opentelemetry/sdk-metrics-base": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics-base/-/sdk-metrics-base-0.25.0.tgz", + "integrity": "sha512-7fwPlAFB5Xw8mnVQfq0wqKNw3RXiAMad9T1bk5Sza9LK/L6hz8RTuHWCsFMsj+1OOSAaiPFuUMYrK1J75+2IAg==", "requires": { - "@opentelemetry/core": "0.24.0", - "@opentelemetry/semantic-conventions": "0.24.0" + "@opentelemetry/api-metrics": "0.25.0", + "@opentelemetry/core": "0.25.0", + "@opentelemetry/resources": "0.25.0", + "lodash.merge": "^4.6.2" } }, - "@opentelemetry/semantic-conventions": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-0.24.0.tgz", - "integrity": "sha512-a/szuMQV0Quy0/M7kKdglcbRSoorleyyOwbTNNJ32O+RBN766wbQlMTvdimImTmwYWGr+NJOni1EcC242WlRcA==" - }, "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" }, "ansi-styles": { "version": "4.3.0", @@ -338,30 +1385,22 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "requires": { - "lru-cache": "^6.0.0" - } - }, "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "strip-ansi": "^6.0.1" } }, "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "requires": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" } }, "wrap-ansi": { @@ -400,44 +1439,53 @@ } } }, - "@opentelemetry/metrics": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/metrics/-/metrics-0.24.0.tgz", - "integrity": "sha512-QqmQCzrSuJE+sCOJ2xXNhctWPp/Am9ILs0Y01MDS08PRJoK20akKHM7eC4oU8ZdXphMg8rYgW2w7tY8rqvYnJg==", + "@opentelemetry/resources": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-0.25.0.tgz", + "integrity": "sha512-O46u53vDBlxCML8O9dIjsRcCC2VT5ri1upwhp02ITobgJ16aVD/iScCo1lPl/x2E7yq9uwzMINENiiYZRFb6XA==", + "requires": { + "@opentelemetry/core": "0.25.0", + "@opentelemetry/semantic-conventions": "0.25.0" + } + }, + "@opentelemetry/sdk-metrics-base": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics-base/-/sdk-metrics-base-0.26.0.tgz", + "integrity": "sha512-PbJsso7Vy/CLATAOyXbt/VP7ZQ2QYnvlq28lhOWaLPw8aqLogMBvidNGRrt7rF4/hfzLT6pMgpAAcit2C/nUMA==", "requires": { - "@opentelemetry/api-metrics": "0.24.0", - "@opentelemetry/core": "0.24.0", - "@opentelemetry/resources": "0.24.0", + "@opentelemetry/api-metrics": "0.26.0", + "@opentelemetry/core": "1.0.0", + "@opentelemetry/resources": "1.0.0", "lodash.merge": "^4.6.2" }, "dependencies": { "@opentelemetry/api-metrics": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-metrics/-/api-metrics-0.24.0.tgz", - "integrity": "sha512-hdpkMeVlRGTuMshD2ZFaDjA/U0cZTkxUkJFvS/4yOiWfw+kEASmGE+U0/i9lbdQKuCR7X1rXSjbcYumlHcMG+A==" + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-metrics/-/api-metrics-0.26.0.tgz", + "integrity": "sha512-idDSUTx+LRwJiHhVHhdh45SWow5u9lKNDROKu5AMzsIVPI29utH5FfT9vor8qMM6blxWWvlT22HUNdNMWqUQfQ==" }, "@opentelemetry/core": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-0.24.0.tgz", - "integrity": "sha512-KpsfxBbFTZT9zaB4Es/fFLbvSzVl9Io/8UUu/TYl4/HgqkmyVInNlWTgRiKyz9nsHzFpGP1kdZJj+YIut0IFsw==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.0.0.tgz", + "integrity": "sha512-1+qvKilADnSFW4PiXy+f7D22pvfGVxepZ69GcbF8cTcbQTUt7w63xEBWn5f5j92x9I3c0sqbW1RUx5/a4wgzxA==", "requires": { - "@opentelemetry/semantic-conventions": "0.24.0", - "semver": "^7.1.3" + "@opentelemetry/semantic-conventions": "1.0.0", + "semver": "^7.3.5" } }, "@opentelemetry/resources": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-0.24.0.tgz", - "integrity": "sha512-uEr2m13IRkjQAjX6fsYqJ21aONCspRvuQunaCl8LbH1NS1Gj82TuRUHF6TM82ulBPK8pU+nrrqXKuky2cMcIzw==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.0.0.tgz", + "integrity": "sha512-ORP8F2LLcJEm5M3H24RmdlMdiDc70ySPushpkrAW34KZGdZXwkrFoFXZhhs5MUxPT+fLrTuBafXxZVr8eHtFuQ==", "requires": { - "@opentelemetry/core": "0.24.0", - "@opentelemetry/semantic-conventions": "0.24.0" + "@opentelemetry/core": "1.0.0", + "@opentelemetry/semantic-conventions": "1.0.0" } }, "@opentelemetry/semantic-conventions": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-0.24.0.tgz", - "integrity": "sha512-a/szuMQV0Quy0/M7kKdglcbRSoorleyyOwbTNNJ32O+RBN766wbQlMTvdimImTmwYWGr+NJOni1EcC242WlRcA==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.0.0.tgz", + "integrity": "sha512-XCZ6ZSmc8FOspxKUU+Ow9UtJeSSRcS5rFBYGpjzix02U2v+X9ofjOjgNRnpvxlSvkccYIhdTuwcvNskmZ46SeA==" }, "semver": { "version": "7.3.5", @@ -449,74 +1497,21 @@ } } }, - "@opentelemetry/resources": { - "version": "0.24.1-alpha.31", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-0.24.1-alpha.31.tgz", - "integrity": "sha512-QtgoLy0xYXe/O9D8xcZajcrMI47L8tOC7+hbPAjtt7kSGYm0RJU+mupbLvOtxCDKDzsv+VcacxVg/bAbrOF2uA==", - "requires": { - "@opentelemetry/core": "^0.24.1-alpha.31+fd2410cc", - "@opentelemetry/semantic-conventions": "^0.24.1-alpha.31+fd2410cc" - } - }, - "@opentelemetry/sdk-metrics-base": { - "version": "0.24.1-alpha.4", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics-base/-/sdk-metrics-base-0.24.1-alpha.4.tgz", - "integrity": "sha512-X9JOlABaOS/gHqukIjPeupLpXxGcKRGZgN/U6W/8EAxhRzFlJBQEmRA4oph68I3jSGymPP4+KcBTAhGMUK6usw==", + "@opentelemetry/sdk-trace-base": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-0.25.0.tgz", + "integrity": "sha512-TInkLSF/ThM3GNVM+9tgnCVjyNLnRxvAkG585Fhu0HNwaEtCTUwI0r7AvMRIREOreeRWttBG6kvT0LOKdo8yjw==", "requires": { - "@opentelemetry/api-metrics": "^0.24.1-alpha.4+a8d39317", - "@opentelemetry/core": "^0.24.1-alpha.4+a8d39317", - "@opentelemetry/resources": "^0.24.1-alpha.4+a8d39317", + "@opentelemetry/core": "0.25.0", + "@opentelemetry/resources": "0.25.0", + "@opentelemetry/semantic-conventions": "0.25.0", "lodash.merge": "^4.6.2" } }, "@opentelemetry/semantic-conventions": { - "version": "0.24.1-alpha.31", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-0.24.1-alpha.31.tgz", - "integrity": "sha512-kVdxTt4M+Fe5MwZ3QjAFDoHpiJ0MDh0IbvxQkHODb9EUiZxYICgDXKVUM25z/DDctaiXzlfTtVYOLDpPHGStPA==" - }, - "@opentelemetry/tracing": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/tracing/-/tracing-0.24.0.tgz", - "integrity": "sha512-sTLEs1SIon3xV8vLe53PzfbU0FahoxL9NPY/CYvA1mwGbMu4zHkHAjqy1Tc8JmqRrfa+XrHkmzeSM4hrvloBaA==", - "requires": { - "@opentelemetry/core": "0.24.0", - "@opentelemetry/resources": "0.24.0", - "@opentelemetry/semantic-conventions": "0.24.0", - "lodash.merge": "^4.6.2" - }, - "dependencies": { - "@opentelemetry/core": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-0.24.0.tgz", - "integrity": "sha512-KpsfxBbFTZT9zaB4Es/fFLbvSzVl9Io/8UUu/TYl4/HgqkmyVInNlWTgRiKyz9nsHzFpGP1kdZJj+YIut0IFsw==", - "requires": { - "@opentelemetry/semantic-conventions": "0.24.0", - "semver": "^7.1.3" - } - }, - "@opentelemetry/resources": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-0.24.0.tgz", - "integrity": "sha512-uEr2m13IRkjQAjX6fsYqJ21aONCspRvuQunaCl8LbH1NS1Gj82TuRUHF6TM82ulBPK8pU+nrrqXKuky2cMcIzw==", - "requires": { - "@opentelemetry/core": "0.24.0", - "@opentelemetry/semantic-conventions": "0.24.0" - } - }, - "@opentelemetry/semantic-conventions": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-0.24.0.tgz", - "integrity": "sha512-a/szuMQV0Quy0/M7kKdglcbRSoorleyyOwbTNNJ32O+RBN766wbQlMTvdimImTmwYWGr+NJOni1EcC242WlRcA==" - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "requires": { - "lru-cache": "^6.0.0" - } - } - } + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-0.25.0.tgz", + "integrity": "sha512-V3N+MDBiv0TUlorbgiSqk6CvcP876CYUk/41Tg6s8OIyvniTwprE6vPvFQayuABiVkGlHOxv1Mlvp0w4qNdnVg==" }, "@protobufjs/aspromise": { "version": "1.1.2", @@ -581,33 +1576,451 @@ "type-detect": "4.0.8" } }, - "@sinonjs/formatio": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.2.tgz", - "integrity": "sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ==", - "dev": true, + "@sinonjs/formatio": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.2.tgz", + "integrity": "sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1", + "@sinonjs/samsam": "^3.1.0" + } + }, + "@sinonjs/samsam": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.3.tgz", + "integrity": "sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.3.0", + "array-from": "^2.1.1", + "lodash": "^4.17.15" + } + }, + "@sinonjs/text-encoding": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", + "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", + "dev": true + }, + "@stryker-mutator/api": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@stryker-mutator/api/-/api-5.4.1.tgz", + "integrity": "sha512-NWO2YvNGjXvZ6yvcpWCDCWRpFjFKUUInUNqnD1rtD4cOnqWX458ViHeHhNsEQ1b5c22zDw/MedAbUwkvudXiWg==", + "optional": true, + "requires": { + "mutation-testing-metrics": "1.7.5", + "mutation-testing-report-schema": "1.7.4", + "tslib": "~2.3.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "optional": true + } + } + }, + "@stryker-mutator/core": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@stryker-mutator/core/-/core-5.4.1.tgz", + "integrity": "sha512-q9Ss6ixgc/DcRNrel0Vuk/Is/sHZhyCxkIwlhUguLnOtJXlLbn89mURUqUAf9TGgEkZuzk580/nrkBGlUN8kRQ==", + "optional": true, + "requires": { + "@stryker-mutator/api": "5.4.1", + "@stryker-mutator/instrumenter": "5.4.1", + "@stryker-mutator/util": "5.4.1", + "ajv": "~8.6.0", + "chalk": "~4.1.0", + "commander": "~8.1.0", + "execa": "~5.1.1", + "file-url": "~3.0.0", + "get-port": "~5.1.1", + "glob": "~7.2.0", + "inquirer": "~8.1.0", + "lodash.flatmap": "~4.5.0", + "lodash.groupby": "~4.6.0", + "log4js": "~6.2.1", + "minimatch": "~3.0.4", + "mkdirp": "~1.0.3", + "mutation-testing-elements": "1.7.5", + "mutation-testing-metrics": "1.7.5", + "npm-run-path": "~4.0.1", + "progress": "~2.0.0", + "rimraf": "~3.0.0", + "rxjs": "~7.3.0", + "semver": "^7.3.5", + "source-map": "~0.7.3", + "tree-kill": "~1.2.2", + "tslib": "~2.3.0", + "typed-inject": "~3.0.0", + "typed-rest-client": "~1.8.0" + }, + "dependencies": { + "ajv": { + "version": "8.6.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.3.tgz", + "integrity": "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==", + "optional": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "optional": true, + "requires": { + "type-fest": "^0.21.3" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "optional": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "optional": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "optional": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "optional": 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==", + "optional": true + }, + "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==", + "optional": 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==", + "optional": true + }, + "commander": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.1.0.tgz", + "integrity": "sha512-mf45ldcuHSYShkplHHGKWb4TrmwQadxOn7v4WuhDJy0ZVoY5JFajaRDKD0PNe5qXzBX0rhovjTnP6Kz9LETcuA==", + "optional": 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==", + "optional": true + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "optional": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "optional": true + }, + "inquirer": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.1.5.tgz", + "integrity": "sha512-G6/9xUqmt/r+UvufSyrPpt84NYwhKZ9jLsgMbQzlx804XErNupor8WQdBnBRrXmBfTPpuwf1sV+ss2ovjgdXIg==", + "optional": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.2.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + } + }, + "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==", + "optional": true + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "optional": true + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "optional": true + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "optional": true + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "optional": true + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "optional": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "optional": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "rxjs": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.3.1.tgz", + "integrity": "sha512-vNenx7gqjPyeKpRnM6S5Ksm/oFTRijWWzYlRON04KaehZ3YjDwEmVjGUGo0TKWVjeNXOujVRlh0K1drUbcdPkw==", + "optional": true, + "requires": { + "tslib": "~2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "optional": true + } + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "optional": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "optional": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "optional": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "optional": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "optional": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "optional": true + } + } + }, + "@stryker-mutator/instrumenter": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@stryker-mutator/instrumenter/-/instrumenter-5.4.1.tgz", + "integrity": "sha512-G9fpBqSHt3Uoquz2ufgzFcqrWG0j2i0sb93yLZ+hLsLFXsN5T1BqtoYi94keBBmQ/D7EseoDWwz5t29CgC4flA==", + "optional": true, + "requires": { + "@babel/core": "~7.15.5", + "@babel/generator": "~7.15.0", + "@babel/parser": "~7.15.0", + "@babel/plugin-proposal-class-properties": "^7.12.1", + "@babel/plugin-proposal-decorators": "~7.15.4 ", + "@babel/plugin-proposal-private-methods": "^7.12.1", + "@babel/preset-typescript": "~7.15.0 ", + "@stryker-mutator/api": "5.4.1", + "@stryker-mutator/util": "5.4.1", + "angular-html-parser": "~1.8.0", + "weapon-regex": "~0.6.0" + }, + "dependencies": { + "@babel/generator": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.8.tgz", + "integrity": "sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g==", + "optional": true, + "requires": { + "@babel/types": "^7.15.6", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "optional": true + }, + "@babel/parser": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", + "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", + "optional": true + }, + "@babel/types": { + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", + "optional": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@stryker-mutator/mocha-runner": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@stryker-mutator/mocha-runner/-/mocha-runner-5.4.1.tgz", + "integrity": "sha512-Jlei+em1tYpPEFX9jBZx5ukbxxg3sWE/8INtYVqF+drVKhFPXRYJgAJaZ9+9oSZxoqNMrC8GtbK0s3P8oaamaQ==", + "optional": true, "requires": { - "@sinonjs/commons": "^1", - "@sinonjs/samsam": "^3.1.0" + "@stryker-mutator/api": "5.4.1", + "@stryker-mutator/util": "5.4.1", + "glob": "~7.2.0", + "tslib": "~2.3.0" + }, + "dependencies": { + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "optional": true + } } }, - "@sinonjs/samsam": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.3.tgz", - "integrity": "sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ==", - "dev": true, + "@stryker-mutator/util": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@stryker-mutator/util/-/util-5.4.1.tgz", + "integrity": "sha512-G0IaLUO15Rk7otvSz8/ayAuUW9AvGRxQZNZnNut44YKR0J1dk3rI1sFhQwaAh3gKFElm6FntToDoChI4eGZElg==", + "optional": true, "requires": { - "@sinonjs/commons": "^1.3.0", - "array-from": "^2.1.1", - "lodash": "^4.17.15" + "lodash.flatmap": "~4.5.0" } }, - "@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", - "dev": true - }, "@types/bytebuffer": { "version": "5.0.42", "resolved": "https://registry.npmjs.org/@types/bytebuffer/-/bytebuffer-5.0.42.tgz", @@ -654,9 +2067,9 @@ } }, "acorn": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", - "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", + "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==", "dev": true }, "acorn-jsx": { @@ -689,18 +2102,21 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, + "angular-html-parser": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/angular-html-parser/-/angular-html-parser-1.8.0.tgz", + "integrity": "sha512-n5ZowjJJs1OPG3DHDSyUXZvscQzy7uQG227ncL1NzbJEPzfb2XtBZ9qT0PW7cbD7MViho3ijawXoRLCM0ih1rw==", + "optional": true, + "requires": { + "tslib": "^1.9.3" + } + }, "ansi-colors": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", "dev": true }, - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", @@ -710,7 +2126,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -781,27 +2196,149 @@ "dev": true }, "array-includes": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", - "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", + "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", + "es-abstract": "^1.19.1", "get-intrinsic": "^1.1.1", - "is-string": "^1.0.5" + "is-string": "^1.0.7" + }, + "dependencies": { + "es-abstract": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "object-inspect": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "dev": true + } } }, "array.prototype.flat": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", - "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", + "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", "dev": true, "requires": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1" + "es-abstract": "^1.19.0" + }, + "dependencies": { + "es-abstract": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "object-inspect": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "dev": true + } } }, "arrify": { @@ -838,12 +2375,6 @@ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true }, - "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": "2.6.3", "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", @@ -875,9 +2406,9 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "aws-sdk": { - "version": "2.991.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.991.0.tgz", - "integrity": "sha512-TybluMJhRBZ0h5HGupHPTfamwtsJlW56HddJpMbsIjvmh4LGupajrkEwLQYW7osFXQ1S/xuE+0QIy6vWgOpT0g==", + "version": "2.1018.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1018.0.tgz", + "integrity": "sha512-XIZ7X8O//bkwuh7a7CkWt5+ldwFzP1bHpWCI33BdCaW7Q6WFKokvtS8CkHMxgsmqnaQ+YC0PpHzoTdAtXpqxQw==", "requires": { "buffer": "4.9.2", "events": "1.1.1", @@ -997,6 +2528,19 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "browserslist": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.4.tgz", + "integrity": "sha512-Zg7RpbZpIJRW3am9Lyckue7PLytvVxxhJj1CaJVlCWENsGEAOlnlt8X0ZxGRPp7Bt9o8tIRM5SEXy4BCPMJjLQ==", + "optional": true, + "requires": { + "caniuse-lite": "^1.0.30001265", + "electron-to-chromium": "^1.3.867", + "escalade": "^3.1.1", + "node-releases": "^2.0.0", + "picocolors": "^1.0.0" + } + }, "buffer": { "version": "4.9.2", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", @@ -1092,7 +2636,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, "requires": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" @@ -1110,6 +2653,12 @@ "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", "dev": true }, + "caniuse-lite": { + "version": "1.0.30001271", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001271.tgz", + "integrity": "sha512-BBruZFWmt3HFdVPS8kceTBIguKxu4f99n5JNp06OlPD/luoAMIaIK5ieV5YjnBLH3Nysai9sxj9rpJj4ZisXOA==", + "optional": true + }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -1150,7 +2699,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -1161,7 +2709,7 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true + "optional": true }, "check-error": { "version": "1.0.2", @@ -1190,20 +2738,11 @@ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "dev": true, - "requires": { - "restore-cursor": "^2.0.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 + "cli-spinners": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "optional": true }, "cliui": { "version": "3.2.0", @@ -1216,6 +2755,12 @@ "wrap-ansi": "^2.0.0" } }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "optional": true + }, "cls-hooked": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/cls-hooked/-/cls-hooked-4.2.2.tgz", @@ -1235,7 +2780,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "requires": { "color-name": "1.1.3" } @@ -1243,8 +2787,7 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "colors": { "version": "1.0.3", @@ -1306,7 +2849,6 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, "requires": { "safe-buffer": "~5.1.1" }, @@ -1314,8 +2856,7 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } }, @@ -1346,16 +2887,25 @@ } }, "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "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" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "dependencies": { + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } } }, "cycle": { @@ -1372,6 +2922,12 @@ "assert-plus": "^1.0.0" } }, + "date-format": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-3.0.0.tgz", + "integrity": "sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w==", + "optional": true + }, "debug": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", @@ -1443,6 +2999,15 @@ "strip-bom": "^3.0.0" } }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "optional": true, + "requires": { + "clone": "^1.0.2" + } + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -1512,6 +3077,12 @@ "safe-buffer": "^5.0.1" } }, + "electron-to-chromium": { + "version": "1.3.877", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.877.tgz", + "integrity": "sha512-fT5mW5Giw5iyVukeHb2XvB4joBKvzHtl8Vs3QzE7APATpFMt/T7RWyUcIKSZzYkKQgpMbu+vDBTCHfQZvh8klA==", + "optional": true + }, "emitter-listener": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz", @@ -1535,6 +3106,23 @@ "once": "^1.4.0" } }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + }, + "dependencies": { + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + } + } + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -1593,79 +3181,209 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "eslint": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", - "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.2.0.tgz", + "integrity": "sha512-erw7XmM+CLxTOickrimJ1SiF55jiNlVSp2qqm0NuBWPtHYQCegD5ZMaW0c3i5ytPqL+SSLaCxdvQXFPLJn+ABw==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "ajv": "^6.9.1", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^4.0.1", + "@eslint/eslintrc": "^1.0.4", + "@humanwhocodes/config-array": "^0.6.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", "doctrine": "^3.0.0", - "eslint-scope": "^4.0.3", - "eslint-utils": "^1.3.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^5.0.1", - "esquery": "^1.0.1", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^6.0.0", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.0.0", + "espree": "^9.0.0", + "esquery": "^1.4.0", "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.7.0", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", - "inquirer": "^6.2.2", - "js-yaml": "^3.13.0", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.11", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", + "optionator": "^0.9.1", "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^5.5.1", - "strip-ansi": "^4.0.0", - "strip-json-comments": "^2.0.1", - "table": "^5.2.3", - "text-table": "^0.2.0" + "regexpp": "^3.2.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" }, "dependencies": { "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, - "strip-ansi": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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 + }, + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + }, + "dependencies": { + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + } + } + }, + "globals": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", + "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "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 + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "has-flag": "^4.0.0" } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true } } }, "eslint-config-airbnb-base": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-13.2.0.tgz", - "integrity": "sha512-1mg/7eoB4AUeB0X1c/ho4vb2gYkNH8Trr/EgCT/aGmKhhG+F6vF5s8+iRBlWAzFIAphxIdp3YfEKgEl0f9Xg+w==", + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz", + "integrity": "sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA==", "dev": true, "requires": { - "confusing-browser-globals": "^1.0.5", - "object.assign": "^4.1.0", - "object.entries": "^1.1.0" + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.2" } }, "eslint-import-resolver-node": { @@ -1690,12 +3408,13 @@ } }, "eslint-module-utils": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.2.tgz", - "integrity": "sha512-QG8pcgThYOuqxupd06oYTZoNOGaUdTY1PqK+oS6ElF6vs4pBdk/aYxFVQQXzcrAqp9m7cl7lb2ubazX+g16k2Q==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.1.tgz", + "integrity": "sha512-fjoetBXQZq2tSTWZ9yWVl2KuFrTZZH3V+9iD1V1RfpDgxzJR+mPd/KZmMiA8gbPqdBzpNiEHOuT7IYEWxrH0zQ==", "dev": true, "requires": { "debug": "^3.2.7", + "find-up": "^2.1.0", "pkg-dir": "^2.0.0" }, "dependencies": { @@ -1711,24 +3430,22 @@ } }, "eslint-plugin-import": { - "version": "2.24.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.24.2.tgz", - "integrity": "sha512-hNVtyhiEtZmpsabL4neEj+6M5DCLgpYyG9nzJY8lZQeQXEn5UPW1DpUdsMHMXsq98dbNm7nt1w9ZMSVpfJdi8Q==", + "version": "2.25.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.3.tgz", + "integrity": "sha512-RzAVbby+72IB3iOEL8clzPLzL3wpDrlwjsTBAQXgyp5SeTqqY+0bFubwuo+y/HLhNZcXV4XqTBO4LGsfyHIDXg==", "dev": true, "requires": { - "array-includes": "^3.1.3", - "array.prototype.flat": "^1.2.4", + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", "debug": "^2.6.9", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.6.2", - "find-up": "^2.0.0", + "eslint-module-utils": "^2.7.1", "has": "^1.0.3", - "is-core-module": "^2.6.0", + "is-core-module": "^2.8.0", + "is-glob": "^4.0.3", "minimatch": "^3.0.4", - "object.values": "^1.1.4", - "pkg-up": "^2.0.0", - "read-pkg-up": "^3.0.0", + "object.values": "^1.1.5", "resolve": "^1.20.0", "tsconfig-paths": "^3.11.0" }, @@ -1752,14 +3469,23 @@ } }, "is-core-module": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", - "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", + "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", "dev": true, "requires": { "has": "^1.0.3" } }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -1769,39 +3495,47 @@ } }, "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-6.0.0.tgz", + "integrity": "sha512-uRDL9MWmQCkaFus8RF5K9/L/2fn+80yoW3jkD53l4shjCh26fCtvJGasxjUqP5OT87SYTxCVA3BwTUzuELx9kA==", "dev": true, "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" } }, "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==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "dev": true, "requires": { - "eslint-visitor-keys": "^1.1.0" + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + } } }, "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==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", + "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", "dev": true }, "espree": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", - "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.0.0.tgz", + "integrity": "sha512-r5EQJcYZ2oaGbeR0jR0fFVijGOcwai07/690YRXLINuhmVeRY4UKSAsQPe/0BNuDgwP7Ophoc1PRsr2E3tkbdQ==", "dev": true, "requires": { - "acorn": "^6.0.7", - "acorn-jsx": "^5.0.0", - "eslint-visitor-keys": "^1.0.0" + "acorn": "^8.5.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.0.0" } }, "esprima": { @@ -1817,14 +3551,6 @@ "dev": true, "requires": { "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } } }, "esrecurse": { @@ -1834,20 +3560,12 @@ "dev": true, "requires": { "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } } }, "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, "esutils": { @@ -1862,15 +3580,90 @@ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" }, "eventemitter2": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.4.tgz", - "integrity": "sha512-HLU3NDY6wARrLCEwyGKRBvuWYyvW6mHYv72SJJAH3iJN3a6eVUvkjFkcxah1bcTgGVBBrFdIopBJPhCQFMLyXw==" + "version": "6.4.5", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.5.tgz", + "integrity": "sha512-bXE7Dyc1i6oQElDG0jMRZJrRAn9QR2xyyFGmBdZleNmyQX0FqGYmhZIrIrpPfm/w//LTo4tVQGOGQcGCb5q9uw==" }, "events": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "optional": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "optional": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "optional": true + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "optional": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "optional": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "optional": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "optional": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "optional": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, "expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", @@ -1886,7 +3679,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, + "optional": true, "requires": { "chardet": "^0.7.0", "iconv-lite": "^0.4.24", @@ -1925,22 +3718,13 @@ "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz", "integrity": "sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig==" }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "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==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "requires": { - "flat-cache": "^2.0.1" + "flat-cache": "^3.0.4" } }, "file-uri-to-path": { @@ -1949,6 +3733,12 @@ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", "optional": true }, + "file-url": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/file-url/-/file-url-3.0.0.tgz", + "integrity": "sha512-g872QGsHexznxkIAdK8UiZRe7SkE6kvylShU4Nsj8NvfvZag7S0QuQ4IgvPDkk75HxgjIVDwycFTDAgIiO4nDA==", + "optional": true + }, "fill-keys": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/fill-keys/-/fill-keys-1.0.2.tgz", @@ -2052,21 +3842,37 @@ } }, "flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", "dev": true, "requires": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "dependencies": { + "flatted": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", + "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "flatted": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", - "dev": true + "optional": true }, "foreground-child": { "version": "1.5.6", @@ -2127,6 +3933,17 @@ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "optional": true }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "optional": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, "fs-minipass": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", @@ -2139,8 +3956,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { "version": "2.1.3", @@ -2152,8 +3968,7 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "functional-red-black-tree": { "version": "1.0.1", @@ -2197,6 +4012,12 @@ "json-bigint": "^1.0.0" } }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "optional": true + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -2212,13 +4033,34 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1" } }, + "get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "optional": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "optional": true + }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -2259,8 +4101,7 @@ "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" }, "google-auth-library": { "version": "6.1.6", @@ -2289,8 +4130,7 @@ "graceful-fs": { "version": "4.2.6", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", - "dev": true + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" }, "growl": { "version": "1.10.5", @@ -2372,7 +4212,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -2386,14 +4225,21 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "has-symbols": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } }, "has-unicode": { "version": "2.0.1", @@ -2454,18 +4300,23 @@ "debug": "4" } }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "optional": true + }, "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" } }, "icrdk": { - "version": "git://github.com/f5devcentral/f5-icontrollx-dev-kit.git#b439e63cc8936059f93392e88c3a653a4f823cb3", - "from": "git://github.com/f5devcentral/f5-icontrollx-dev-kit.git#master", + "version": "git+https://github.com/f5devcentral/f5-icontrollx-dev-kit.git#b439e63cc8936059f93392e88c3a653a4f823cb3", + "from": "git+https://github.com/f5devcentral/f5-icontrollx-dev-kit.git#master", "dev": true }, "ieee754": { @@ -2508,7 +4359,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -2524,77 +4374,15 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, - "inquirer": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", - "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", - "dev": true, - "requires": { - "ansi-escapes": "^3.2.0", - "chalk": "^2.4.2", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.3", - "figures": "^2.0.0", - "lodash": "^4.17.12", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^6.4.0", - "string-width": "^2.1.0", - "strip-ansi": "^5.1.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "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": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "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" - }, - "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 - } - } - } + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" } }, "invert-kv": { @@ -2692,6 +4480,12 @@ "is-extglob": "^2.1.1" } }, + "is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "optional": true + }, "is-negative-zero": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", @@ -2726,6 +4520,12 @@ "has-symbols": "^1.0.2" } }, + "is-shared-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "dev": true + }, "is-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", @@ -2751,6 +4551,21 @@ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "optional": true + }, + "is-weakref": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz", + "integrity": "sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0" + } + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -2759,8 +4574,7 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "isstream": { "version": "0.1.2", @@ -2865,8 +4679,7 @@ "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "js-yaml": { "version": "3.14.1", @@ -2886,8 +4699,7 @@ "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" }, "json-bigint": { "version": "1.0.0", @@ -2939,6 +4751,15 @@ "minimist": "^1.2.0" } }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "optional": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, "jsonwebtoken": { "version": "8.5.1", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", @@ -3059,13 +4880,13 @@ } }, "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" } }, "load-json-file": { @@ -3106,12 +4927,24 @@ "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=", "dev": true }, + "lodash.flatmap": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.flatmap/-/lodash.flatmap-4.5.0.tgz", + "integrity": "sha1-74y/QI9uSCaGYzRTBcaswLd4cC4=", + "optional": true + }, "lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", "dev": true }, + "lodash.groupby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", + "integrity": "sha1-Cwih3PaDl8OXhVwyOXg4Mt90A9E=", + "optional": true + }, "lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -3161,6 +4994,19 @@ "chalk": "^2.4.2" } }, + "log4js": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.2.1.tgz", + "integrity": "sha512-7n+Oqxxz7VcQJhIlqhcYZBTpbcQ7XsR0MUIfJkx/n3VUjkAS4iUr+4UJlhxf28RvP9PMGQXbgTUhLApnu0XXgA==", + "optional": true, + "requires": { + "date-format": "^3.0.0", + "debug": "^4.1.1", + "flatted": "^2.0.1", + "rfdc": "^1.1.4", + "streamroller": "^2.2.4" + } + }, "lolex": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lolex/-/lolex-4.2.0.tgz", @@ -3229,6 +5075,12 @@ } } }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "optional": true + }, "mime-db": { "version": "1.47.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", @@ -3242,12 +5094,6 @@ "mime-db": "1.47.0" } }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, "mimic-response": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", @@ -3533,11 +5379,26 @@ "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==" }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", - "dev": true + "mutation-testing-elements": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/mutation-testing-elements/-/mutation-testing-elements-1.7.5.tgz", + "integrity": "sha512-s42vYrnIt3IF9nvnSjGMUqv8kwxxpXVNiaBVl+a29p2c3ec4xn7jq/VnmYYwhGE0I9DTNYzxgO9kg0pwm4deOA==", + "optional": true + }, + "mutation-testing-metrics": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/mutation-testing-metrics/-/mutation-testing-metrics-1.7.5.tgz", + "integrity": "sha512-BkXuzaMHzP3V+1QlScJ0es13PWEIXsc48t8/OMuCB/RDyCKKblZNlGb7KpY4oDgU0VIFMR6sBJ4F3IFkY6Elnw==", + "optional": true, + "requires": { + "mutation-testing-report-schema": "1.7.4" + } + }, + "mutation-testing-report-schema": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/mutation-testing-report-schema/-/mutation-testing-report-schema-1.7.4.tgz", + "integrity": "sha512-69CxAaIBprkxvHkZ/1zDJesFOxiXAKUpOeK6xUHAmfqMW3zYfb+nPae40GwTQt9WFFCHj56O6d6GJzR7Qm2ZwQ==", + "optional": true }, "nan": { "version": "2.14.2", @@ -3589,12 +5450,6 @@ "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz", "integrity": "sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==" }, - "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.3", "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.3.tgz", @@ -3663,9 +5518,9 @@ } }, "node-abi": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.29.0.tgz", - "integrity": "sha512-+xu2xkzOkdVS0YAVpiF+xsYM+hM1ylmeGUaG1OC3To6+pUIL4yoCqU6ggWpZeeiq0GFPE2uUOCf36Y4iq3JnOA==", + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", + "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==", "optional": true, "requires": { "semver": "^5.4.1" @@ -3709,6 +5564,12 @@ "tar": "^4.4.2" } }, + "node-releases": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", + "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", + "optional": true + }, "node-scp": { "version": "0.0.14", "resolved": "https://registry.npmjs.org/node-scp/-/node-scp-0.0.14.tgz", @@ -3733,9 +5594,9 @@ "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" }, "underscore": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", - "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==" + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", + "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=" } } }, @@ -3799,6 +5660,23 @@ "npm-normalize-package-bin": "^1.0.1" } }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "optional": true, + "requires": { + "path-key": "^3.0.0" + }, + "dependencies": { + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "optional": true + } + } + }, "npmlog": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", @@ -3984,8 +5862,7 @@ "object-inspect": { "version": "1.10.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", - "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==", - "dev": true + "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==" }, "object-is": { "version": "1.1.5", @@ -4016,14 +5893,75 @@ } }, "object.entries": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.4.tgz", - "integrity": "sha512-h4LWKWE+wKQGhtMjZEBud7uLGhqyLwj8fpHOarZhD2uY3C9cRtk57VQ89ke3moByLXMedqs3XCHzyb4AmA2DjA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", + "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.2" + "es-abstract": "^1.19.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "object-inspect": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "dev": true + } } }, "object.getownpropertydescriptors": { @@ -4038,14 +5976,75 @@ } }, "object.values": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.4.tgz", - "integrity": "sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.2" + "es-abstract": "^1.19.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "object-inspect": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "dev": true + } } }, "once": { @@ -4056,32 +6055,23 @@ "wrappy": "1" } }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, "optional": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/optional/-/optional-0.1.4.tgz", "integrity": "sha512-gtvrrCfkE08wKcgXaVwQVgwEQ8vel2dc5DDBn9RLQZ3YtmtkBss6A2HY6BnJH4N/4Ku97Ri/SF8sNWE2225WJw==" }, "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", "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" + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" } }, "optjs": { @@ -4090,6 +6080,165 @@ "integrity": "sha1-aabOicRCpEQDFBrS+bNwvVu29O4=", "dev": true }, + "ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "optional": true, + "requires": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "optional": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "optional": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "optional": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "optional": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "optional": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "optional": true, + "requires": { + "restore-cursor": "^3.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==", + "optional": 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==", + "optional": 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==", + "optional": true + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "optional": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "optional": true + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "optional": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "optional": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "optional": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "optional": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "optional": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", @@ -4107,8 +6256,7 @@ "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 + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, "osenv": { "version": "0.1.5", @@ -4190,19 +6338,12 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, "path-parse": { @@ -4236,6 +6377,12 @@ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "optional": true + }, "picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", @@ -4257,15 +6404,6 @@ "find-up": "^2.1.0" } }, - "pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", - "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - } - }, "prebuild-install": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.0.tgz", @@ -4291,9 +6429,9 @@ } }, "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, "process-nextick-args": { @@ -4304,8 +6442,7 @@ "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" }, "prom-client": { "version": "11.0.0", @@ -4417,16 +6554,6 @@ "path-type": "^3.0.0" } }, - "read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - } - }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -4468,9 +6595,9 @@ } }, "regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true }, "release-zalgo": { @@ -4514,6 +6641,12 @@ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "optional": true + }, "require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", @@ -4536,21 +6669,17 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, "retry": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=" }, + "rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "optional": true + }, "rimraf": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", @@ -4564,16 +6693,7 @@ "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.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } + "optional": true }, "safe-buffer": { "version": "5.2.1", @@ -4601,18 +6721,18 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "requires": { - "shebang-regex": "^1.0.0" + "shebang-regex": "^3.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=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, "shimmer": { @@ -4620,6 +6740,16 @@ "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, "signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", @@ -4657,25 +6787,6 @@ "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==", - "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 - } - } - }, "snappy": { "version": "6.3.5", "resolved": "https://registry.npmjs.org/snappy/-/snappy-6.3.5.tgz", @@ -4690,8 +6801,7 @@ "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" }, "spawn-wrap": { "version": "1.4.3", @@ -4797,6 +6907,25 @@ "resolved": "https://registry.npmjs.org/statsd-client/-/statsd-client-0.4.7.tgz", "integrity": "sha512-+sGCE6FednJ/vI7vywErOg/mhVqmf6Zlktz7cdGRnF/cQWXD9ifMgtqU1CIIXmhSwm11SCk4zDN+bwNCvIR/Kg==" }, + "streamroller": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-2.2.4.tgz", + "integrity": "sha512-OG79qm3AujAM9ImoqgWEY1xG4HX+Lw+yY6qZj9R1K2mhF5bEmQ849wvrb+4vt4jLMLzwXttJlQbOdPOQVRv7DQ==", + "optional": true, + "requires": { + "date-format": "^2.1.0", + "debug": "^4.1.1", + "fs-extra": "^8.1.0" + }, + "dependencies": { + "date-format": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz", + "integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==", + "optional": true + } + } + }, "streamsearch": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", @@ -4862,6 +6991,12 @@ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "optional": true + }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -4871,57 +7006,10 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "requires": { "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 - }, - "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" - } - }, - "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" - } - } - } - }, "tar": { "version": "4.4.13", "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", @@ -5069,7 +7157,7 @@ "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true + "optional": true }, "tiny-request-router": { "version": "1.2.2", @@ -5083,7 +7171,7 @@ "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, + "optional": true, "requires": { "os-tmpdir": "~1.0.2" } @@ -5097,8 +7185,7 @@ "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" }, "to-regex-range": { "version": "5.0.1", @@ -5123,6 +7210,12 @@ "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=" }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "optional": true + }, "tsconfig-paths": { "version": "3.11.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.11.0.tgz", @@ -5139,7 +7232,13 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "optional": true + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "optional": true }, "tunnel-agent": { "version": "0.6.0", @@ -5155,12 +7254,12 @@ "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=", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "requires": { - "prelude-ls": "~1.1.2" + "prelude-ls": "^1.2.1" } }, "type-detect": { @@ -5169,6 +7268,40 @@ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true }, + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "optional": true + }, + "typed-inject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/typed-inject/-/typed-inject-3.0.1.tgz", + "integrity": "sha512-5yr8inrNos7uo/irp5PZ7WNwmYGfoa0w1NiDdCWW6hhIxYH2NCqYwX9BUOXpZgxk964rb1ElEfvBtftuvIPpvw==", + "optional": true + }, + "typed-rest-client": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.6.tgz", + "integrity": "sha512-xcQpTEAJw2DP7GqVNECh4dD+riS+C1qndXLfBCJ3xk0kqprtGN491P5KlmrDbKdtuW8NEcP/5ChxiJI3S9WYTA==", + "optional": true, + "requires": { + "qs": "^6.9.1", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + }, + "dependencies": { + "qs": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", + "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", + "optional": true, + "requires": { + "side-channel": "^1.0.4" + } + } + } + }, "unbox-primitive": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", @@ -5181,6 +7314,18 @@ "which-boxed-primitive": "^1.0.2" } }, + "underscore": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", + "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==", + "optional": true + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "optional": true + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -5215,6 +7360,12 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -5235,6 +7386,21 @@ "extsprintf": "^1.2.0" } }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "optional": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "weapon-regex": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/weapon-regex/-/weapon-regex-0.6.0.tgz", + "integrity": "sha512-k1gh8cffl+uOEakFS3Ze32nBsoqmcXembTI3nNQMPMUKAr83YBShTrjYGyeVC9zNzW5Ac/+dcvk3PLYUtNtSEQ==", + "optional": true + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -5326,15 +7492,6 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, - "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" - } - }, "write-file-atomic": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", diff --git a/package.json b/package.json index f0584f7c..ee0509c8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "f5-telemetry", - "version": "1.24.0-3", + "version": "1.25.0-2", "author": "F5 Networks", "license": "Apache-2.0", "repository": { @@ -14,7 +14,10 @@ "test-functional": "mocha \"./test/functional/testRunner.js\" --opts ./test/functional/.mocha.opts", "test-functional-cloud-azure": "mocha \"./test/functional/cloud/azureTests.js\" --opts ./test/functional/.mocha.opts", "test-functional-cloud-aws": "mocha \"./test/functional/cloud/awsTests.js\" --opts ./test/functional/.mocha.opts", - "test-only": "mocha --opts ./test/unit/.mocha.opts", + "test-mutation": "stryker run", + "test-only": "mocha --recursive --opts ./test/unit/.mocha.opts \"./test/unit/**/*.js\"", + "test-specific": "mocha --opts ./test/unit/.mocha.opts", + "test-specific-coverage": "nyc --all npm run test-specific", "test": "nyc --all npm run test-only", "build": "./scripts/build/buildRpm.sh" }, @@ -31,23 +34,24 @@ "test/**", "scripts/**", "examples/**", - "**/node_modules/**" + "**/node_modules/**", + "stryker.conf.js" ] }, "dependencies": { "@f5devcentral/f5-teem": "^1.5.0", - "@grpc/grpc-js": "^1.3.4", + "@grpc/grpc-js": "^1.4.2", "@grpc/proto-loader": "~0.3.0", "@opentelemetry/api": "^1.0.3", - "@opentelemetry/exporter-collector-proto": "^0.24.0", - "@opentelemetry/sdk-metrics-base": "^0.24.1-alpha.4", + "@opentelemetry/exporter-collector-proto": "^0.25.0", + "@opentelemetry/sdk-metrics-base": "^0.26.0", "ajv": "^6.12.6", "ajv-keywords": "^3.5.2", "applicationinsights": "^1.8.10", - "aws-sdk": "^2.991.0", + "aws-sdk": "2.1018.0", "commander": "^2.20.3", "deep-diff": "^1.0.2", - "eventemitter2": "^6.4.4", + "eventemitter2": "^6.4.5", "google-auth-library": "^6.1.6", "jmespath": "^0.15.0", "json-duplicate-key-handle": "file:opensource/json-duplicate-key-handle", @@ -67,9 +71,12 @@ "@f5devcentral/eslint-config-f5-atg": "latest", "chai": "^4.3.4", "chai-as-promised": "^7.1.1", + "eslint": "^8.1.0", + "eslint-config-airbnb-base": "^14.2.1", + "eslint-plugin-import": "^2.25.2", "grpc": "1.24.7", "grpc-mock": "^0.7.0", - "icrdk": "git://github.com/f5devcentral/f5-icontrollx-dev-kit.git#master", + "icrdk": "git+https://github.com/f5devcentral/f5-icontrollx-dev-kit.git#master", "mocha": "^7.2.0", "nock": "10.0.0", "node-scp": "0.0.14", @@ -84,7 +91,8 @@ "extends": "@f5devcentral/eslint-config-f5-atg", "rules": { "func-names": "off", - "prefer-spread": "off" + "prefer-spread": "off", + "max-classes-per-file": "off" } }, "comments": { @@ -93,6 +101,7 @@ "ajv": "This package dropped support for older node versions and requires the ajv package to be recompiled. Use v6.X.Y for Node v4.", "ajv-keywords": "This package documents that v3 should be used when using ajv v6", "applicationinsights": "This package requires Node v8 in 2.0.0+ as it now includes OpenTelemetry packages. Use v1.X.Y for Node v4.", + "aws-sdk": "This package dropped support for older node versions. Use 2.1018.0 for Node < v10.", "commander": "This package dropped support for older node versions. Use v2.X.Y for Node v4.", "google-auth-library": "This package is used for GRPC connection, supports node v10 and above, but fromJSON function works in node v8.11.1 - should use v6.1.X only", "json-duplicate-key-handle": "This package is included locally to track updates and/or vulnerabilities. Included version is v1.0.0", @@ -122,5 +131,9 @@ "ssh2": "This packaged dropped support for older node versions. Use v0.X.Y for Node >= v5.2 and <= v10.16", "winston": "This package dropped support for older node versions. Use v2.X.Y for Node v4." } + }, + "optionalDependencies": { + "@stryker-mutator/core": "^5.4.1", + "@stryker-mutator/mocha-runner": "^5.4.1" } } diff --git a/src/lib/config.js b/src/lib/config.js index f473232c..8972956f 100644 --- a/src/lib/config.js +++ b/src/lib/config.js @@ -18,7 +18,7 @@ const SafeEventEmitter = require('./utils/eventEmitter').SafeEventEmitter; const TeemReporter = require('./teemReporter').TeemReporter; const util = require('./utils/misc'); -/** @module ConfigWorker */ +/** @module config */ const BASE_CONFIG = { components: [], @@ -34,15 +34,15 @@ const BASE_STORAGE_DATA = {}; * * @event change - config was validated and can be propagated * - * @property {Configuration} currentConfig - copy of current configuration - * @property {Logger} logger - logger instance + * @property {configUtil.Configuration} currentConfig - copy of current configuration + * @property {logger.Logger} logger - logger instance * @property {TeemReporter} teemReporter - TeemReporter instance * @property {object} validators - AJV validators */ class ConfigWorker extends SafeEventEmitter { /** * @public - * @returns {Configuration} copy of current configuration + * @returns {configUtil.Configuration} copy of current configuration */ get currentConfig() { return util.deepCopy(this._currentConfig || BASE_CONFIG); @@ -50,7 +50,7 @@ class ConfigWorker extends SafeEventEmitter { /** * @public - * @returns {Logger} instance + * @returns {logger.Logger} instance */ get logger() { // lazy initialization @@ -99,7 +99,7 @@ class ConfigWorker extends SafeEventEmitter { * Get raw (original) config * * @public - * @param {string} namespace - namespace name + * @param {string} [namespace] - namespace name * * @returns {Promise} resolved with raw (original) config - full declaration * if no namespace param provided, otherwise just the namespace config @@ -127,7 +127,7 @@ class ConfigWorker extends SafeEventEmitter { */ load() { return this.getDeclaration() - .then(declaration => this.processDeclaration(declaration)) + .then((declaration) => this.processDeclaration(declaration)) .catch((error) => { this.logger.exception('Unable to load and validate existing declaration', error); this.logger.warning('Going to try to load default empty declaration. Old declaration is still accessible via API'); @@ -167,7 +167,7 @@ class ConfigWorker extends SafeEventEmitter { options.namespaceToUpdate = namespace; declaration[namespace] = namespaceDeclaration; return this.processDeclaration(declaration, options) - .then(fullConfig => (fullConfig[namespace] || {})); + .then((fullConfig) => (fullConfig[namespace] || {})); }); } @@ -283,7 +283,7 @@ function expandDeclaration(declaration) { * NOTE: it mutates 'newConfig'. * * @this ConfigWorker - * @param {Configuration} newConfig - new config + * @param {configUtil.Configuration} newConfig - new config * @param {object} options - options when setting config * @param {string} options.namespaceToUpdate - namespace of components * that are the only ones that need updating instead of all config @@ -314,7 +314,7 @@ function notifyConfigChange(newConfig, options) { * the config must make its own local copy. * * @event ConfigWorker#change - * @type {Configuration} + * @type {configUtil.Configuration} */ return this.emitAsync('change', decryptedConfig); }) @@ -346,7 +346,7 @@ function saveToStorage(storageData) { * * @this ConfigWorker * @param {object} declaration - declaration to validate against config schema - * @param {object} options - optional validation settings + * @param {object} options - optional validation settings * @param {string} options.schemaType - type of schema to validate against. Defaults to full (whole schema) * @param {object} options.context - additional context to pass through to validator * @@ -358,17 +358,17 @@ function validate(declaration, options) { if (typeof validatorFunc !== 'undefined') { // AJV validators mutates 'declaration' return configUtil.validate(validatorFunc, declaration, options.context) - .catch(err => Promise.reject(new errors.ValidationError(err))); + .catch((err) => Promise.reject(new errors.ValidationError(err))); } return Promise.reject(new Error('Validator is not available')); } - // initialize singleton const configWorker = new ConfigWorker(); +configWorker.setMaxListeners(20); // config worker change event, should be first in the handlers chain -configWorker.on('change', config => new Promise((resolve) => { +configWorker.on('change', (config) => new Promise((resolve) => { const settings = configUtil.getTelemetryControls(config); if (!util.isObjectEmpty(settings)) { // default value should be 'info' @@ -379,7 +379,7 @@ configWorker.on('change', config => new Promise((resolve) => { // handle EventEmitter errors to avoid NodeJS crashing configWorker.on('error', (err) => { - this.logger.exception('Unhandled error in ConfigWorker', err); + configWorker.logger.exception('Unhandled error in ConfigWorker', err); }); module.exports = configWorker; diff --git a/src/lib/constants.js b/src/lib/constants.js index fefcf75b..7a3520d6 100644 --- a/src/lib/constants.js +++ b/src/lib/constants.js @@ -49,7 +49,6 @@ const schemaInfo = (function () { const VERSION = packageVersionInfo[0]; const RELEASE = packageVersionInfo[1]; - /** * Create new Object with value => key mapping from source Object * @@ -65,7 +64,6 @@ function valuesToKeys(srcObj) { return dstObj; } - const PROTO_TO_PORT = { http: 80, https: 443 @@ -84,7 +82,6 @@ const DAY_NAME_TO_WEEKDAY = { const WEEKDAY_TO_DAY_NAME = valuesToKeys(DAY_NAME_TO_WEEKDAY); WEEKDAY_TO_DAY_NAME[7] = 'sunday'; - module.exports = { RELEASE, VERSION, diff --git a/src/lib/consumers.js b/src/lib/consumers.js index f2c77e14..18b4b326 100644 --- a/src/lib/consumers.js +++ b/src/lib/consumers.js @@ -45,15 +45,15 @@ function loadConsumers(config) { return Promise.resolve([]); } - const enabledConsumers = config.filter(c => c.enable); + const enabledConsumers = config.filter((c) => c.enable); if (enabledConsumers.length === 0) { logger.debug('No enabled consumer(s) to load'); return Promise.resolve([]); } logger.debug(`Loading consumer specific plug-ins from ${CONSUMERS_DIR}`); - const loadPromises = enabledConsumers.map(consumerConfig => new Promise((resolve) => { - const existingConsumer = CONSUMERS.find(c => c.id === consumerConfig.id); + const loadPromises = enabledConsumers.map((consumerConfig) => new Promise((resolve) => { + const existingConsumer = CONSUMERS.find((c) => c.id === consumerConfig.id); if (consumerConfig.skipUpdate && existingConsumer) { resolve(existingConsumer); } else { @@ -86,7 +86,7 @@ function loadConsumers(config) { } })); return Promise.all(loadPromises) - .then(loadedConsumers => loadedConsumers.filter(c => c !== undefined)); + .then((loadedConsumers) => loadedConsumers.filter((c) => c !== undefined)); } /** @@ -96,7 +96,7 @@ function loadConsumers(config) { */ function getLoadedConsumerTypes() { if (CONSUMERS.length > 0) { - return new Set(CONSUMERS.map(consumer => consumer.config.type)); + return new Set(CONSUMERS.map((consumer) => consumer.config.type)); } return new Set(); } @@ -122,7 +122,7 @@ function unloadUnusedModules(before) { } // config worker change event -configWorker.on('change', config => Promise.resolve() +configWorker.on('change', (config) => Promise.resolve() .then(() => { logger.debug('configWorker change event in consumers'); @@ -139,7 +139,6 @@ configWorker.on('change', config => Promise.resolve() .then(() => unloadUnusedModules(typesBefore)); })); - module.exports = { getConsumers: () => CONSUMERS }; diff --git a/src/lib/consumers/AWS_CloudWatch/index.js b/src/lib/consumers/AWS_CloudWatch/index.js index 714bdb3b..b16d83c8 100644 --- a/src/lib/consumers/AWS_CloudWatch/index.js +++ b/src/lib/consumers/AWS_CloudWatch/index.js @@ -8,7 +8,7 @@ 'use strict'; -const awsUtil = require('./../shared/awsUtil'); +const awsUtil = require('../shared/awsUtil'); const EVENT_TYPES = require('../../constants').EVENT_TYPES; /** diff --git a/src/lib/consumers/AWS_S3/index.js b/src/lib/consumers/AWS_S3/index.js index 1d2cf892..97ea1151 100644 --- a/src/lib/consumers/AWS_S3/index.js +++ b/src/lib/consumers/AWS_S3/index.js @@ -9,7 +9,7 @@ 'use strict'; const AWS = require('aws-sdk'); -const awsUtil = require('./../shared/awsUtil'); +const awsUtil = require('../shared/awsUtil'); const util = require('../../utils/misc'); /** * See {@link ../README.md#context} for documentation @@ -29,7 +29,11 @@ module.exports = function (context) { return awsUtil.initializeConfig(context) .then(() => { - s3 = new AWS.S3({ apiVersion: '2006-03-01' }); + const clientProperties = { apiVersion: '2006-03-01' }; + if (context.config.endpointUrl) { + clientProperties.endpoint = new AWS.Endpoint(context.config.endpointUrl); + } + s3 = new AWS.S3(clientProperties); const params = { // fallback to host if no bucket Bucket: context.config.bucket || context.config.host, diff --git a/src/lib/consumers/Azure_Application_Insights/index.js b/src/lib/consumers/Azure_Application_Insights/index.js index 5faaa9ae..5ba34ce3 100644 --- a/src/lib/consumers/Azure_Application_Insights/index.js +++ b/src/lib/consumers/Azure_Application_Insights/index.js @@ -9,7 +9,7 @@ 'use strict'; const appInsights = require('applicationinsights'); -const azureUtil = require('./../shared/azureUtil'); +const azureUtil = require('../shared/azureUtil'); const EVENT_TYPES = require('../../constants').EVENT_TYPES; /** diff --git a/src/lib/consumers/Azure_Log_Analytics/index.js b/src/lib/consumers/Azure_Log_Analytics/index.js index df1f4271..a8f91c87 100644 --- a/src/lib/consumers/Azure_Log_Analytics/index.js +++ b/src/lib/consumers/Azure_Log_Analytics/index.js @@ -9,7 +9,7 @@ 'use strict'; const util = require('../../utils/misc'); -const azureUtil = require('./../shared/azureUtil'); +const azureUtil = require('../shared/azureUtil'); const requestsUtil = require('../../utils/requests'); const EVENT_TYPES = require('../../constants').EVENT_TYPES; @@ -47,7 +47,7 @@ module.exports = function (context) { } else { data = [data]; // place in array per API spec } - data.forEach(d => azureUtil.scrubReservedKeys(d)); + data.forEach((d) => azureUtil.scrubReservedKeys(d)); const date = new Date().toUTCString(); const bodyString = JSON.stringify(data); @@ -66,7 +66,6 @@ module.exports = function (context) { allowSelfSignedCert: context.config.allowSelfSignedCert }; - if (context.metadata && context.metadata.compute && context.metadata.compute.resourceId) { requestOptions.headers['x-ms-AzureResourceId'] = context.metadata.compute.resourceId; } diff --git a/src/lib/consumers/DataDog/index.js b/src/lib/consumers/DataDog/index.js index 9e2171ec..fdb7b0a1 100644 --- a/src/lib/consumers/DataDog/index.js +++ b/src/lib/consumers/DataDog/index.js @@ -67,6 +67,12 @@ module.exports = function (context) { const interval = (data.telemetryServiceInfo && data.telemetryServiceInfo.pollingInterval) || undefined; const ddService = context.config.service; const ddRegion = context.config.region; + const metricPrefix = context.config.metricPrefix ? `${context.config.metricPrefix.join('.')}.` : ''; + const boolsToMetrics = context.config.convertBooleansToMetrics || false; + const customTags = (context.config.customTags || []).reduce((result, tag) => { + result[tag.name] = tag.value; + return result; + }, {}); // for now use current time, ideally should try to fetch it from event data const timestamp = Date.now() / 1000; @@ -78,7 +84,7 @@ module.exports = function (context) { ddData = { ddsource: context.event.type, // usually there is nothing along this data that can be used as a tag - ddtags: buildTags({ telemetryEventCategory: eventType }, true), + ddtags: buildTags(Object.assign({ telemetryEventCategory: eventType }, customTags), true), hostname, message: data.data, service: ddService @@ -92,6 +98,7 @@ module.exports = function (context) { collectTags: true, excludeNameFromPath: true, parseMetrics: true, + boolsToMetrics, onMetric: (metricPath, metricValue, metricTags) => { // ignore timestamps and intervals if (metricPath[metricPath.length - 1].indexOf('imestamp') === -1 @@ -99,9 +106,9 @@ module.exports = function (context) { ddData.series.push({ host: hostname, interval, - metric: buildMetricName(metricPath), + metric: buildMetricName(metricPrefix, metricPath), points: [[timestamp, metricValue]], - tags: buildTags(metricTags), + tags: buildTags(Object.assign(metricTags, customTags)), type: DATA_DOG_METRIC_TYPE }); } @@ -116,6 +123,7 @@ module.exports = function (context) { * Looks like no metrics were found, then let's * transform this event into log message and attach * all possible tags to it + * Ex: LTM data */ let ddtags = { telemetryEventCategory: eventType }; metricsUtil.findMetricsAndTags(data, { @@ -129,7 +137,7 @@ module.exports = function (context) { ddType = 'log'; ddData = { ddsource: context.event.type, - ddtags: buildTags(ddtags, true), + ddtags: buildTags(Object.assign(ddtags, customTags), true), hostname, message: JSON.stringify(data), service: ddService @@ -158,7 +166,7 @@ module.exports = function (context) { 'Accept-Encoding': encoding, 'Content-Encoding': encoding }); - gzipPromise = gzipPromise.then(payload => new Promise((resolve, reject) => { + gzipPromise = gzipPromise.then((payload) => new Promise((resolve, reject) => { zlibMeth.call(zlib, JSON.stringify(payload), (err, buffer) => { if (!err) { resolve(buffer); @@ -169,7 +177,7 @@ module.exports = function (context) { })); } - return gzipPromise.then(payload => requestsUtil.makeRequest({ + return gzipPromise.then((payload) => requestsUtil.makeRequest({ body: payload, expectedResponseCode: [200, 202], fullURI: getDataDogGateway(ddRegion, ddType), @@ -186,26 +194,27 @@ module.exports = function (context) { }; /** - * Builds metric name from path + * Builds metric name from metric prefix and the metric path * - * @param {Array} mpath - metric' path + * @param {Array} metricPrefix - prefix for each metric name + * @param {Array} metricPath - metric path * * @returns {string} metric name */ -function buildMetricName(mpath) { - return mpath.join('.'); +function buildMetricName(metricPrefix, metricPath) { + return `${metricPrefix}${metricPath.join('.')}`; } /** * Builds metric tags * - * @param {object} tags - metric' tags + * @param {object} tags - metric tags * @param {boolean} [joinAll = false] - join all tags into a string * * @returns {Array | string} tags */ function buildTags(tags, joinAll) { - tags = Object.keys(tags).map(key => `${key}:${tags[key]}`); + tags = Object.keys(tags).map((key) => `${key}:${tags[key]}`); if (joinAll) { tags = tags.join(','); } diff --git a/src/lib/consumers/ElasticSearch/index.js b/src/lib/consumers/ElasticSearch/index.js index 235b0541..cc95f60a 100644 --- a/src/lib/consumers/ElasticSearch/index.js +++ b/src/lib/consumers/ElasticSearch/index.js @@ -8,7 +8,7 @@ 'use strict'; -const httpUtil = require('./../shared/httpUtil'); +const httpUtil = require('../shared/httpUtil'); const util = require('../../utils/misc'); const EVENT_TYPES = require('../../constants').EVENT_TYPES; @@ -21,7 +21,7 @@ const DEFAULT_DOC_TYPE = '_doc'; * * @returns {Boolean} Whether or not the dataType property is deprecated */ -const dataTypeIsDeprecated = config => util.compareVersionStrings(config.apiVersion, '>=', '7.0'); +const dataTypeIsDeprecated = (config) => util.compareVersionStrings(config.apiVersion, '>=', '7.0'); /** * Returns the appropriate http URI, given the consumer's context. diff --git a/src/lib/consumers/F5_Cloud/index.js b/src/lib/consumers/F5_Cloud/index.js index a66329cf..ca9413ba 100644 --- a/src/lib/consumers/F5_Cloud/index.js +++ b/src/lib/consumers/F5_Cloud/index.js @@ -84,7 +84,7 @@ module.exports = function (context) { */ if (context.event.isCustom && context.event.type === constants.EVENT_TYPES.SYSTEM_POLLER - && Object.keys(data).every(key => key.indexOf(':') !== -1 + && Object.keys(data).every((key) => key.indexOf(':') !== -1 || key === 'telemetryEventCategory' || key === 'telemetryServiceInfo')) { Object.keys(data).forEach((key) => { diff --git a/src/lib/consumers/Generic_HTTP/index.js b/src/lib/consumers/Generic_HTTP/index.js index e7ce90b7..ee891349 100644 --- a/src/lib/consumers/Generic_HTTP/index.js +++ b/src/lib/consumers/Generic_HTTP/index.js @@ -8,7 +8,7 @@ 'use strict'; -const httpUtil = require('./../shared/httpUtil'); +const httpUtil = require('../shared/httpUtil'); const util = require('../../utils/misc'); /** diff --git a/src/lib/consumers/Google_Cloud_Logging/index.js b/src/lib/consumers/Google_Cloud_Logging/index.js index e9949a84..1055f29c 100644 --- a/src/lib/consumers/Google_Cloud_Logging/index.js +++ b/src/lib/consumers/Google_Cloud_Logging/index.js @@ -8,7 +8,7 @@ 'use strict'; -const gcpUtil = require('./../shared/gcpUtil'); +const gcpUtil = require('../shared/gcpUtil'); const requestsUtil = require('../../utils/requests'); const CLOUD_LOGGING_WRITE_URL = 'https://logging.googleapis.com/v2/entries:write'; @@ -21,8 +21,10 @@ module.exports = function (context) { const serviceAccount = { serviceEmail: config.serviceEmail, privateKeyId: config.privateKeyId, - privateKey: config.privateKey + privateKey: config.privateKey, + useServiceAccountToken: config.useServiceAccountToken }; + // Should be in format projects/[PROJECT_ID]/logs/[LOG_ID] const logName = `${config.logScope}/${config.logScopeId}/logs/${config.logId}`; diff --git a/src/lib/consumers/Google_Cloud_Monitoring/index.js b/src/lib/consumers/Google_Cloud_Monitoring/index.js index 28162530..9d31a3b7 100644 --- a/src/lib/consumers/Google_Cloud_Monitoring/index.js +++ b/src/lib/consumers/Google_Cloud_Monitoring/index.js @@ -8,7 +8,7 @@ 'use strict'; -const gcpUtil = require('./../shared/gcpUtil'); +const gcpUtil = require('../shared/gcpUtil'); const EVENT_TYPES = require('../../constants').EVENT_TYPES; const requestsUtil = require('../../utils/requests'); @@ -29,7 +29,7 @@ function checkMetricDescriptors(data, descriptors, currentPath, metrics) { key, value: data[key], path, - new: descriptors.findIndex(element => element.type.includes(path)) === -1 + new: descriptors.findIndex((element) => element.type.includes(path)) === -1 }; metrics.push(metric); } @@ -49,7 +49,8 @@ module.exports = function (context) { const serviceAccount = { serviceEmail: context.config.serviceEmail, privateKeyId: context.config.privateKeyId, - privateKey: context.config.privateKey + privateKey: context.config.privateKey, + useServiceAccountToken: context.config.useServiceAccountToken }; let Authorization; diff --git a/src/lib/consumers/OpenTelemetry_Exporter/index.js b/src/lib/consumers/OpenTelemetry_Exporter/index.js index a9fb9d13..313362a2 100644 --- a/src/lib/consumers/OpenTelemetry_Exporter/index.js +++ b/src/lib/consumers/OpenTelemetry_Exporter/index.js @@ -13,7 +13,7 @@ const CollectorMetricExporter = require('@opentelemetry/exporter-collector-proto const otelApi = require('@opentelemetry/api'); const metricsUtil = require('../shared/metricsUtil'); -const httpUtil = require('./../shared/httpUtil'); +const httpUtil = require('../shared/httpUtil'); const EVENT_TYPES = require('../../constants').EVENT_TYPES; @@ -78,6 +78,7 @@ module.exports = function (context) { const port = context.config.port; const metricsPath = context.config.metricsPath || ''; const headers = httpUtil.processHeaders(context.config.headers); // no defaults - provide all headers needed + const boolsToMetrics = context.config.convertBooleansToMetrics || false; // We cannot process ihealth/syslog/raw/event data, so don't even try. // Other event types may not contain metrics, but handle later @@ -89,7 +90,7 @@ module.exports = function (context) { return Promise.resolve(); } - const metrics = createMetrics(context.event.data); + const metrics = createMetrics(context.event.data, { boolsToMetrics }); if (Object.keys(metrics).length === 0) { context.logger.debug('Event did not contain any metrics, skipping'); return Promise.resolve(); @@ -108,7 +109,6 @@ module.exports = function (context) { interval: 60 * 1000 // give us 60s to initialize/set metric set }).getMeter('telemetry-streaming'); - if (context.tracer) { context.tracer.write(metrics); } @@ -135,15 +135,19 @@ module.exports = function (context) { /** * Convert Telemetry Streaming data into a collection of OpenTelemetry compatible metrics * - * @param {Object} data - Telemetry Streaming metric data + * @param {Object} data - Telemetry Streaming metric data + * @param {Object} [opts] - Options for creating metrics + * @param {Boolean} [opts.boolsToMetrics] - Whether to convert booleans values to metrics */ -function createMetrics(data) { +function createMetrics(data, opts) { + opts = opts || {}; const otelMetrics = {}; metricsUtil.findMetricsAndTags(data, { collectTags: true, excludeNameFromPath: true, parseMetrics: true, + boolsToMetrics: opts.boolsToMetrics, onMetric: (metricPath, value, tags) => { // ignore timestamps and intervals if (metricPath[metricPath.length - 1].indexOf('imestamp') === -1 diff --git a/src/lib/consumers/Splunk/dataMapping.js b/src/lib/consumers/Splunk/dataMapping.js index ac6cc194..6c23bc14 100644 --- a/src/lib/consumers/Splunk/dataMapping.js +++ b/src/lib/consumers/Splunk/dataMapping.js @@ -110,7 +110,7 @@ const STAT_2_TMCTL_TABLE = { }; function getTemplate(sourceName, data, cache) { - let deviceGroup = Object.keys(data.deviceGroups || {}).find(dgKey => data.deviceGroups[dgKey].type === 'sync-failover'); + let deviceGroup = Object.keys(data.deviceGroups || {}).find((dgKey) => data.deviceGroups[dgKey].type === 'sync-failover'); deviceGroup = (deviceGroup || '').split('/').pop(); return { @@ -176,7 +176,7 @@ function ihealth(request) { const solutions = diagnostic.solution || []; const versions = diagnostic.version || []; - const verStr = verObj => `${verObj.major}.${verObj.minor}.${verObj.maintenance}.${verObj.point}`; + const verStr = (verObj) => `${verObj.major}.${verObj.minor}.${verObj.maintenance}.${verObj.point}`; newData.event = { Category: diagnostic.importance, @@ -184,8 +184,8 @@ function ihealth(request) { Internal: 'no', Title: diagnostic.header, Description: diagnostic.summary, - Solutions: solutions.map(s => s.id).join(' '), - 'Solution Hyperlinks': solutions.map(s => s.value).join(' '), + Solutions: solutions.map((s) => s.id).join(' '), + 'Solution Hyperlinks': solutions.map((s) => s.value).join(' '), version: versions.map(verStr).join(' '), hostname: system.hostname, ihealth_link: system.ihealthLink, @@ -196,7 +196,6 @@ function ihealth(request) { return output; } - const stats = [ function (request) { const data = getData(request, 'system'); @@ -467,7 +466,6 @@ const stats = [ } ]; - module.exports = { overall, stats, diff --git a/src/lib/consumers/Splunk/index.js b/src/lib/consumers/Splunk/index.js index 42a206d8..82d7db39 100644 --- a/src/lib/consumers/Splunk/index.js +++ b/src/lib/consumers/Splunk/index.js @@ -15,7 +15,6 @@ const EVENT_TYPES = require('../../constants').EVENT_TYPES; const memConverter = require('./multiMetricEventConverter'); const httpUtil = require('../shared/httpUtil'); - const MAX_CHUNK_SIZE = 99000; const HEC_EVENTS_URI = '/services/collector/event'; const HEC_METRICS_URI = '/services/collector'; @@ -32,7 +31,7 @@ const DATA_FORMATS = { */ module.exports = function (context) { return transformData(context) - .then(data => forwardData(data, context)) + .then((data) => forwardData(data, context)) .catch((err) => { context.logger.exception('Splunk data processing error', err); }); @@ -72,7 +71,7 @@ function safeDataTransform(cb, ctx) { .then((data) => { if (data) { if (Array.isArray(data)) { - data.forEach(part => appendData(ctx, part)); + data.forEach((part) => appendData(ctx, part)); } else { appendData(ctx, data); } @@ -104,7 +103,7 @@ function multiMetricDataFormat(ctx) { return Promise.resolve() .then(() => { const events = []; - memConverter(ctx.event.data, event => events.push(JSON.stringify(event))); + memConverter(ctx.event.data, (event) => events.push(JSON.stringify(event))); return events; }); } @@ -147,7 +146,7 @@ function transformData(globalCtx) { let p = null; if (globalCtx.event.type === EVENT_TYPES.SYSTEM_POLLER && !globalCtx.event.isCustom) { requestCtx.cache.dataTimestamp = Date.parse(globalCtx.event.data.system.systemTimestamp); - p = Promise.all(dataMapping.stats.map(func => safeDataTransform(func, requestCtx))) + p = Promise.all(dataMapping.stats.map((func) => safeDataTransform(func, requestCtx))) .then(() => safeDataTransform(dataMapping.overall, requestCtx)); } else if (globalCtx.event.type === EVENT_TYPES.IHEALTH_POLLER) { p = safeDataTransform(dataMapping.ihealth, requestCtx); diff --git a/src/lib/consumers/Splunk/multiMetricEventConverter.js b/src/lib/consumers/Splunk/multiMetricEventConverter.js index a36db954..db55de85 100644 --- a/src/lib/consumers/Splunk/multiMetricEventConverter.js +++ b/src/lib/consumers/Splunk/multiMetricEventConverter.js @@ -242,7 +242,7 @@ const DEFAULT_OPTS = { castCb: DEFAULT_CAST_CB, // ignore References to sub collection ignoreSubCollectionCb: (key, value) => key.endsWith('Reference') && value.link, - skipCb: key => DEFAULT_PROPS_TO_SKIP.indexOf(key) !== -1 + skipCb: (key) => DEFAULT_PROPS_TO_SKIP.indexOf(key) !== -1 }; // options to process keys like - aWideIps, aaaaWideIps and etc. @@ -326,8 +326,8 @@ const PROPERTIES_TO_HANDLERS = { } return DEFAULT_CAST_CB(key, value); }, - deleteCb: key => key === 'Capacity_Float', - renameCb: key => (key === 'Capacity' ? 'Capacity%' : key) + deleteCb: (key) => key === 'Capacity_Float', + renameCb: (key) => (key === 'Capacity' ? 'Capacity%' : key) }, tmmTraffic: { handler: processObject @@ -345,7 +345,7 @@ const PROPERTIES_TO_HANDLERS = { } return DEFAULT_CAST_CB(key, value); }, - deleteCb: key => [ + deleteCb: (key) => [ 'diskLatency', 'diskStorage', 'networkInterfaces', diff --git a/src/lib/consumers/Statsd/index.js b/src/lib/consumers/Statsd/index.js index 1746da66..38958f3f 100644 --- a/src/lib/consumers/Statsd/index.js +++ b/src/lib/consumers/Statsd/index.js @@ -33,6 +33,7 @@ module.exports = function (context) { const protocol = context.config.protocol; const useTcp = protocol === 'tcp'; const collectTags = context.config.addTags && context.config.addTags.method === 'sibling'; + const boolsToMetrics = context.config.convertBooleansToMetrics || false; const tcpCheck = new Promise((resolve, reject) => { if (useTcp) { @@ -74,6 +75,7 @@ module.exports = function (context) { metricUtils.findMetricsAndTags(data, { collectTags, parseMetrics: true, + boolsToMetrics, onMetric: (metricPath, metricValue, metricTags) => { const metricName = `${metricPrefix}.${makePath(metricPath)}`; if (context.tracer) { @@ -95,7 +97,7 @@ module.exports = function (context) { client.close(); context.logger.debug('success'); }) - .catch(err => context.logger.exception('Unable to forward to statsd client', err)); + .catch((err) => context.logger.exception('Unable to forward to statsd client', err)); }; /** @@ -104,5 +106,5 @@ module.exports = function (context) { * @returns {string} path to statsd metric */ function makePath(paths) { - return paths.map(i => i.replace(/\.|\/|:/g, '-')).join('.'); + return paths.map((i) => i.replace(/\.|\/|:/g, '-')).join('.'); } diff --git a/src/lib/consumers/shared/awsUtil.js b/src/lib/consumers/shared/awsUtil.js index 04ef135f..645112c4 100644 --- a/src/lib/consumers/shared/awsUtil.js +++ b/src/lib/consumers/shared/awsUtil.js @@ -79,7 +79,11 @@ function sendLogs(context) { let params; return Promise.resolve() .then(() => { - cloudWatchLogs = new AWS.CloudWatchLogs({ apiVersion: '2014-03-28' }); + const clientProperties = { apiVersion: '2014-03-28' }; + if (context.config.endpointUrl) { + clientProperties.endpoint = new AWS.Endpoint(context.config.endpointUrl); + } + cloudWatchLogs = new AWS.CloudWatchLogs(clientProperties); const logGroup = context.config.logGroup; const logStream = context.config.logStream; const epochDate = new Date().getTime(); @@ -141,16 +145,16 @@ function getDefaultDimensions(data) { { Name: 'baseMac', Value: sysData.baseMac } ]; } - return dimensions.filter(d => d.Value); + return dimensions.filter((d) => d.Value); } function buildMetric(key, value, dimensions) { let dims; // if metric is on pool level, remove redundant dimension poolName - const nameDim = dimensions.find(d => d.Name === 'name'); - const poolNameDim = dimensions.find(d => d.Name === 'poolName'); + const nameDim = dimensions.find((d) => d.Name === 'name'); + const poolNameDim = dimensions.find((d) => d.Name === 'poolName'); if (poolNameDim && (nameDim.Value === poolNameDim.Value)) { - dims = dimensions.filter(d => d.Name !== 'poolName'); + dims = dimensions.filter((d) => d.Name !== 'poolName'); } return { // Metric Name must be max 255 and ASCII chars @@ -203,7 +207,7 @@ function getMetrics(data, dimensions, key, metrics) { if (typeof data === 'object') { let newKey; let newDims = util.deepCopy(dimensions); - let nameDim = newDims.find(d => d.Name === 'name'); + let nameDim = newDims.find((d) => d.Name === 'name'); if (!nameDim) { newDims.push({ Name: 'name' }); nameDim = newDims[newDims.length - 1]; @@ -248,7 +252,7 @@ function getMetrics(data, dimensions, key, metrics) { nameDim.Value = data[dataKey].name; newKey = key || dataKey; if (key === 'pools') { - const poolDim = newDims.find(d => d.Name === 'poolName'); + const poolDim = newDims.find((d) => d.Name === 'poolName'); if (poolDim) { poolDim.Value = data[dataKey].name; } else { @@ -265,7 +269,7 @@ function getMetrics(data, dimensions, key, metrics) { // } newKey = key ? `${key}_${dataKey}` : dataKey; } - newDims = newDims.filter(d => d.Value); + newDims = newDims.filter((d) => d.Value); getMetrics(data[dataKey], newDims, newKey, metrics); }); } @@ -290,7 +294,11 @@ function sendMetrics(context, metrics) { return Promise.resolve() .then(() => { - const cloudWatchMetrics = new AWS.CloudWatch({ apiVersion: '2010-08-01' }); + const clientProperties = { apiVersion: '2010-08-01' }; + if (context.config.endpointUrl) { + clientProperties.endpoint = new AWS.Endpoint(context.config.endpointUrl); + } + const cloudWatchMetrics = new AWS.CloudWatch(clientProperties); const putPromises = []; for (let i = 0; i < metrics.length; i += METRICS_BATCH_SIZE) { const metricsBatch = metrics.slice(i, i + METRICS_BATCH_SIZE); diff --git a/src/lib/consumers/shared/azureUtil.js b/src/lib/consumers/shared/azureUtil.js index 982537ad..35e70fd3 100644 --- a/src/lib/consumers/shared/azureUtil.js +++ b/src/lib/consumers/shared/azureUtil.js @@ -100,7 +100,7 @@ function getAccessTokenFromMetadata(context, mgmtUrl) { allowSelfSignedCert: context.config.allowSelfSignedCert }; return requestsUtil.makeRequest(accessTokenOpts) - .then(resp => resp.access_token) + .then((resp) => resp.access_token) .catch((err) => { context.logger.error(`Unable to generate access token. Error: ${err.message}`); return Promise.reject(err); @@ -118,13 +118,12 @@ function listSubscriptions(accessToken, url) { return requestsUtil.makeRequest(listSubOpts); } - function listWorkspaces(context, accessToken) { const mgmtUrl = getApiUrl(context, AZURE_API_TYPES.MGMT); return listSubscriptions(accessToken, mgmtUrl) .then((resp) => { - const listWorkspaceBySubOpts = resp.value.map(v => ({ + const listWorkspaceBySubOpts = resp.value.map((v) => ({ fullURI: `${mgmtUrl}/subscriptions/${v.subscriptionId}/providers/Microsoft.OperationalInsights/workspaces?api-version=2015-11-01-preview`, headers: { Authorization: `Bearer ${accessToken}` @@ -133,8 +132,8 @@ function listWorkspaces(context, accessToken) { })); const workspacePromises = listWorkspaceBySubOpts - .map(o => requestsUtil.makeRequest(o) - .then(items => items.value) + .map((o) => requestsUtil.makeRequest(o) + .then((items) => items.value) .catch((e) => { context.logger.exception('Error when listing workspaces', e); // don't reject right away when one of the subscription list action failed for some reason @@ -144,7 +143,7 @@ function listWorkspaces(context, accessToken) { return Promise.all(workspacePromises) .then((results) => { const values = Array.prototype.concat.apply([], results); - const workspaces = values.filter(r => r.properties && r.properties.customerId); + const workspaces = values.filter((r) => r.properties && r.properties.customerId); if (workspaces.length === 0) { return Promise.reject(new Error('Unable to list workspaces for subscription(s)')); } @@ -157,7 +156,7 @@ function getWorkspaceResourceId(context, accessToken) { const workspaceGuid = context.config.workspaceId; return listWorkspaces(context, accessToken) .then((resp) => { - const matched = resp.filter(v => v.properties.customerId === workspaceGuid); + const matched = resp.filter((v) => v.properties.customerId === workspaceGuid); if (!matched) { return Promise.reject(new Error(`Unable to find matching workspace with id ${workspaceGuid}`)); } @@ -193,7 +192,7 @@ function getSharedKey(context) { allowSelfSignedCert: context.config.allowSelfSignedCert }; return requestsUtil.makeRequest(sharedKeysOpts) - .then(response => response.primarySharedKey) + .then((response) => response.primarySharedKey) .catch((err) => { context.logger.error(`Unable to get sharedKey. err: ${err.message}`); return Promise.reject(err); @@ -264,7 +263,7 @@ function getInstrumentationKeys(context) { const region = getInstanceRegion(context); let keys; if (Array.isArray(context.config.instrumentationKey)) { - keys = context.config.instrumentationKey.map(iKey => ({ + keys = context.config.instrumentationKey.map((iKey) => ({ instrKey: iKey, connString: buildAppInsConnString(iKey, region) })); @@ -288,7 +287,7 @@ function getInstrumentationKeys(context) { return listSubscriptions(accessToken, mgmtUrl); }) .then((resp) => { - const listAppInsightsBySubOpts = resp.value.map(v => ({ + const listAppInsightsBySubOpts = resp.value.map((v) => ({ fullURI: `${mgmtUrl}/subscriptions/${v.subscriptionId}/providers/Microsoft.Insights/components?api-version=2015-05-01`, headers: { Authorization: `Bearer ${accessToken}` @@ -297,8 +296,8 @@ function getInstrumentationKeys(context) { })); const aiResourcesPromises = listAppInsightsBySubOpts - .map(o => requestsUtil.makeRequest(o) - .then(items => items.value) + .map((o) => requestsUtil.makeRequest(o) + .then((items) => items.value) .catch((e) => { context.logger.error(`Error when listing Application Insights resources: ${e.stack}`); // don't reject right away when one of the subscription list action failed for some reason @@ -309,12 +308,12 @@ function getInstrumentationKeys(context) { }) .then((results) => { const values = Array.prototype.concat.apply([], results); - const aiResources = values.filter(r => r.properties && r.properties.InstrumentationKey + const aiResources = values.filter((r) => r.properties && r.properties.InstrumentationKey && (aiNamePattern ? r.name.match(aiNamePattern) : true)); if (aiResources.length === 0) { return Promise.reject(new Error(`Unable to find Application Insights resources for subscription(s). Name filter: ${aiNamePattern || 'none'}`)); } - const instrKeys = aiResources.map(a => ( + const instrKeys = aiResources.map((a) => ( { name: a.name, instrKey: a.properties.InstrumentationKey, connString: a.properties.ConnectionString } )); return instrKeys; @@ -333,10 +332,10 @@ function getInstrumentationKeys(context) { */ function isConfigItems(data, type) { // is it of type sslCerts or keys are of format of format /.../... - if (type === 'sslCerts' || Object.keys(data).every(key => /\/[^/]*\/.*/.test(key))) { + if (type === 'sslCerts' || Object.keys(data).every((key) => /\/[^/]*\/.*/.test(key))) { // check that the key is the same as property 'name' return Object.keys(data) - .every(key => typeof data[key] === 'object' && key === data[key].name); + .every((key) => typeof data[key] === 'object' && key === data[key].name); } return false; } @@ -350,7 +349,7 @@ function isConfigItems(data, type) { * @returns {Array} values of data */ function transformConfigItems(data) { - return Object.keys(data).map(key => data[key]); + return Object.keys(data).map((key) => data[key]); } /** diff --git a/src/lib/consumers/shared/gcpUtil.js b/src/lib/consumers/shared/gcpUtil.js index e5f094f7..8a2aa87d 100644 --- a/src/lib/consumers/shared/gcpUtil.js +++ b/src/lib/consumers/shared/gcpUtil.js @@ -12,6 +12,9 @@ const jwt = require('jsonwebtoken'); const requestsUtil = require('../../utils/requests'); const getCurrentUnixTimeInSeconds = require('../../utils/datetime').getCurrentUnixTimeInSeconds; +// Google's metadata service +const METADATA_URL = 'http://metadata.google.internal/computeMetadata'; + /** * Caching mechanism for Access Tokens * Instead of requesting a new token on each request, can cache the token until it is set to expire. @@ -101,78 +104,107 @@ class TokenCache { const tokenCache = new TokenCache(); +/** + * Sets tokenId for tokenCache to privateKeyId or serviceEmail, depending on whether useServiceAccountToken is used. + * + * @param {Object} serviceAccount Google Cloud Service Account properties + * @param {String} serviceAccount.serviceEmail Service Account email address + * @param {String} serviceAccount.privateKey Service Account private key + * @param {Boolean} serviceAccount.useServiceAccountToken sets if instance metadata token should be used + * + * @returns {String} tokenId +*/ +function getTokenId(serviceAccount) { + if (!serviceAccount.useServiceAccountToken) { + return serviceAccount.privateKeyId; + } + return serviceAccount.serviceEmail; +} + /** * Given Google Cloud Access credentials, requests an Access Token from Google Cloud. * Will request following scopes when requesting an Access Token: * - https://www.googleapis.com/auth/monitoring * - https://www.googleapis.com/auth/logging.write * - * @param {Object} serviceAccount Google Cloud Service Account properties - * @param {String} serviceAccount.serviceEmail Service Account email address - * @param {String} serviceAccount.privateKeyId Service Account private key ID - * @param {String} serviceAccount.privateKey Service Account private key + * @param {Object} serviceAccount Google Cloud Service Account properties + * @param {String} serviceAccount.serviceEmail Service Account email address + * @param {String} serviceAccount.privateKeyId Service Account private key ID + * @param {String} serviceAccount.privateKey Service Account private key + * @param {Boolean} serviceAccount.useServiceAccountToken sets if instance metadata token should be used * * @returns {String} Access Token used to authenticate to Google Cloud APIs */ function getAccessToken(serviceAccount) { - const privateKeyId = serviceAccount.privateKeyId; - const cachedToken = tokenCache.getToken(privateKeyId); + const tokenId = getTokenId(serviceAccount); + const cachedToken = tokenCache.getToken(tokenId); if (cachedToken && cachedToken.access_token) { return Promise.resolve(cachedToken.access_token); } - const scope = 'https://www.googleapis.com/auth/monitoring https://www.googleapis.com/auth/logging.write'; - const jwtAge = 60 * 60; // 1 hour, in seconds - const jwtSigningOptions = { - algorithm: 'RS256', - header: { - kid: privateKeyId, - typ: 'JWT', - alg: 'RS256' - } - }; - - const jwtRequest = jwt.sign( - { - iss: serviceAccount.serviceEmail, - scope, - aud: 'https://oauth2.googleapis.com/token', - exp: getCurrentUnixTimeInSeconds() + jwtAge, - iat: getCurrentUnixTimeInSeconds() - }, - serviceAccount.privateKey, - jwtSigningOptions - ); - - const httpOptions = { - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - }, - fullURI: 'https://oauth2.googleapis.com/token', - form: { - grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', - assertion: jwtRequest - } - }; + let httpOptions = {}; + if (serviceAccount.useServiceAccountToken) { + httpOptions = { + headers: { + 'Metadata-Flavor': 'Google' + }, + method: 'GET', + fullURI: `${METADATA_URL}/v1/instance/service-accounts/${serviceAccount.serviceEmail}/token` + }; + } else { + const scope = 'https://www.googleapis.com/auth/monitoring https://www.googleapis.com/auth/logging.write'; + const jwtAge = 60 * 60; // 1 hour, in seconds + const jwtSigningOptions = { + algorithm: 'RS256', + header: { + kid: serviceAccount.privateKeyId, + typ: 'JWT', + alg: 'RS256' + } + }; + + const jwtRequest = jwt.sign( + { + iss: serviceAccount.serviceEmail, + scope, + aud: 'https://oauth2.googleapis.com/token', + exp: getCurrentUnixTimeInSeconds() + jwtAge, + iat: getCurrentUnixTimeInSeconds() + }, + serviceAccount.privateKey, + jwtSigningOptions + ); + + httpOptions = { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + }, + fullURI: 'https://oauth2.googleapis.com/token', + form: { + grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', + assertion: jwtRequest + } + }; + } return requestsUtil.makeRequest(httpOptions) - .then(token => tokenCache.cacheToken(privateKeyId, token).access_token); + .then((token) => tokenCache.cacheToken(tokenId, token).access_token); } /** * Given a Google Service Account object, invalidates the cached tokens held by the Token Cache. * - * @param {Object} serviceAccount Service Account object - * @param {String} serviceAccount.privateKeyId PrivateKeyID for the Service Account + * @param {Object} serviceAccount Service Account object + * @param {String} serviceAccount.serviceEmail Service Account email address + * @param {String} serviceAccount.privateKeyId PrivateKeyID for the Service Account + * @param {Boolean} serviceAccount.useServiceAccountToken sets if instance metadata token should be used */ function invalidateToken(serviceAccount) { - tokenCache.removeToken(serviceAccount.privateKeyId); + const tokenId = getTokenId(serviceAccount); + tokenCache.removeToken(tokenId); } -// Google's metadata service -const METADATA_URL = 'http://metadata.google.internal/computeMetadata'; - function getInstanceMetadata(context) { const metadataOpts = { fullURI: `${METADATA_URL}/v1/instance/?recursive=true`, diff --git a/src/lib/consumers/shared/metricsUtil.js b/src/lib/consumers/shared/metricsUtil.js index b7f918cf..49ca1b30 100644 --- a/src/lib/consumers/shared/metricsUtil.js +++ b/src/lib/consumers/shared/metricsUtil.js @@ -49,6 +49,8 @@ module.exports = { * example: e.g. string is 'disk space left 2500Kb only' - it will fetch '2500' and * if next time string will be 'disk space left 4.5mb' - it will fetch '4.5' that * actually is bigger than 2500 (mb > kb) but numbers without units gives us misleading info + * - 'boolsToMetrics' setting will convert boolean values to metrics. true -> 1 and false -> 0 + * - booleans will be treated as metrics, and will ignore 'tagsToIgnore' and 'tagsToMetrics' * * @param {object} tsData - TS data to inspect * @param {object} [options] - options @@ -63,6 +65,7 @@ module.exports = { * @param {boolean} [options.parseMetrics = false] - parse and re-assign metrics * @param {Array} [options.tagsToIgnore] - tags to ignore * @param {Array} [options.tagsToMetrics] - tags to metrics + * @param {boolean} [options.boolsToMetrics = false] - whether to convert boolean values to metrics * * @returns {void} once done with inspecting a data */ @@ -96,6 +99,12 @@ module.exports = { isCollectionOfObjects = false; if (metricsToTags.indexOf(itemKey) === -1) { let parsedVal = itemData; + if (typeof parsedVal === 'boolean' + && options.boolsToMetrics + && metricsToIgnore.indexOf(itemKey) === -1) { + data[itemKey] = parsedVal ? 1 : 0; + return; // early return, within "if" to stop evaluating 'parsedVal' + } if (typeof parsedVal === 'string') { // still have to parse data despite on "options.parseMetrics" value // to differentiate between "metric" and "non-metric" data diff --git a/src/lib/customKeywords.js b/src/lib/customKeywords.js index b1569420..4a45d780 100644 --- a/src/lib/customKeywords.js +++ b/src/lib/customKeywords.js @@ -57,8 +57,8 @@ function resolvePointer(origin, pointer, srcPointer, options) { }; let ret = ''; - const pointers = pointer.split('/').filter(i => (i !== '' && i !== '@')); // strip '' or leading '@' - const sourcePointers = srcPointer.split('/').filter(i => (i !== '')); // strip '' + const pointers = pointer.split('/').filter((i) => (i !== '' && i !== '@')); // strip '' or leading '@' + const sourcePointers = srcPointer.split('/').filter((i) => (i !== '')); // strip '' // first character in path indicates "relativeness" const fC = pointer[0]; @@ -242,7 +242,7 @@ function declarationClassPropCheck(schemaCtx, dataCtx) { const options = schemaCtx.schema; // remove leading and trailing '/' - const trimPath = val => val.substring( + const trimPath = (val) => val.substring( val.startsWith('/') ? 1 : 0, val.endsWith('/') ? (val.length - 1) : val.length ); @@ -273,7 +273,7 @@ function declarationClassPropCheck(schemaCtx, dataCtx) { const pathParts = schemaParts.slice(1).concat(dataParts.slice(1)); /* eslint-disable no-return-assign */ - if (!pathParts.every(key => typeof (objInstance = objInstance[key]) !== 'undefined')) { + if (!pathParts.every((key) => typeof (objInstance = objInstance[key]) !== 'undefined')) { const resolvedPath = `${classInstanceName}/${pathParts.join('/')}`; throw new Error(`Unable to find "${resolvedPath}"`); } @@ -445,7 +445,6 @@ function nodeSupportVersionCheck(schemaObj) { return true; } - /** * Expand JSON pointer * @@ -538,7 +537,7 @@ function runValidationFunction(func, schemaCtx, dataCtx) { */ function wrapAsyncValidationFunction(func, schemaCtx, dataCtx) { return () => func() - .catch(err => Promise.reject(getValidationError(err, schemaCtx, dataCtx))); + .catch((err) => Promise.reject(getValidationError(err, schemaCtx, dataCtx))); } /** diff --git a/src/lib/dataFilter.js b/src/lib/dataFilter.js index cd5b1c93..5c1e27d4 100644 --- a/src/lib/dataFilter.js +++ b/src/lib/dataFilter.js @@ -84,7 +84,6 @@ function applyExcludeList(data) { }); } - module.exports = { DataFilter }; diff --git a/src/lib/dataPipeline.js b/src/lib/dataPipeline.js index 97313082..8c0d094a 100644 --- a/src/lib/dataPipeline.js +++ b/src/lib/dataPipeline.js @@ -52,12 +52,11 @@ function buildSkippedDataLog(dataCtx) { }); const consumers = consumersHandler.getConsumers() - .filter(c => dataCtx.destinationIds.indexOf(c.id) > -1) - .map(c => c.name); + .filter((c) => dataCtx.destinationIds.indexOf(c.id) > -1) + .map((c) => c.name); return `Skipped Data - Category: "${dataCtx.data.telemetryEventCategory}" | Consumers: ${JSON.stringify(consumers)} | Addtl Info: ${timestampInfo}`; } - /** * Pipeline to process data * @@ -101,7 +100,7 @@ function process(dataCtx, options) { promise = promise.then(() => { // detach forwarding process from here forwarder.forward(dataCtx) - .catch(err => logger.exception('Error on attempt to forward data to consumers', err)); + .catch((err) => logger.exception('Error on attempt to forward data to consumers', err)); }); } } @@ -112,7 +111,7 @@ function process(dataCtx, options) { }); } -monitor.on('check', status => new Promise((resolve) => { +monitor.on('check', (status) => new Promise((resolve) => { const monitorChecksOk = status === APP_THRESHOLDS.MEMORY.OK; // only log on status change to minimize entries if (processingEnabled !== monitorChecksOk) { diff --git a/src/lib/dataTagging.js b/src/lib/dataTagging.js index a5ae77aa..7a6c6d4c 100644 --- a/src/lib/dataTagging.js +++ b/src/lib/dataTagging.js @@ -53,7 +53,7 @@ function addTags(dataCtx, actionCtx, deviceCtx) { if (typeof items === 'object' && !util.isObjectEmpty(items) && statProp.normalization - && statProp.normalization.find(norm => norm.addKeysByTag)) { + && statProp.normalization.find((norm) => norm.addKeysByTag)) { Object.keys(items).forEach((itemKey) => { Object.keys(tags).forEach((tagKey) => { addTag(items[itemKey], tagKey, tags[tagKey], itemKey, statProp); @@ -181,7 +181,6 @@ function addTag(data, tagKey, tagVal, location, statProp) { } } - module.exports = { addTags }; diff --git a/src/lib/declarationValidator.js b/src/lib/declarationValidator.js index 93be637c..70ba0684 100644 --- a/src/lib/declarationValidator.js +++ b/src/lib/declarationValidator.js @@ -28,7 +28,6 @@ const sharedSchema = require('../schema/latest/shared_schema.json'); const systemPollerSchema = require('../schema/latest/system_poller_schema.json'); const systemSchema = require('../schema/latest/system_schema.json'); - /** * Process errors * @@ -140,7 +139,7 @@ module.exports = { errors = []; const deferred = context.deferred; - const safeWrapper = promiseFn => promiseFn().catch(e => errors.push(e)); + const safeWrapper = (promiseFn) => promiseFn().catch((e) => errors.push(e)); const processDeferred = (idx) => { if (idx >= customKeywords.asyncOrder.length) { diff --git a/src/lib/endpointLoader.js b/src/lib/endpointLoader.js index 04865427..69ff50b3 100644 --- a/src/lib/endpointLoader.js +++ b/src/lib/endpointLoader.js @@ -332,7 +332,7 @@ EndpointLoader.prototype.getAndExpandData = function (endpointObj) { return this.getData(endpointObj.path, endpointObj) // Promise below will be resolved with array of 2 elements: // [ baseData, [refData, refData] ] - .then(baseData => Promise.all([ + .then((baseData) => Promise.all([ Promise.resolve(baseData), this.expandReferences(endpointObj, baseData) ])) diff --git a/src/lib/eventListener/baseDataReceiver.js b/src/lib/eventListener/baseDataReceiver.js index 7ee6fac4..b0e6fafa 100644 --- a/src/lib/eventListener/baseDataReceiver.js +++ b/src/lib/eventListener/baseDataReceiver.js @@ -17,7 +17,6 @@ const SafeEventEmitter = require('../utils/eventEmitter').SafeEventEmitter; class BaseDataReceiverError extends errors.BaseError {} class StateTransitionError extends BaseDataReceiverError {} - /** * Base class for Data Receivers (base on EventEmitter2) * @@ -200,9 +199,9 @@ class BaseDataReceiver extends SafeEventEmitter { return this._setState(this.constructor.STATE.RESTARTING) .then(() => new Promise((resolve, reject) => { const inner = () => this.stop() - .catch(stopError => this.logger.exception('caught error on attempt to stop during restart', stopError)) + .catch((stopError) => this.logger.exception('caught error on attempt to stop during restart', stopError)) .then(() => this.start()) - .catch(restartErr => this._setState(this.constructor.STATE.FAILED_TO_RESTART) + .catch((restartErr) => this._setState(this.constructor.STATE.FAILED_TO_RESTART) .then(() => { if ((attempts === true || options.attempts > 1) && this.isRestartAllowed()) { this.logger.exception('re-trying to restart due error', restartErr); diff --git a/src/lib/eventListener/dataPublisher.js b/src/lib/eventListener/dataPublisher.js index 2c87907d..145423a6 100644 --- a/src/lib/eventListener/dataPublisher.js +++ b/src/lib/eventListener/dataPublisher.js @@ -33,7 +33,7 @@ function sendDataToListener(data, listenerName, options) { return Promise.resolve() .then(() => { const eventListener = configUtil.getTelemetryListeners(configWorker.currentConfig, opts.namespace) - .find(el => el.name === listenerName); + .find((el) => el.name === listenerName); let error; const namespaceSuffix = `${opts.namespace ? ` in Namespace: ${opts.namespace}` : ''}.`; @@ -81,7 +81,7 @@ function sendDataToListener(data, listenerName, options) { }); }); }) - .catch(error => Promise.reject(error)); + .catch((error) => Promise.reject(error)); } module.exports = { diff --git a/src/lib/eventListener/index.js b/src/lib/eventListener/index.js index 847c33c7..12737d5e 100644 --- a/src/lib/eventListener/index.js +++ b/src/lib/eventListener/index.js @@ -22,7 +22,6 @@ const tracers = require('../utils/tracer'); /** @module EventListener */ - const normalizationOpts = { global: properties.global, events: properties.events, @@ -72,7 +71,7 @@ class ReceiversManager { * @returns {Promise} resolved once all receivers destroyed */ destroyAll() { - return promiseUtil.allSettled(this.getAll().map(receiver => receiver.destroy())) + return promiseUtil.allSettled(this.getAll().map((receiver) => receiver.destroy())) .then((ret) => { this.registered = {}; return ret; @@ -85,7 +84,7 @@ class ReceiversManager { * @returns {Array} registered receivers */ getAll() { - return Object.keys(this.registered).map(key => this.registered[key]); + return Object.keys(this.registered).map((key) => this.registered[key]); } /** @@ -121,8 +120,8 @@ class ReceiversManager { } }); return promiseUtil.allSettled( - receivers.map(r => r.restart({ attempts: 10 }) // without delay for now (REST API is sync) - .catch(err => r.stop() // stop to avoid resources leaking + receivers.map((r) => r.restart({ attempts: 10 }) // without delay for now (REST API is sync) + .catch((err) => r.stop() // stop to avoid resources leaking .then(() => Promise.reject(err)))) ) .then(promiseUtil.getValues); @@ -144,8 +143,8 @@ class ReceiversManager { } } }); - return promiseUtil.allSettled(receivers.map(r => r.destroy() - .catch(destroyErr => r.logger.exception('unable to stop and destroy receiver', destroyErr)))); + return promiseUtil.allSettled(receivers.map((r) => r.destroy() + .catch((destroyErr) => r.logger.exception('unable to stop and destroy receiver', destroyErr)))); } } @@ -238,7 +237,7 @@ class EventListener { destinationIds: this.destinationIds }; const p = dataPipeline.process(dataCtx, { tracer: this.tracer, actions: this.actions }) - .catch(err => this.logger.exception('EventListener:_processEvents unexpected error from dataPipeline:process', err)); + .catch((err) => this.logger.exception('EventListener:_processEvents unexpected error from dataPipeline:process', err)); promises.push(p); } }); @@ -339,7 +338,7 @@ EventListener.getByName = function (name) { * @returns {Array} current listeners */ EventListener.getAll = function () { - return Object.keys(EventListener.instances).map(key => EventListener.instances[key]); + return Object.keys(EventListener.instances).map((key) => EventListener.instances[key]); }; /** @@ -361,7 +360,7 @@ configWorker.on('change', (config) => { // stop all removed listeners EventListener.getAll().forEach((listener) => { - const configMatch = configuredListeners.find(n => n.traceName === listener.name); + const configMatch = configuredListeners.find((n) => n.traceName === listener.name); if (!configMatch) { logger.debug(`Removing event listener - ${listener.name} [port = ${listener.messageStream.port}]. Reason - removed from configuration.`); EventListener.remove(listener); @@ -390,7 +389,7 @@ configWorker.on('change', (config) => { const listener = EventListener.get(name, port); listener.updateConfig({ actions: listenerConfig.actions, - destinationIds: configUtil.getReceivers(config, listenerConfig).map(r => r.id), + destinationIds: configUtil.getReceivers(config, listenerConfig).map((r) => r.id), filterFunc: buildFilterFunc(listenerConfig), id: listenerConfig.id, tags: listenerConfig.tag, @@ -403,7 +402,7 @@ configWorker.on('change', (config) => { return EventListener.receiversManager.stopAndRemoveInactive() .then(() => EventListener.receiversManager.start()) .then(() => logger.debug(`${EventListener.getAll().length} event listener(s) listening`)) - .catch(err => logger.exception('Unable to start some (or all) of the event listeners', err)); + .catch((err) => logger.exception('Unable to start some (or all) of the event listeners', err)); }); function sendShutdownEvent() { diff --git a/src/lib/eventListener/messageStream.js b/src/lib/eventListener/messageStream.js index a4cf906e..30a32556 100644 --- a/src/lib/eventListener/messageStream.js +++ b/src/lib/eventListener/messageStream.js @@ -1,4 +1,3 @@ - /* * Copyright 2021. F5 Networks, Inc. See End User License Agreement ("EULA") for * license terms. Notwithstanding anything to the contrary in the EULA, Licensee @@ -100,7 +99,7 @@ class MessageStream extends baseDataReceiver.BaseDataReceiver { } if (this._dataFwdCallbacks) { - this._dataFwdCallbacks.forEach(config => config.receiver.removeListener('data', config.callback)); + this._dataFwdCallbacks.forEach((config) => config.receiver.removeListener('data', config.callback)); this._dataFwdCallbacks = null; } } @@ -153,7 +152,7 @@ class MessageStream extends baseDataReceiver.BaseDataReceiver { return Promise.reject(this.getStateTransitionError(this.constructor.STATE.STARTING)); } this.createReceivers(); - return promiseUtil.allSettled(this._receivers.map(receiver => receiver.receiver.start())) + return promiseUtil.allSettled(this._receivers.map((receiver) => receiver.receiver.start())) .then(promiseUtil.getValues); } @@ -167,7 +166,7 @@ class MessageStream extends baseDataReceiver.BaseDataReceiver { if (!this.hasReceivers()) { return Promise.resolve(); } - return promiseUtil.allSettled(this._receivers.map(receiver => receiver.receiver.destroy())) + return promiseUtil.allSettled(this._receivers.map((receiver) => receiver.receiver.destroy())) .then((statuses) => { if (this._dataBuffers) { Object.keys(this._dataBuffers).forEach((key) => { diff --git a/src/lib/eventListener/tcpUdpDataReceiver.js b/src/lib/eventListener/tcpUdpDataReceiver.js index 045b5966..b7481c66 100644 --- a/src/lib/eventListener/tcpUdpDataReceiver.js +++ b/src/lib/eventListener/tcpUdpDataReceiver.js @@ -119,7 +119,7 @@ class TCPDataReceiver extends TcpUdpBaseDataReceiver { */ connectionHandler(conn) { addTcpConnection.call(this, conn); - conn.on('data', data => callDataCallback.call(this, data, conn)) + conn.on('data', (data) => callDataCallback.call(this, data, conn)) .on('error', () => conn.destroy()) // destroy emits 'close' event .on('close', () => removeTcpConnection.call(this, conn)) .on('end', () => {}); // allowHalfOpen is false by default, no need to call 'end' explicitly @@ -308,7 +308,6 @@ class UDPDataReceiver extends TcpUdpBaseDataReceiver { } } - /** * Data Receiver over UDPv4 and UDPv6 * @@ -356,7 +355,7 @@ class DualUDPDataReceiver extends TcpUdpBaseDataReceiver { return Promise.reject(this.getStateTransitionError(this.constructor.STATE.STARTING)); } this.createReceivers(); - return promiseUtil.allSettled(this._receivers.map(receiver => receiver.start())) + return promiseUtil.allSettled(this._receivers.map((receiver) => receiver.start())) .then(promiseUtil.getValues); } @@ -372,7 +371,7 @@ class DualUDPDataReceiver extends TcpUdpBaseDataReceiver { } // stop to listen for 'data' event this.stopListeningTo(); - return promiseUtil.allSettled(this._receivers.map(receiver => receiver.destroy())) + return promiseUtil.allSettled(this._receivers.map((receiver) => receiver.destroy())) .then((statuses) => { this._receivers = null; return promiseUtil.getValues(statuses); @@ -417,7 +416,7 @@ function callDataCallback(data, connInfo) { function closeAllTcpConnections() { this.logger.debug('closing all client connections'); // do .slice in case if ._removeConnection will be called - this._connections.slice(0).forEach(conn => conn.destroy()); + this._connections.slice(0).forEach((conn) => conn.destroy()); this._connections = []; } diff --git a/src/lib/forwarder.js b/src/lib/forwarder.js index 1ce81cf5..209eebd5 100644 --- a/src/lib/forwarder.js +++ b/src/lib/forwarder.js @@ -26,7 +26,7 @@ function forwardData(dataCtx) { if (!Array.isArray(consumers)) { return Promise.resolve(); } - consumers = consumers.filter(c => dataCtx.destinationIds.indexOf(c.id) > -1); + consumers = consumers.filter((c) => dataCtx.destinationIds.indexOf(c.id) > -1); // don't rely on plugins' code, wrap consumer's call to Promise // eslint-disable-next-line return Promise.all(consumers.map((consumer) => { @@ -60,7 +60,6 @@ function forwardData(dataCtx) { })); } - module.exports = { forward: forwardData }; diff --git a/src/lib/ihealth.js b/src/lib/ihealth.js index c9d09a6e..8c69cacd 100644 --- a/src/lib/ihealth.js +++ b/src/lib/ihealth.js @@ -79,7 +79,7 @@ function safeProcess() { */ function createReportCallback(poller, pollerConfig, globalConfig) { const ctx = { - destinationIDs: configUtil.getReceivers(globalConfig, pollerConfig).map(r => r.id), + destinationIDs: configUtil.getReceivers(globalConfig, pollerConfig).map((r) => r.id), pollerID: pollerConfig.id, demoMode: poller.isDemoModeEnabled(), tracer: tracer.fromConfig(pollerConfig.trace) @@ -99,10 +99,10 @@ function getCurrentState(namespaceName) { const ids = configUtil.getTelemetryIHealthPollers( configWorker.currentConfig, namespaceName || constants.DEFAULT_UNNAMED_NAMESPACE ) - .map(pc => pc.traceName); - instances = instances.filter(poller => ids.indexOf(poller.id) !== -1); + .map((pc) => pc.traceName); + instances = instances.filter((poller) => ids.indexOf(poller.id) !== -1); } - return instances.map(poller => poller.info()); + return instances.map((poller) => poller.info()); } /** @@ -120,7 +120,7 @@ function startPoller(systemName, namespaceName) { const pollerConfig = configUtil.getTelemetryIHealthPollers( config, namespaceName || constants.DEFAULT_UNNAMED_NAMESPACE ) - .find(pc => pc.systemName === systemName); + .find((pc) => pc.systemName === systemName); if (util.isObjectEmpty(pollerConfig)) { throw new errors.ObjectNotFoundInConfigError('System or iHealth Poller declaration not found'); @@ -131,7 +131,7 @@ function startPoller(systemName, namespaceName) { message: `iHealth Poller for System "${systemName}"${namespaceName ? ` (namespace "${namespaceName}")` : ''} started` }; let retPromise = Promise.resolve(); - let poller = iHealthPoller.get(pollerConfig.traceName).find(p => p.isDemoModeEnabled()); + let poller = iHealthPoller.get(pollerConfig.traceName).find((p) => p.isDemoModeEnabled()); if (poller) { response.isRunning = true; @@ -147,7 +147,7 @@ function startPoller(systemName, namespaceName) { // check if poller was disabled already to avoid concurrency with 'config.change' event if (!poller.isDisabled()) { iHealthPoller.disable(poller) - .catch(disableError => poller.logger.debugException('Unexpected error on attempt to disable', disableError)); + .catch((disableError) => poller.logger.debugException('Unexpected error on attempt to disable', disableError)); } else { poller.logger.debug('Disabled already!'); } @@ -167,7 +167,7 @@ function startPoller(systemName, namespaceName) { } // config worker change event -configWorker.on('change', config => Promise.resolve() +configWorker.on('change', (config) => Promise.resolve() .then(() => { logger.debug('configWorker change event in iHealthPoller'); // helpful debug const configuredPollers = configUtil.getTelemetryIHealthPollers(config); @@ -180,7 +180,7 @@ configWorker.on('change', config => Promise.resolve() function cleanupInactive() { // - stop all removed pollers - doesn't matter even if namespace only was updated return iHealthPoller.getAll({ includeDemo: true }) - .filter(poller => !configuredPollers.find(conf => conf.traceName === poller.id)) + .filter((poller) => !configuredPollers.find((conf) => conf.traceName === poller.id)) .map((poller) => { logger.debug(`Removing iHealth Poller "${poller.name}". Reason - removed from configuration.`); return iHealthPoller.disable(poller); @@ -242,10 +242,9 @@ configWorker.on('change', config => Promise.resolve() }); }) .then(() => logger.info(`${iHealthPoller.getAll().length} iHealth Poller(s) running`)) - .catch(error => logger.exception('Uncaught exception on attempt to process iHealth Pollers configuration', error)) + .catch((error) => logger.exception('Uncaught exception on attempt to process iHealth Pollers configuration', error)) .then(() => iHealthPoller.cleanupOrphanedStorageData()) - .catch(error => logger.debugException('Uncaught exception on attempt to cleanup orphaned data', error))); - + .catch((error) => logger.debugException('Uncaught exception on attempt to cleanup orphaned data', error))); module.exports = { getCurrentState, diff --git a/src/lib/ihealthPoller.js b/src/lib/ihealthPoller.js index d2623252..2f5a8758 100644 --- a/src/lib/ihealthPoller.js +++ b/src/lib/ihealthPoller.js @@ -212,7 +212,7 @@ const IHealthPollerFSM = machina.Fsm.extend({ if (!this._diedPromise) { this._diedPromise = this.safeEmitter.waitFor('died'); // catch and resolve errors in case of cancellation - this._diedPromise.catch(error => error); + this._diedPromise.catch((error) => error); } // need to emit 'disabling' event to // - interrupt 'sleep' @@ -387,7 +387,7 @@ const IHealthPollerFSM = machina.Fsm.extend({ this.data.currentCycle.succeed = false; this.data.currentCycle.errorMsg = `${error}`; }, { reject: true }) - .catch(innerError => innerError) + .catch((innerError) => innerError) .then((innerError) => { if (this.isDemoModeEnabled() || this.isDisabled()) { this.transition('died', innerError || error); @@ -530,7 +530,7 @@ const IHealthPollerFSM = machina.Fsm.extend({ return; } fsmRun.call(this, () => checkNextExecutionTime.call(this) - .then(allowedToStart => this.handle(allowedToStart ? 'ready' : 'sleep'))); + .then((allowedToStart) => this.handle(allowedToStart ? 'ready' : 'sleep'))); }, ready: 'collectQkview', sleep: function sleep() { @@ -726,7 +726,7 @@ class IHealthPoller extends SafeEventEmitter { return Promise.resolve() .then(() => { const pollerConfig = configUtil.getTelemetryIHealthPollers(configWorker.currentConfig) - .find(ihpConf => ihpConf.traceName === this.id); + .find((ihpConf) => ihpConf.traceName === this.id); if (util.isObjectEmpty(pollerConfig)) { return Promise.reject(new Error(`Configuration for iHealth Poller "${this.name}" (${this.id}) not found!`)); @@ -779,7 +779,7 @@ class IHealthPoller extends SafeEventEmitter { if (!this._startPromise) { this._startPromise = Promise.resolve() .then(() => this._fsm.start()) - .catch(error => error) + .catch((error) => error) .then((error) => { delete this._startPromise; return error ? Promise.reject(error) : Promise.resolve(); @@ -832,7 +832,7 @@ IHealthPoller.cleanupOrphanedStorageData = function cleanupOrphanedStorageData() .then((storageData) => { storageData = storageData || {}; // ignoring 'demo' instances - they are restricted from storing the data - const existingKeys = IHealthPoller.getAll().map(poller => poller.storageKey); + const existingKeys = IHealthPoller.getAll().map((poller) => poller.storageKey); Object.keys(storageData).forEach((skey) => { if (existingKeys.indexOf(skey) === -1) { delete storageData[skey]; @@ -850,7 +850,7 @@ IHealthPoller.cleanupOrphanedStorageData = function cleanupOrphanedStorageData() */ IHealthPoller.create = function create(id, options) { options = options || {}; - let poller = IHealthPoller._instances.find(p => p.id === id); + let poller = IHealthPoller._instances.find((p) => p.id === id); if (poller && poller.isDemoModeEnabled() === !!options.demo) { throw new Error(`iHealthPoller instance with ID "${poller.id}" created already (demo = ${poller.isDemoModeEnabled()})`); @@ -893,7 +893,7 @@ IHealthPoller.disable = function disable(poller) { poller.logger.exception('Unexpected exception on attempt to stop', error); }) .then(() => poller.cleanup()) - .catch(error => poller.logger.exception('Unexpected exception on attempt to stop', error)) + .catch((error) => poller.logger.exception('Unexpected exception on attempt to stop', error)) }; }); }; @@ -904,7 +904,7 @@ IHealthPoller.disable = function disable(poller) { * @returns {Array} iHealth Poller instances (non-demo and/or demo) */ IHealthPoller.get = function get(id) { - return IHealthPoller._instances.filter(p => p.id === id); + return IHealthPoller._instances.filter((p) => p.id === id); }; /** @@ -921,10 +921,10 @@ IHealthPoller.getAll = function getAll(options) { }); let instances = []; if (!options.demoOnly) { - instances = instances.concat(IHealthPoller._instances.filter(p => !p.isDemoModeEnabled())); + instances = instances.concat(IHealthPoller._instances.filter((p) => !p.isDemoModeEnabled())); } if (options.demoOnly || options.includeDemo) { - instances = instances.concat(IHealthPoller._instances.filter(p => p.isDemoModeEnabled())); + instances = instances.concat(IHealthPoller._instances.filter((p) => p.isDemoModeEnabled())); } return instances; }; @@ -973,7 +973,7 @@ function cleanupSensitiveData() { */ function collectQkview() { return createQkviewManager.call(this) - .then(qkviewMgr => qkviewMgr.process()) + .then((qkviewMgr) => qkviewMgr.process()) .then((qkviewFilePath) => { this.data.currentCycle.qkview.qkviewFile = qkviewFilePath; this.data.stats.qkviewsCollected += 1; @@ -1077,7 +1077,6 @@ function fetchQkviewReport() { }); } - /** * Do transition to 'disabled' if needed * @@ -1295,7 +1294,7 @@ function removeQkview() { return Promise.resolve(); } return createQkviewManager.call(this) - .then(qkviewMgr => qkviewMgr.localDevice.removeFile(qkviewPath)); + .then((qkviewMgr) => qkviewMgr.localDevice.removeFile(qkviewPath)); } /** @@ -1355,7 +1354,7 @@ function timeUntilNextExecution() { */ function uploadQkview() { return createIHealthManager.call(this) - .then(ihealthMgr => ihealthMgr.uploadQkview(this.data.currentCycle.qkview.qkviewFile)) + .then((ihealthMgr) => ihealthMgr.uploadQkview(this.data.currentCycle.qkview.qkviewFile)) .then((qkviewURI) => { this.data.currentCycle.qkview.qkviewURI = qkviewURI; this.data.stats.qkviewsUploaded += 1; diff --git a/src/lib/normalize.js b/src/lib/normalize.js index ca203db8..30de53ee 100644 --- a/src/lib/normalize.js +++ b/src/lib/normalize.js @@ -12,7 +12,6 @@ const logger = require('./logger'); // eslint-disable-line no-unused-vars const constants = require('./constants'); const normalizeUtil = require('./utils/normalize'); - /** * RegExp for syslog message detection. */ @@ -28,7 +27,7 @@ const SYSLOG_REGEX = new RegExp([ /([\w\-().0-9/]+)/, // process /(?:\[([a-z0-9-.]+)\])?:/, // pid /(.+)/ // message -].map(regex => regex.source).join(''), 'i'); +].map((regex) => regex.source).join(''), 'i'); /* * Lumen might set "$F5TelemetryEventCategory" to "raw", to indicate that the processing is not required @@ -41,7 +40,6 @@ const SYSLOG_MSG_IDX = 11; const USELESS_MESSAGE_PREFIX = /^((?:BigIP|ASM):)/; - module.exports = { /** * Format as JSON - assume data looks similar to this: KEY1="value",KEY2="value" @@ -351,7 +349,6 @@ module.exports = { return thisData; }; - if (data && typeof data === 'object' && !Array.isArray(data)) { // if we are classifying by keys (already defined) assume we are processing a flat // data structure @@ -598,9 +595,9 @@ module.exports = { if ((norm.convertArrayToMap || norm.includeFirstEntry) && !setReduced) { // standard reduce data first const reduceDataOptions = { - convertArrayToMap: (options.normalization.find(n => n.convertArrayToMap) + convertArrayToMap: (options.normalization.find((n) => n.convertArrayToMap) || {}).convertArrayToMap, - includeFirstEntry: (options.normalization.find(n => n.includeFirstEntry) + includeFirstEntry: (options.normalization.find((n) => n.includeFirstEntry) || {}).includeFirstEntry }; ret = this._reduceData(norm.useCurrentData ? ret : data, reduceDataOptions); diff --git a/src/lib/persistentStorage.js b/src/lib/persistentStorage.js index 9c1630df..5099ee31 100644 --- a/src/lib/persistentStorage.js +++ b/src/lib/persistentStorage.js @@ -15,7 +15,6 @@ const unsetData = require('lodash/unset'); const logger = require('./logger'); const util = require('./utils/misc'); - /** @module persistentStorage */ /** @@ -407,7 +406,6 @@ class RestStorage extends StorageInterface { } } - module.exports = { /** * Creating singleton instance that will be configured later in restWorker.js and shared across modules diff --git a/src/lib/properties.json b/src/lib/properties.json index 7bbf64de..77d18d62 100644 --- a/src/lib/properties.json +++ b/src/lib/properties.json @@ -829,7 +829,7 @@ "includeFirstEntry": { "pattern": "/stats", "excludePattern": "/members/" } }, { - "filterKeys": { "exclude": [ "tmName", "availableMemberCnt", "sessionStatus", "connqAll.ageEdm", "connqAll.ageEma", "connqAll.ageHead", "connqAll.ageMax", "connqAll.depth", "connqAll.serviced", "connq.ageEdm", "connq.ageEma", "connq.ageHead", "connq.ageMax", "connq.depth", "connq.serviced", "curSessions", "memberCnt", "minActiveMembers", "monitorRule", "ipTosToServer", "minUpMembersAction", "appService", "appServiceReference", "minUpMembersChecking", "kind","ignorePersistedWeight", "fullPath", "partition", "linkQosToClient", "linkQosToServer", "ipTosToClient", "generation", "serviceDownAction", "queueDepthLimit", "queueTimeLimit", "allowNat", "reselectTries", "minUpMembers", "nodeName", "poolName", "allowSnat", "monitor", "selfLink", "subPath", "queueOnConnectionLimit", "loadBalancingMode", "slowRampTime", "gatewayFailsafeDeviceReference" ] } + "filterKeys": { "exclude": [ "tmName", "availableMemberCnt", "sessionStatus", "connqAll.ageEdm", "connqAll.ageEma", "connqAll.ageHead", "connqAll.ageMax", "connqAll.depth", "connqAll.serviced", "connq.ageEdm", "connq.ageEma", "connq.ageHead", "connq.ageMax", "connq.depth", "connq.serviced", "curSessions", "memberCnt", "minActiveMembers", "monitorRule", "ipTosToServer", "minUpMembersAction", "appService", "appServiceReference", "minUpMembersChecking", "kind","ignorePersistedWeight", "fullPath", "partition", "linkQosToClient", "linkQosToServer", "ipTosToClient", "generation", "serviceDownAction", "queueDepthLimit", "queueTimeLimit", "allowNat", "reselectTries", "minUpMembers", "nodeName", "allowSnat", "monitor", "selfLink", "subPath", "queueOnConnectionLimit", "loadBalancingMode", "slowRampTime", "gatewayFailsafeDeviceReference" ] } }, { "renameKeys": { "patterns": { "name/": { "pattern": "name\/(.*)", "group": 1 }, "ltm/pool/": { "pattern": "pool\/(.*)\\?", "group": 1 } , "members/": { "pattern": "members\/(.*)\/", "group": 1 }, "membersReference": "members" } } diff --git a/src/lib/pullConsumers.js b/src/lib/pullConsumers.js index 1dd223a4..0a0c81c6 100644 --- a/src/lib/pullConsumers.js +++ b/src/lib/pullConsumers.js @@ -43,7 +43,7 @@ function getData(consumerName, namespace) { consumerConfig = getConsumerConfig(config, consumerName, namespace); // Don't bother collecting stats if requested Consumer Type is not loaded - if (!PULL_CONSUMERS.find(pc => pc.config.type === consumerConfig.type)) { + if (!PULL_CONSUMERS.find((pc) => pc.config.type === consumerConfig.type)) { throw new ModuleNotLoadedError(`Pull Consumer of type '${consumerConfig.type}' is not loaded`); } @@ -51,11 +51,11 @@ function getData(consumerName, namespace) { config, consumerConfig ); const pollerConfigs = configUtil.getTelemetrySystemPollersForGroup(config, pollerGroup) - .filter(sp => sp.enable); + .filter((sp) => sp.enable); return systemPoller.fetchPollersData(util.deepCopy(pollerConfigs), true); }) - .then(pollerData => invokeConsumer(consumerConfig, pollerData)); + .then((pollerData) => invokeConsumer(consumerConfig, pollerData)); } /** @@ -69,7 +69,7 @@ function getData(consumerName, namespace) { * @returns {Promise} Promise which is resolved with data formatted by the requested Pull Consumer */ function invokeConsumer(consumerConfig, dataCtxs) { - const consumer = PULL_CONSUMERS.find(pc => pc.id === consumerConfig.id); + const consumer = PULL_CONSUMERS.find((pc) => pc.id === consumerConfig.id); const context = { config: consumerConfig, @@ -84,7 +84,7 @@ function getConsumerConfig(config, consumerName, namespace) { const consumers = configUtil.getTelemetryPullConsumers(config, namespace); const namespaceInfo = namespace ? ` (namespace: ${namespace})` : ''; if (consumers.length > 0) { - const consumer = consumers.find(c => c.name === consumerName); + const consumer = consumers.find((c) => c.name === consumerName); if (util.isObjectEmpty(consumer)) { throw new errors.ObjectNotFoundInConfigError(`Pull Consumer with name '${consumerName}' doesn't exist${namespaceInfo}`); } @@ -119,7 +119,7 @@ function loadConsumers(config) { logger.info('No pull consumer(s) to load, define in configuration first'); return Promise.resolve([]); } - const enabledConsumers = config.filter(c => c.enable); + const enabledConsumers = config.filter((c) => c.enable); if (enabledConsumers.length === 0) { logger.debug('No enabled pull consumer(s) to load'); return Promise.resolve([]); @@ -127,8 +127,8 @@ function loadConsumers(config) { logger.debug(`Loading pull consumer specific plug-ins from ${PULL_CONSUMERS_DIR}`); - const loadPromises = enabledConsumers.map(consumerConfig => new Promise((resolve) => { - const existingConsumer = PULL_CONSUMERS.find(c => c.id === consumerConfig.id); + const loadPromises = enabledConsumers.map((consumerConfig) => new Promise((resolve) => { + const existingConsumer = PULL_CONSUMERS.find((c) => c.id === consumerConfig.id); if (consumerConfig.skipUpdate && existingConsumer) { resolve(existingConsumer); } else { @@ -155,7 +155,7 @@ function loadConsumers(config) { })); return Promise.all(loadPromises) - .then(loadedConsumers => loadedConsumers.filter(c => c !== undefined)); + .then((loadedConsumers) => loadedConsumers.filter((c) => c !== undefined)); } /** @@ -165,12 +165,11 @@ function loadConsumers(config) { */ function getLoadedConsumerTypes() { if (PULL_CONSUMERS.length > 0) { - return new Set(PULL_CONSUMERS.map(consumer => consumer.config.type)); + return new Set(PULL_CONSUMERS.map((consumer) => consumer.config.type)); } return new Set(); } - /** * Unload unused modules from cache * @@ -193,7 +192,7 @@ function unloadUnusedModules(before) { } // config worker change event -configWorker.on('change', config => Promise.resolve() +configWorker.on('change', (config) => Promise.resolve() .then(() => { logger.debug('configWorker change event in Pull Consumers'); diff --git a/src/lib/pullConsumers/Prometheus/index.js b/src/lib/pullConsumers/Prometheus/index.js index b5866892..5dcdc3e1 100644 --- a/src/lib/pullConsumers/Prometheus/index.js +++ b/src/lib/pullConsumers/Prometheus/index.js @@ -133,7 +133,7 @@ module.exports = function (context) { */ function collectMetrics(event) { const filteredEvents = event - .filter(d => (typeof d !== 'undefined' && Object.keys(d).indexOf('data') !== -1)); + .filter((d) => (typeof d !== 'undefined' && Object.keys(d).indexOf('data') !== -1)); const metrics = []; filteredEvents.forEach((eventObj) => { @@ -150,7 +150,6 @@ function collectMetrics(event) { return mergeObjectArray(metrics); } - /** * Prioritizes metrics by the number of transformations that were required to convert a metric name * into a valid Prometheus metric name. @@ -307,7 +306,7 @@ function storeMetric(pathOrKey, metricValue, metrics) { if (typeof metricPointer.labels === 'undefined') { metricPointer.labels = new Set(); } - labelKeys.forEach(l => metricPointer.labels.add(l)); + labelKeys.forEach((l) => metricPointer.labels.add(l)); newMetricValue.labels = labels; } metricPointer.values.push(newMetricValue); diff --git a/src/lib/pullConsumers/default/index.js b/src/lib/pullConsumers/default/index.js index 5ecdc237..4747ff75 100644 --- a/src/lib/pullConsumers/default/index.js +++ b/src/lib/pullConsumers/default/index.js @@ -40,8 +40,8 @@ module.exports = function (context) { // Re-format event data into array of objects const formattedData = event - .filter(d => (typeof d !== 'undefined' && Object.keys(d).indexOf('data') !== -1)) - .map(d => d.data); + .filter((d) => (typeof d !== 'undefined' && Object.keys(d).indexOf('data') !== -1)) + .map((d) => d.data); logger.info('success'); if (tracer) { diff --git a/src/lib/requestHandlers/declareHandler.js b/src/lib/requestHandlers/declareHandler.js index 9ced00d9..9b9d3a26 100644 --- a/src/lib/requestHandlers/declareHandler.js +++ b/src/lib/requestHandlers/declareHandler.js @@ -78,7 +78,7 @@ class DeclareEndpointHandler extends BaseRequestHandler { }; return this; }) - .catch(error => new ErrorHandler(error).process()); + .catch((error) => new ErrorHandler(error).process()); } } diff --git a/src/lib/requestHandlers/eventListenerHandler.js b/src/lib/requestHandlers/eventListenerHandler.js index 381f1259..29d48b87 100644 --- a/src/lib/requestHandlers/eventListenerHandler.js +++ b/src/lib/requestHandlers/eventListenerHandler.js @@ -53,7 +53,7 @@ class EventListenerEndpointHandler extends BaseRequestHandler { this.code = 200; return this; }) - .catch(error => new ErrorHandler(error).process()); + .catch((error) => new ErrorHandler(error).process()); } } diff --git a/src/lib/requestHandlers/ihealthPollerHandler.js b/src/lib/requestHandlers/ihealthPollerHandler.js index d0791a82..9406110f 100644 --- a/src/lib/requestHandlers/ihealthPollerHandler.js +++ b/src/lib/requestHandlers/ihealthPollerHandler.js @@ -45,7 +45,7 @@ class IHealthPollerEndpointHandler extends BaseRequestHandler { let responsePromise = Promise.resolve(); if (!this.params.system) { responsePromise = responsePromise.then(() => new Promise( - resolve => resolve(ihealth.getCurrentState(this.params.namespace)) + (resolve) => resolve(ihealth.getCurrentState(this.params.namespace)) ) .then((statuses) => { this.code = 200; @@ -66,7 +66,7 @@ class IHealthPollerEndpointHandler extends BaseRequestHandler { return this; })); } - return responsePromise.catch(error => new ErrorHandler(error).process()); + return responsePromise.catch((error) => new ErrorHandler(error).process()); } } diff --git a/src/lib/requestHandlers/infoHandler.js b/src/lib/requestHandlers/infoHandler.js index 5dbe8692..552e888d 100644 --- a/src/lib/requestHandlers/infoHandler.js +++ b/src/lib/requestHandlers/infoHandler.js @@ -52,6 +52,6 @@ class InfoEndpointHandler extends BaseRequestHandler { } } -router.on('register', routerInst => routerInst.register('GET', '/info', InfoEndpointHandler)); +router.on('register', (routerInst) => routerInst.register('GET', '/info', InfoEndpointHandler)); module.exports = InfoEndpointHandler; diff --git a/src/lib/requestHandlers/pullConsumerHandler.js b/src/lib/requestHandlers/pullConsumerHandler.js index 90d3e853..72472ee9 100644 --- a/src/lib/requestHandlers/pullConsumerHandler.js +++ b/src/lib/requestHandlers/pullConsumerHandler.js @@ -57,7 +57,7 @@ class PullConsumerEndpointHandler extends BaseRequestHandler { this.body = response.data; this.contentType = response.contentType || undefined; // If not set, default to iControlRest response return this; - }).catch(error => new ErrorHandler(error).process()); + }).catch((error) => new ErrorHandler(error).process()); } } diff --git a/src/lib/requestHandlers/router.js b/src/lib/requestHandlers/router.js index 0e005d62..d7f8293d 100644 --- a/src/lib/requestHandlers/router.js +++ b/src/lib/requestHandlers/router.js @@ -192,9 +192,8 @@ function restOperationResponder(restOperation, status, body, contentType) { restOperation.complete(); } - const defaultRouter = new RequestRouter(); -configWorker.on('change', config => new Promise((resolve) => { +configWorker.on('change', (config) => new Promise((resolve) => { logger.debug('configWorker change event in RequestRouter'); // helpful debug defaultRouter.removeAllHandlers(); defaultRouter.registerAllHandlers(configUtil.getTelemetryControls(config).debug); diff --git a/src/lib/requestHandlers/systemPollerHandler.js b/src/lib/requestHandlers/systemPollerHandler.js index 30f0cfd2..37e97132 100644 --- a/src/lib/requestHandlers/systemPollerHandler.js +++ b/src/lib/requestHandlers/systemPollerHandler.js @@ -50,10 +50,10 @@ class SystemPollerEndpointHandler extends BaseRequestHandler { .then(systemPoller.fetchPollersData.bind(systemPoller)) .then((fetchedData) => { this.code = 200; - this.body = fetchedData.map(d => d.data); + this.body = fetchedData.map((d) => d.data); return this; }) - .catch(error => new ErrorHandler(error).process()); + .catch((error) => new ErrorHandler(error).process()); } } diff --git a/src/lib/systemPoller.js b/src/lib/systemPoller.js index ad412f76..0ce2b10b 100644 --- a/src/lib/systemPoller.js +++ b/src/lib/systemPoller.js @@ -60,11 +60,11 @@ function findSystemOrPollerConfigs(originalConfig, sysOrPollerName, pollerName, if (sysOrPollerName && pollerName) { // probably system's and poller's names - pollers = systemPollers.filter(p => p.name === pollerName && p.systemName === sysOrPollerName); + pollers = systemPollers.filter((p) => p.name === pollerName && p.systemName === sysOrPollerName); } else { // each object has unique name per namespace // so, one of the system or poller will be 'undefined' - pollers = systemPollers.filter(p => p.systemName === sysOrPollerName); + pollers = systemPollers.filter((p) => p.systemName === sysOrPollerName); } if (pollers.length === 0) { @@ -81,10 +81,9 @@ function getEnabledPollerConfigs(originalConfig, includeDisabled) { if (includeDisabled) { return pollers; } - return pollers.filter(p => p.enable); + return pollers.filter((p) => p.enable); } - function applyConfig(originalConfig) { const currPollers = module.exports.getPollerTimers(); const systemPollers = getEnabledPollerConfigs(originalConfig); @@ -99,18 +98,18 @@ function applyConfig(originalConfig) { pollerConfig.tracer = tracers.fromConfig(pollerConfig.trace); const baseMsg = `system poller ${key}. Interval = ${pollerConfig.interval} sec.`; // add to data context to track source poller config and destination(s) - pollerConfig.destinationIds = configUtil.getReceivers(originalConfig, pollerConfig).map(r => r.id); + pollerConfig.destinationIds = configUtil.getReceivers(originalConfig, pollerConfig).map((r) => r.id); if (pollerConfig.interval === 0) { logger.info(`Configuring non-polling ${baseMsg}`); if (currPollers[key] && currPollers[key].timer) { promises.push(currPollers[key].timer.stop() - .catch((error => logger.exception(`Unable to stop timer for System Poller "${key}"`, error)))); + .catch(((error) => logger.exception(`Unable to stop timer for System Poller "${key}"`, error)))); } currPollers[key] = undefined; } else if (currPollers[key] && currPollers[key].timer) { logger.info(`Updating ${baseMsg}`); promises.push(currPollers[key].timer.update(safeProcess, pollerConfig.interval, pollerConfig) - .catch((error => logger.exception(`Unable to update timer for System Poller "${key}"`, error)))); + .catch(((error) => logger.exception(`Unable to update timer for System Poller "${key}"`, error)))); currPollers[key].config = pollerConfig; } else { logger.info(`Starting ${baseMsg}`); @@ -123,7 +122,7 @@ function applyConfig(originalConfig) { config: pollerConfig }; promises.push(currPollers[key].timer.start() - .catch((error => logger.exception(`Unable to start timer for System Poller "${key}"`, error)))); + .catch(((error) => logger.exception(`Unable to start timer for System Poller "${key}"`, error)))); } } }); @@ -134,7 +133,7 @@ function applyConfig(originalConfig) { // for pollers with interval=0, the key exists, but value is undefined if (!util.isObjectEmpty(currPollers[key]) && currPollers[key].timer) { promises.push(currPollers[key].timer.stop() - .catch((error => logger.exception(`Unable to stop timer for System Poller "${key}"`, error)))); + .catch(((error) => logger.exception(`Unable to stop timer for System Poller "${key}"`, error)))); } delete currPollers[key]; } @@ -154,7 +153,7 @@ function enablePollers() { if (poller.timer) { // Update timer to enable/re-enable the poller promises.push(poller.timer.update(safeProcess, poller.config.interval, poller.config) - .catch((error => logger.exception(`Unable to update timer for System Poller "${pollerKey}"`, error)))); + .catch(((error) => logger.exception(`Unable to update timer for System Poller "${pollerKey}"`, error)))); } else { poller.timer = new timers.SlidingTimer(safeProcess, { abortOnFailure: false, @@ -162,7 +161,7 @@ function enablePollers() { logger: logger.getChild(`${poller.config.traceName}.timer`) }, poller.config); promises.push(poller.timer.start() - .catch((error => logger.exception(`Unable to start timer for System Poller "${pollerKey}"`, error)))); + .catch(((error) => logger.exception(`Unable to start timer for System Poller "${pollerKey}"`, error)))); } } }); @@ -179,7 +178,7 @@ function disablePollers() { logger.info(`Disabling system poller ${pollerKey}`); if (poller.timer) { promises.push(poller.timer.stop() - .catch((error => logger.exception(`Unable to stop timer for System Poller "${pollerKey}"`, error)))); + .catch(((error) => logger.exception(`Unable to stop timer for System Poller "${pollerKey}"`, error)))); } } }); @@ -288,13 +287,13 @@ function getPollersConfig(sysOrPollerName, options) { .then(() => findSystemOrPollerConfigs( configWorker.currentConfig, sysOrPollerName, options.pollerName, options.namespace )) - .then(config => configUtil.decryptSecrets(config)) + .then((config) => configUtil.decryptSecrets(config)) .then((configs) => { if (configs.length === 0) { // unexpected, something went wrong throw new errors.ObjectNotFoundInConfigError(`No System or System Poller with name '${sysOrPollerName}' configured`); } - configs = configs.filter(c => c.enable || includeDisabled); + configs = configs.filter((c) => c.enable || includeDisabled); return configs; }); } @@ -316,8 +315,8 @@ function fetchPollersData(pollerConfigs, decryptSecrets) { return promise .then((decryptedConf) => { - const processPollers = decryptedConf.map(pollerConf => safeProcess(pollerConf, { requestFromUser: true }) - .catch(err => caughtErrors.push(err))); + const processPollers = decryptedConf.map((pollerConf) => safeProcess(pollerConf, { requestFromUser: true }) + .catch((err) => caughtErrors.push(err))); return Promise.all(processPollers); }) .then((data) => { @@ -329,7 +328,7 @@ function fetchPollersData(pollerConfigs, decryptSecrets) { } // config worker change event -configWorker.on('change', config => Promise.resolve() +configWorker.on('change', (config) => Promise.resolve() .then(() => { logger.debug('configWorker change event in systemPoller'); // reset for monitor check @@ -337,9 +336,9 @@ configWorker.on('change', config => Promise.resolve() return applyConfig(util.deepCopy(config)); }) .then(() => logger.debug(`${Object.keys(getPollerTimers()).length} system poller(s) running`)) - .catch(error => logger.exception('Uncaught error during System Poller(s) configuration', error))); + .catch((error) => logger.exception('Uncaught error during System Poller(s) configuration', error))); -monitor.on('check', status => new Promise((resolve) => { +monitor.on('check', (status) => new Promise((resolve) => { const monitorChecksOk = status === APP_THRESHOLDS.MEMORY.OK; // only enable/disable when there is change in state if (processingEnabled !== monitorChecksOk) { diff --git a/src/lib/systemStats.js b/src/lib/systemStats.js index 9f041b89..e3e22f71 100644 --- a/src/lib/systemStats.js +++ b/src/lib/systemStats.js @@ -108,10 +108,10 @@ SystemStats.prototype._getNormalizationOpts = function (property) { const options = {}; const defaultTags = { name: { pattern: '(.*)', group: 1 } }; const addKeysByTagIsObject = property.normalization - && property.normalization.find(n => n.addKeysByTag && typeof n.addKeysByTag === 'object'); + && property.normalization.find((n) => n.addKeysByTag && typeof n.addKeysByTag === 'object'); if (property.normalization) { - const filterKeysIndex = property.normalization.findIndex(i => i.filterKeys); + const filterKeysIndex = property.normalization.findIndex((i) => i.filterKeys); if (filterKeysIndex > -1) { property.normalization[filterKeysIndex] = { filterKeys: [ @@ -123,7 +123,7 @@ SystemStats.prototype._getNormalizationOpts = function (property) { options.filterKeys = [this.global.filterKeys]; } - const renameKeysIndex = property.normalization.findIndex(i => i.renameKeys); + const renameKeysIndex = property.normalization.findIndex((i) => i.renameKeys); if (renameKeysIndex > -1) { property.normalization[renameKeysIndex] = { renameKeys: [ @@ -135,7 +135,7 @@ SystemStats.prototype._getNormalizationOpts = function (property) { options.renameKeys = [this.global.renameKeys]; } - const addKeysByTagIndex = property.normalization.findIndex(i => i.addKeysByTag); + const addKeysByTagIndex = property.normalization.findIndex((i) => i.addKeysByTag); if (addKeysByTagIndex > -1) { property.normalization[addKeysByTagIndex] = { addKeysByTag: { @@ -279,7 +279,7 @@ SystemStats.prototype._processProperty = function (key, property) { SystemStats.prototype._computeContextData = function () { if (this.contextProps) { const promises = Object.keys(this.contextProps) - .map(key => this._processProperty(key, this.contextProps[key])); + .map((key) => this._processProperty(key, this.contextProps[key])); return Promise.all(promises).then(() => { Object.assign(this.contextData, this.collectedData); @@ -437,7 +437,6 @@ SystemStats.prototype._convertToProperty = function (keyName, endpoint) { }; }; - /** * Compute properties * @@ -447,7 +446,7 @@ SystemStats.prototype._convertToProperty = function (keyName, endpoint) { */ SystemStats.prototype._computePropertiesData = function () { return Promise.all(Object.keys(this.stats) - .map(key => this._processProperty(key, this.stats[key]))); + .map((key) => this._processProperty(key, this.stats[key]))); }; /** @@ -527,7 +526,6 @@ SystemStats.prototype.collect = function () { }); }; - /** * Helpers for stats filtering */ diff --git a/src/lib/teemReporter.js b/src/lib/teemReporter.js index f36ef725..a24670f7 100644 --- a/src/lib/teemReporter.js +++ b/src/lib/teemReporter.js @@ -58,7 +58,7 @@ class TeemReporter { .then(() => teemRecord.addClassCount(config)) .then(() => teemRecord.addJsonObject(this.fetchExtraData(config))) .then(() => this.teemDevice.reportRecord(teemRecord)) - .catch(err => this.logger.debugException('Unable to send analytics data', err)); + .catch((err) => this.logger.debugException('Unable to send analytics data', err)); } /** diff --git a/src/lib/utils/config.js b/src/lib/utils/config.js index 0fd2b63d..d6706c13 100644 --- a/src/lib/utils/config.js +++ b/src/lib/utils/config.js @@ -16,8 +16,7 @@ const deviceUtil = require('./device'); const logger = require('../logger'); const util = require('./misc'); - -/** @module configUtil */ +/** @module utils/config */ const CLASSES = constants.CONFIG_CLASSES; const POLLER_KEYS = { @@ -112,7 +111,7 @@ function componentizeConfig(originConfig, convertedConfig) { } }); }; - if (convertedConfig.components.some(c => c.namespace === constants.DEFAULT_UNNAMED_NAMESPACE)) { + if (convertedConfig.components.some((c) => c.namespace === constants.DEFAULT_UNNAMED_NAMESPACE)) { // add 'default' namespace as object too, will be removed later convertedConfig.components.push({ name: constants.DEFAULT_UNNAMED_NAMESPACE, @@ -188,12 +187,12 @@ function getTracePrefix(component) { * @param {Component} component - component * @param {boolean | object | string} [traceConfig] - trace config to process instead of component.trace * - * @returns {TypedTracerConfig} Tracer config + * @returns {TracerConfig} Tracer config */ function getTracerConfig(component, traceConfig) { traceConfig = arguments.length > 1 ? traceConfig : component.trace; if (Array.isArray(traceConfig)) { - return traceConfig.map(config => getTracerConfig(component, config)); + return traceConfig.map((config) => getTracerConfig(component, config)); } if (typeof traceConfig !== 'object') { /** @@ -227,7 +226,7 @@ function getTracerConfig(component, traceConfig) { */ function generateComponentsIDs(convertedConfig) { _module.getComponents(convertedConfig).forEach((component) => { - // have to call 'generateUuid' with 'component' as context to be able to generate + // ATTENTION: have to call 'generateUuid' with 'component' as context to be able to generate // predictable IDs for testing component.id = util.generateUuid.call(component); }); @@ -241,7 +240,7 @@ function generateComponentsIDs(convertedConfig) { */ function hasSplunkLegacy(config, namespace) { return _module.getTelemetryConsumers(config, namespace) - .some(c => c.type === 'Splunk' && c.format === 'legacy'); + .some((c) => c.type === 'Splunk' && c.format === 'legacy'); } /** @@ -258,23 +257,25 @@ function mapComponents(convertedConfig) { // then lookup the corresponding destinations (consumers to load) // for pull consumer: map consumer to the poller so we can look up configuration to use to then fetch data _module.getComponents(convertedConfig).forEach((component) => { - if (useForMapping(component.class)) { - const systemNameLog = component.systemName ? `system = ${component.systemName}, ` : ''; - let receivers; - + if (useForMapping(component.class) && component.enable) { + const receivers = []; if (component.class === CLASSES.PULL_CONSUMER_SYSTEM_POLLER_GROUP_CLASS_NAME) { - receivers = _module.getTelemetryPullConsumers(convertedConfig, component.namespace) - .filter(pc => pc.name === component.pullConsumer); - } else { - receivers = _module.getTelemetryConsumers(convertedConfig, component.namespace); + const pullConsumer = _module.getTelemetryPullConsumers(convertedConfig, component.namespace) + .find((pc) => pc.id === component.pullConsumer); + receivers.push(pullConsumer); + } else if (!(component.class === CLASSES.SYSTEM_POLLER_CLASS_NAME && component.interval === 0)) { + _module.getTelemetryConsumers(convertedConfig, component.namespace) + .forEach((c) => receivers.push(c)); } - if (receivers.length) { - convertedConfig.mappings[component.id] = receivers.map((p) => { - logger.debug(`Creating a route for data to "${p.name}" (${p.class}) from "${component.name}" (${component.class}) [${systemNameLog}namespace = ${component.namespace}]`); + + const enabledReceivers = receivers.filter((r) => r.enable); + if (enabledReceivers.length) { + convertedConfig.mappings[component.id] = enabledReceivers.map((p) => { + logger.debug(`Creating a route for data to "${p.traceName}" (${p.class}) from "${component.traceName}" (${component.class})`); return p.id; }); } else { - logger.debug(`No data route created for "${component.name}" (${component.class}) - no data sources/receivers [${systemNameLog}namespace = ${component.namespace}]]`); + logger.debug(`No data route created for "${component.traceName}" (${component.class}) - no data sources/receivers`); } } }); @@ -302,6 +303,7 @@ function normalizeComponents(convertedConfig) { normalizeTelemetryConsumers(convertedConfig); normalizeTelemetryPullConsumers(convertedConfig); generateComponentsIDs(convertedConfig); + postProcessTelemetryPullConsumerSystemPollerGroups(convertedConfig); }); } @@ -364,12 +366,12 @@ function normalizeTelemetryEndpoints(convertedConfig) { if (typeof endpoint === 'object') { // array of definitions - can be all of the following if (Array.isArray(endpoint)) { - endpoint.forEach(innerEndpoint => processEndpoint(innerEndpoint, cb)); + endpoint.forEach((innerEndpoint) => processEndpoint(innerEndpoint, cb)); // endpoint is Telemetry_Endpoints } else if (endpoint.class === CLASSES.ENDPOINTS_CLASS_NAME || endpoint.items) { // don't need to copy 'endpoint' because it was either reference or inline config computeBasePath(endpoint); - Object.keys(endpoint.items).forEach(key => cb(parseEndpointItem(endpoint, key))); + Object.keys(endpoint.items).forEach((key) => cb(parseEndpointItem(endpoint, key))); // endpoint is Telemetry_Endpoint } else if (typeof endpoint.path === 'string') { // it has 'name' and 'path' properties already (see endpoints_schema.json) @@ -380,7 +382,7 @@ function normalizeTelemetryEndpoints(convertedConfig) { const refs = trimPath(endpoint).split('/'); // reference to a Telemetry_Endpoints object // format is ObjectName/pathName - endpoint = telemetryEndpoints.find(e => e.name === refs[0]); + endpoint = telemetryEndpoints.find((e) => e.name === refs[0]); if (refs.length > 1) { // reference to a child of Telemetry_Endpoints.items endpoint = util.deepCopy(endpoint); @@ -423,7 +425,6 @@ function normalizeTelemetryEndpoints(convertedConfig) { * Note: This method mutates 'convertedConfig'. * * @param {Configuration} convertedConfig - object to save normalized config to - * @param {object} componentDefaults - default components values * * @returns {void} once Telemetry_iHealth_Poller objects normalized */ @@ -437,11 +438,11 @@ function normalizeTelemetryIHealthPollers(convertedConfig) { * and should be attached to Telemetry_System instead */ const pollersWithoutSystem = _module.getTelemetryIHealthPollers(convertedConfig) - .filter(poller => !poller.systemName); + .filter((poller) => !poller.systemName); // remove those pollers - we don't need them any more _module.removeComponents(convertedConfig, { class: CLASSES.IHEALTH_POLLER_CLASS_NAME, - filter: c => pollersWithoutSystem.indexOf(c) !== -1 + filter: (c) => pollersWithoutSystem.indexOf(c) !== -1 }); } @@ -465,9 +466,9 @@ function normalizeTelemetryListeners(convertedConfig) { let traceConfigs = getTracerConfig(listener); traceConfigs = Array.isArray(traceConfigs) ? traceConfigs : [traceConfigs]; - listener.trace = traceConfigs.find(t => t.type === 'output') + listener.trace = traceConfigs.find((t) => t.type === 'output') || getTracerConfig(listener, false); - listener.traceInput = traceConfigs.find(t => t.type === 'input') + listener.traceInput = traceConfigs.find((t) => t.type === 'input') || getTracerConfig(listener, { enable: false, type: 'input' }); }); } @@ -514,6 +515,46 @@ function normalizeTelemetryPullConsumers(convertedConfig) { }); } +/** + * Normalize Telemetry_Pull_Consumer_System_Poller_Group objects + * + * Note: This method mutates 'convertedConfig'. + * + * @param {Configuration} convertedConfig - object to save normalized config to + * + * @returns {void} once Telemetry_Pull_Consumer_System_Poller_Group objects normalized + */ +function normalizeTelemetryPullConsumerSystemPollerGroups(convertedConfig) { + // at that point Telemetry_System and Telemetry_System_Poller + // objects should be normalized already. Should happen BEFORE + // Telemetry_Pull_Consumer normalization + /** + * Combination of (class, namespace, name) is unique because: + * - component's name is unique withing namespace + * - Telemetry_Pull_Consumer_System_Poller_Group is not public class + * As result: + * - safe to assign name based on component's name + * without checking for existence + * - safe to refer to a pull consumer by name within namespace scope + */ + const systemPollerGroups = _module.getTelemetryPullConsumerSystemPollerGroups(convertedConfig); + const pullConsumers = _module.getTelemetryPullConsumers(convertedConfig); + + systemPollerGroups.forEach((pollerGroup) => { + const pullConsumer = pullConsumers + .find((pc) => pc.namespace === pollerGroup.namespace && pc.name === pollerGroup.pullConsumer); + + pollerGroup.enable = pullConsumer.enable; + pollerGroup.name = `${pollerGroup.class}_${pullConsumer.name}`; + pollerGroup.traceName = `${getTracePrefix(pollerGroup)}${pollerGroup.name}`; + // disable tracing, rely on pull consumer and system poller(s) tracing + pollerGroup.trace = { enable: false }; + pollerGroup.systemPollers = Array.isArray(pullConsumer.systemPoller) + ? pullConsumer.systemPoller + : [pullConsumer.systemPoller]; + }); +} + /** * Normalize Telemetry_System objects * @@ -561,7 +602,7 @@ function normalizeTelemetrySystems(convertedConfig) { if (typeof systemPoller === 'string') { // looking for unbound pre-existing Telemetry_System_Poller in same namespace pollerObj = preExistingTelemetrySystemPollers - .find(p => p.namespace === system.namespace && p.name === systemPoller); + .find((p) => p.namespace === system.namespace && p.name === systemPoller); if (!pollerObj) { logger.debug(`Unable to find Telemetry_System_Poller referenced by name "${systemPoller}" for Telemetry_System "${system.name}" in Telemetry_Namespace "${system.namespace}"`); return; @@ -573,8 +614,8 @@ function normalizeTelemetrySystems(convertedConfig) { // refresh list of existing name within system existingNames = _module.getTelemetrySystemPollers( convertedConfig, - p => p.namespace === system.namespace && p.systemName === system.name - ).map(p => p.name); + (p) => p.namespace === system.namespace && p.systemName === system.name + ).map((p) => p.name); assignPollerName('SystemPoller', pollerObj); } pollerObj = updateSystemPollerConfig(system, util.deepCopy(pollerObj), fetchTMStats); @@ -591,7 +632,7 @@ function normalizeTelemetrySystems(convertedConfig) { if (typeof iHealthPoller === 'string') { // looking for unbound pre-existing Telemetry_iHealth_Poller in same namespace pollerObj = preExistingTelemetryIHealthPollers - .find(p => p.namespace === system.namespace && p.name === iHealthPoller); + .find((p) => p.namespace === system.namespace && p.name === iHealthPoller); if (!pollerObj) { logger.debug(`Unable to find Telemetry_iHealth_Poller referenced by name "${iHealthPoller}" for Telemetry_System "${system.name}" in Telemetry_Namespace "${system.namespace}"`); return; @@ -603,8 +644,8 @@ function normalizeTelemetrySystems(convertedConfig) { // refresh list of existing name within system existingNames = _module.getTelemetryIHealthPollers( convertedConfig, - p => p.namespace === system.namespace && p.systemName === system.name - ).map(p => p.name); + (p) => p.namespace === system.namespace && p.systemName === system.name + ).map((p) => p.name); assignPollerName('iHealthPoller', pollerObj); } pollerObj = updateIHealthPollerConfig(system, util.deepCopy(pollerObj)); @@ -628,11 +669,11 @@ function normalizeTelemetrySystems(convertedConfig) { */ function normalizeTelemetrySystemPollers(convertedConfig, componentDefaults) { const pollersWithoutSystem = _module.getTelemetrySystemPollers(convertedConfig) - .filter(poller => !poller.systemName); + .filter((poller) => !poller.systemName); // remove those pollers - we don't need them any more _module.removeComponents(convertedConfig, { class: CLASSES.SYSTEM_POLLER_CLASS_NAME, - filter: c => pollersWithoutSystem.indexOf(c) !== -1 + filter: (c) => pollersWithoutSystem.indexOf(c) !== -1 }); function createSystemFromSystemPoller(systemPoller) { @@ -658,44 +699,28 @@ function normalizeTelemetrySystemPollers(convertedConfig, componentDefaults) { } /** - * Normalize Telemetry_Pull_Consumer_System_Poller_Group objects + * Post Process Telemetry_Pull_Consumer_System_Poller_Group objects * * Note: This method mutates 'convertedConfig'. * * @param {Configuration} convertedConfig - object to save normalized config to - * @param {object} componentDefaults - default components values * - * @returns {void} once Telemetry_Pull_Consumer_System_Poller_Group objects normalized + * @returns {void} once Telemetry_Pull_Consumer_System_Poller_Group objects updated */ -function normalizeTelemetryPullConsumerSystemPollerGroups(convertedConfig) { - // at that point Telemetry_System and Telemetry_System_Poller - // objects should be normalized already. Should happen BEFORE - // Telemetry_Pull_Consumer normalization - /** - * Combination of (class, namespace, name) is unique because: - * - component's name is unique withing namespace - * - Telemetry_Pull_Consumer_System_Poller_Group is not public class - * As result: - * - safe to assign name based on component's name - * without checking for existence - * - safe to refer to a pull consumer by name within namespace scope - */ - const systemPollerGroups = _module.getTelemetryPullConsumerSystemPollerGroups(convertedConfig); - const pullConsumers = _module.getTelemetryPullConsumers(convertedConfig); - - systemPollerGroups.forEach((pollerGroup) => { - const pullConsumer = pullConsumers - .find(pc => pc.namespace === pollerGroup.namespace && pc.name === pollerGroup.pullConsumer); - - pollerGroup.enable = pullConsumer.enable; - pollerGroup.name = `${pollerGroup.class}_${pullConsumer.name}`; - pollerGroup.traceName = `${getTracePrefix(pollerGroup)}${pollerGroup.name}`; - // disable tracing, rely on pull consumer and system poller(s) tracing - pollerGroup.trace = { enable: false }; - pollerGroup.systemPollers = Array.isArray(pullConsumer.systemPoller) - ? pullConsumer.systemPoller - : [pullConsumer.systemPoller]; - }); +function postProcessTelemetryPullConsumerSystemPollerGroups(convertedConfig) { + // at that point IDs should be generated already + _module.getTelemetryPullConsumerSystemPollerGroups(convertedConfig) + .forEach((pg) => { + // updating pull consumer info + const pullConsumer = _module.getTelemetryPullConsumers(convertedConfig, pg.namespace) + .find((pc) => pc.name === pg.pullConsumer); + + // convert all names/refs to IDs + pg.pullConsumer = pullConsumer.id; + pg.systemPollers = _module.getTelemetrySystemPollers(convertedConfig, pg.namespace) + .filter((sp) => sp.enable && pg.systemPollers.indexOf(sp.name) !== -1) + .map((sp) => sp.id); + }); } /** @@ -857,14 +882,14 @@ _module = module.exports = { if (options.class) { let filter = options.class; if (typeof filter !== 'function') { - filter = c => c.class === options.class; + filter = (c) => c.class === options.class; } components = components.filter(filter); } if (options.namespace) { let filter = options.namespace; if (typeof filter !== 'function') { - filter = c => c.namespace === options.namespace; + filter = (c) => c.namespace === options.namespace; } components = components.filter(filter); } @@ -875,7 +900,7 @@ _module = module.exports = { }, /** - * Get receivers for component + * Get configured and enabled receivers for component * * @public * @param {Configuration} config - config @@ -889,7 +914,29 @@ _module = module.exports = { return []; } return _module.getComponents(config, { - filter: c => ids.indexOf(c.id) !== -1, + filter: (c) => ids.indexOf(c.id) !== -1, + namespace: component.namespace + }); + }, + + /** + * Get configured and enabled data sources for component + * + * @public + * @param {Configuration} config - config + * @param {Component} component - component + * + * @returns {Array} array of data sources + */ + getSources(config, component) { + const ids = Object.keys(config.mappings) + .filter((id) => config.mappings[id].indexOf(component.id) !== -1); + + if (ids.length === 0) { + return []; + } + return _module.getComponents(config, { + filter: (c) => ids.indexOf(c.id) !== -1, namespace: component.namespace }); }, @@ -1012,7 +1059,7 @@ _module = module.exports = { */ getTelemetrySystemPollersForGroup(config, pollerGroup) { return _module.getTelemetrySystemPollers(config, pollerGroup.namespace) - .filter(sp => pollerGroup.systemPollers.indexOf(sp.name) !== -1); + .filter((sp) => pollerGroup.systemPollers.indexOf(sp.id) !== -1); }, /** @@ -1037,12 +1084,12 @@ _module = module.exports = { * @param {Configuration} config - config * @param {Component} pullConsumer - Telemetry_Pull_Consumer object * - * @returns {Component} Telemetry_Pull_Consumer_System_Poller_Group object + * @returns {Component | undefined} Telemetry_Pull_Consumer_System_Poller_Group object */ getTelemetryPullConsumerSystemPollerGroupForPullConsumer(config, pullConsumer) { return _module.getTelemetryPullConsumerSystemPollerGroups(config, pullConsumer.namespace) - .filter(pg => pg.pullConsumer === pullConsumer.name) - .find(pg => config.mappings[pg.id].indexOf(pullConsumer.id) !== -1); + .filter((pg) => pg.pullConsumer === pullConsumer.id && config.mappings[pg.id]) + .find((pg) => config.mappings[pg.id].indexOf(pullConsumer.id) !== -1); }, /** @@ -1073,7 +1120,7 @@ _module = module.exports = { * @returns {boolean} true if config has "enabled" components */ hasEnabledComponents(config, options) { - return _module.getComponents(config, options).some(c => c.class !== CLASSES.CONTROLS_CLASS_NAME && c.enable); + return _module.getComponents(config, options).some((c) => c.class !== CLASSES.CONTROLS_CLASS_NAME && c.enable); }, /** @@ -1093,10 +1140,10 @@ _module = module.exports = { .then((newNormalizedConfig) => { // all objects have .namespace property - get all namespaces in new config const namespaces = _module.getComponents(newNormalizedConfig) - .map(c => c.namespace) + .map((c) => c.namespace) .filter((val, index, self) => self.indexOf(val) === index); // remove namespaces from existing config - _module.removeComponents(config, { namespace: c => namespaces.indexOf(c.namespace) !== -1 }); + _module.removeComponents(config, { namespace: (c) => namespaces.indexOf(c.namespace) !== -1 }); cleanupComponents(newNormalizedConfig); config.mappings = Object.assign(config.mappings, newNormalizedConfig.mappings); config.components = config.components.concat(newNormalizedConfig.components); @@ -1158,7 +1205,7 @@ _module = module.exports = { config.mappings = {}; return; } - config.components = config.components.filter(c => componentsToRemove.indexOf(c) === -1); + config.components = config.components.filter((c) => componentsToRemove.indexOf(c) === -1); if (util.isObjectEmpty(config.mappings)) { return; } @@ -1203,13 +1250,23 @@ _module = module.exports = { * @typedef Component * @type {object} * @property {string} class - class name a component belongs to - * @property {string} id - unique ID + * @property {string} id - unique ID for config's current life span * @property {string} name - name * @property {string} namespace - namespace a component belongs to * @property {string} traceName - unique name computed using namespace and object's name, * should be used for logging and etc. * @property {boolean} [enable] - true if component enabled - * @property {boolean} [trace] - true if 'trace' enabled + * @property {TraceConfig} [trace] - true if 'trace' enabled + * + * Note: + * - component will have newly generated 'id' every time when: + * - config loaded on application start + * - declaration submitted to 'default' namespace (/declare) (every component + * will have newly generated 'id' despite on namespace it belongs to) + * - declaration submitted for a particular namespace (every component in a namespace will have newly generated 'id') + * - mappings contains only: + * - enabled components + * - if poller has 0 interval it will not be mapped to Telemetry_Consumer */ /** * @typedef Configuration @@ -1234,7 +1291,88 @@ _module = module.exports = { * } */ /** - * @typedef TypedTracerConfig - * @type {TracerConfig} - * @property {string} type - 'output' or 'input' + * @typedef ConsumerComponent + * @type {Component} + * @property {string} type - consumer's type + */ +/** + * @typedef DataAction + * @type {object} + * @property {boolean} enable - enable/disable + */ +/** + * @typedef DataActions + * @type {Array} + */ +/** + * @typedef ExcludeDataAction + * @type {DataAction} + * @property {object} includeData + * @property {object} locations + * @property {object} [ifAllMatch] + * @property {object} [ifAnyMatch] + */ +/** + * @typedef IncludeDataAction + * @type {DataAction} + * @property {object} includeData + * @property {object} locations + * @property {object} [ifAllMatch] + * @property {object} [ifAnyMatch] + */ +/** + * @typedef PassphraseSecret + * @type {object} + * @property {string} cipherText - encrypted secret + * @property {string} class - class name + * @property {string} protected - type of protection + */ +/** + * @typedef PullConsumerSystemPollerGroup + * @type {Component} + * @property {Array} systemPollers - list of System Pollers IDs (enabled only) + * @property {string} pullConsumer - pull consumer ID + */ +/** + * @typedef SetTagDataAction + * @type {DataAction} + * @property {object} setTag - tags to set + * @property {object} [locations] + * @property {object} [ifAllMatch] + * @property {object} [ifAnyMatch] + */ +/** + * @typedef SystemPollerComponent + * @type {Component} + * @property {object} connection - connection config + * @property {boolean} connection.allowSelfSignedCert + * @property {string} connection.host - host + * @property {number} connection.port - port + * @property {string} connection.protocol - protocol (http or https) + * @property {object} credentials - auth config + * @property {string} credentials.username + * @property {PassphraseSecret | string} credentials.passphrase + * @property {object} dataOpts - data modifications config + * @property {DataActions} dataOpts.actions + * @property {boolean} dataOpts.noTMStats - if 'true' then exclude 'tmstats' from stats + * @property {object} dataOpts.tags - old-style tagging + * @property {Object} endpoints - endpoints to poll data from + * @property {number} interval - polling interval in seconds + * @property {string} systemName - system's names it belongs to + */ +/** + * @typedef SystemPollerEndpoint + * @type {object} + * @property {boolean} enable - enabled/disabled + * @property {string} name - endpoint name + * @property {string} path - endpoint path + */ +/** + * @typedef TraceConfig + * @type {object} + * @property {boolean} enable - enabled/disabled + * @property {string} encoding - output encoding + * @property {number} maxRecords - max records to store + * @property {string} path - path to destination file + * @property {string} type - tracer's type - input or output */ diff --git a/src/lib/utils/data.js b/src/lib/utils/data.js index 561d84a1..132c82e3 100644 --- a/src/lib/utils/data.js +++ b/src/lib/utils/data.js @@ -43,7 +43,7 @@ function checkConditions(dataCtx, actionCtx) { function checkAnyMatches(data, matchObjects) { // Use 'Array.prototype.some' to check whether at least 1 condition is true - return matchObjects.some(conditions => checkAllMatches(data, conditions)); + return matchObjects.some((conditions) => checkAllMatches(data, conditions)); } function checkAllMatches(data, conditions) { @@ -59,7 +59,7 @@ function checkAllMatches(data, conditions) { const conditionVals = conditions[conditionKey]; if (typeof conditionVals !== 'object') { - return matches.every(match => checkScalarValue(data[match], conditionVals)); + return matches.every((match) => checkScalarValue(data[match], conditionVals)); } if (Array.isArray(conditionVals)) { @@ -78,7 +78,7 @@ function checkAllMatches(data, conditions) { } // the next condition is an object (and not array); do recursion and matches for the key have been found - return matches.every(match => checkAllMatches(data[match], conditionVals)); + return matches.every((match) => checkAllMatches(data[match], conditionVals)); }); } @@ -129,7 +129,7 @@ function getMatches(data, property, propAsKey) { if (Object.prototype.hasOwnProperty.call(data, property)) { return [property]; } - const checkFx = propAsKey ? (key => property.match(key)) : (key => key.match(property)); + const checkFx = propAsKey ? ((key) => property.match(key)) : ((key) => key.match(property)); return Object.keys(data).filter(checkFx); } @@ -195,7 +195,7 @@ function getDeepMatches(data, matchObj) { */ function searchAnyMatches(data, matchObj, cb) { if (Array.isArray(matchObj)) { - matchObj.forEach(matchItem => searchAnyMatches(data, matchItem, cb)); + matchObj.forEach((matchItem) => searchAnyMatches(data, matchItem, cb)); } else { Object.keys(matchObj).forEach((matchKey) => { getMatches(data, matchKey).forEach((key) => { diff --git a/src/lib/utils/datetime.js b/src/lib/utils/datetime.js index 7a88ae62..ec88db2e 100644 --- a/src/lib/utils/datetime.js +++ b/src/lib/utils/datetime.js @@ -11,7 +11,6 @@ const constants = require('../constants'); const util = require('./misc'); - /** * Parse HH:MM string to tuple(integer, integer) * @@ -212,14 +211,14 @@ function getNextFireDate(schedule, fromDate, allowNow, utcOnly) { } else if (schedule.frequency === 'weekly') { const dayOfWeek = getWeekDayNo(schedule.day); // move time forward for a week - adjustment = date => setNextWeekDay(date, dayOfWeek); + adjustment = (date) => setNextWeekDay(date, dayOfWeek); // simply check if day is desired week day - isOnSchedule = date => date.getUTCDay() === dayOfWeek; + isOnSchedule = (date) => date.getUTCDay() === dayOfWeek; } else { // monthly schedule, day of month const dayOfMonth = schedule.day; // move date to desired dayOfMonth and to next month if needed - adjustment = date => setNextMonthDay(date, dayOfMonth); + adjustment = (date) => setNextMonthDay(date, dayOfMonth); // simply check current date against desired isOnSchedule = function (date) { const lastDayOfMonth = getLastDayOfMonth(date); @@ -259,7 +258,6 @@ function getNextFireDate(schedule, fromDate, allowNow, utcOnly) { return utcOnly ? startExecDate : transformUTCToLocalDate(startExecDate); } - module.exports = { getCurrentUnixTimeInSeconds, getLastDayOfMonth, diff --git a/src/lib/utils/device.js b/src/lib/utils/device.js index f514625f..98a4637c 100644 --- a/src/lib/utils/device.js +++ b/src/lib/utils/device.js @@ -18,7 +18,6 @@ const promiseUtil = require('./promise'); const requestsUtil = require('./requests'); const util = require('./misc'); - /** * Cache for info about the device TS is running on * @@ -223,7 +222,7 @@ DeviceAsyncCLI.prototype._request = function (uri, options) { */ DeviceAsyncCLI.prototype._upsertTemporaryCLIscriptOnDevice = function (script) { return this._createTemporaryCLIscriptOnDevice(script) - .then(retVal => (retVal && Promise.resolve()) || this._updateTemporaryCLIscriptOnDevice(script)); + .then((retVal) => (retVal && Promise.resolve()) || this._updateTemporaryCLIscriptOnDevice(script)); }; /** @@ -532,7 +531,6 @@ function isVersionAffectedBySecretsBug(version) { && util.compareVersionStrings(version.version, '<', '15.0'); } - module.exports = { /** * Gather Host Device Info (Host Device is the device TS is running on) @@ -930,7 +928,7 @@ module.exports = { utilCmdArgs: `-c "${command}"` }; return this.makeDeviceRequest(host, uri, options) - .then(res => res.commandResult || ''); + .then((res) => res.commandResult || ''); }, /** @@ -959,7 +957,7 @@ module.exports = { }; return this.makeDeviceRequest(host, uri, options) - .then(data => ({ token: data.token.token })); + .then((data) => ({ token: data.token.token })); }, /** @@ -1009,7 +1007,7 @@ module.exports = { } return promise.then(() => { const splitData = data.match(/(.|\n){1,500}/g); - return encryptSecretHelper(splitData, [], 0, affectedByBug).then(result => result.join(',')); + return encryptSecretHelper(splitData, [], 0, affectedByBug).then((result) => result.join(',')); }); }, @@ -1024,7 +1022,7 @@ module.exports = { const splitData = data.split(','); const args = [`${__dirname}/decryptConfValue.php`].concat(splitData); return util.childProcess.execFile('/usr/bin/php', args) - .then(ret => ret[0]); + .then((ret) => ret[0]); }, /** @@ -1190,7 +1188,7 @@ module.exports = { options.includeResponseObject = false; return this.makeDeviceRequest(host, uri, options) - .then(res => ({ + .then((res) => ({ baseMac: res.baseMac, build: res.build, chassisSerialNumber: res.chassisSerialNumber, diff --git a/src/lib/utils/eventEmitter.js b/src/lib/utils/eventEmitter.js index da2b0820..5b3cd195 100644 --- a/src/lib/utils/eventEmitter.js +++ b/src/lib/utils/eventEmitter.js @@ -52,7 +52,7 @@ class SafeEventEmitter extends EventEmitter2 { safeEmitAsync() { try { return this.emitAsync.apply(this, arguments) - .catch(error => logSafeEmitException.call(this, arguments[0], error)); + .catch((error) => logSafeEmitException.call(this, arguments[0], error)); } catch (emitErr) { return Promise.resolve(logSafeEmitException.call(this, arguments[0], emitErr)); } diff --git a/src/lib/utils/ihealth.js b/src/lib/utils/ihealth.js index cd656490..aa30c4bd 100644 --- a/src/lib/utils/ihealth.js +++ b/src/lib/utils/ihealth.js @@ -324,7 +324,7 @@ class DeviceAPI { removeFile(fpath) { this.logger.debug(`Removing "${fpath}"`); return deviceUtil.removePath(fpath, this.host, this.getDefaultRequestOptions()) - .catch(removeErr => this.logger.debugException(`Unable to remove "${fpath}"`, removeErr)); + .catch((removeErr) => this.logger.debugException(`Unable to remove "${fpath}"`, removeErr)); } /** @@ -654,7 +654,7 @@ class IHealthAPI { requestOptions.includeResponseObject = true; return this.sendRequest(requestOptions); }) - .then(res => Promise.resolve(res[1].statusCode === 200)); + .then((res) => Promise.resolve(res[1].statusCode === 200)); } /** @@ -721,7 +721,6 @@ class IHealthAPI { } } - /** * iHealth Manager to upload Qkview and poll diagnostics from the local device * diff --git a/src/lib/utils/metadata.js b/src/lib/utils/metadata.js index 30e1d983..9d0326ab 100644 --- a/src/lib/utils/metadata.js +++ b/src/lib/utils/metadata.js @@ -41,7 +41,7 @@ function getInstanceMetadata(consumerContext) { logger.debug(`Unable to retrieve instance metadata for consumer ${consumerType}. ${err.messsage}`); }) // ensure this does not cause promise rejection if error occurs - .then(metadata => Promise.resolve(util.isObjectEmpty(metadata) ? null : metadata)); + .then((metadata) => Promise.resolve(util.isObjectEmpty(metadata) ? null : metadata)); } module.exports = { diff --git a/src/lib/utils/misc.js b/src/lib/utils/misc.js index 6f57faf4..18d31137 100644 --- a/src/lib/utils/misc.js +++ b/src/lib/utils/misc.js @@ -94,7 +94,6 @@ const KEYWORDS_TO_MASK = [ } ]; - module.exports = { /** * Assign defaults to object (uses lodash.defaultsDeep under the hood) @@ -153,7 +152,6 @@ module.exports = { return cloneDeep(obj); }, - /** * Merges an Array of Objects into a single Object (uses lodash.mergeWith under the hood). * Note: Nested Arrays are concatenated, not overwritten. diff --git a/src/lib/utils/monitor.js b/src/lib/utils/monitor.js index 8c235e93..7e50f6ed 100644 --- a/src/lib/utils/monitor.js +++ b/src/lib/utils/monitor.js @@ -145,7 +145,7 @@ class Monitor extends SafeEventEmitter { const usedMemPercent = Math.round((usedMem / this.memoryLimit) * 100); logger.debug(`MEMORY_USAGE: ${usedMem} MB (${usedMemPercent}%, limit = ${this.memoryLimit} MB)`); const newInterval = MEM_PERCENT_TO_INTERVAL_SEC.find( - mapping => usedMemPercent >= mapping.min && usedMemPercent <= mapping.max + (mapping) => usedMemPercent >= mapping.min && usedMemPercent <= mapping.max ).interval; if (this.timer.intervalInS !== newInterval) { return this.update(this.memoryThresholdPercent, newInterval); @@ -172,7 +172,7 @@ monitor.on('error', (err) => { logger.exception('An unexpected error occurred in monitor checks', err); }); -configWorker.on('change', config => Promise.resolve() +configWorker.on('change', (config) => Promise.resolve() .then(() => { logger.debug('configWorker change event in monitor'); const controls = configUtil.getTelemetryControls(config); diff --git a/src/lib/utils/normalize.js b/src/lib/utils/normalize.js index f2e5d6d1..06edaa43 100644 --- a/src/lib/utils/normalize.js +++ b/src/lib/utils/normalize.js @@ -177,7 +177,7 @@ module.exports = { */ _checkDataHasKeys(data, keys, options) { options = options || {}; - const func = key => data[key] !== undefined; + const func = (key) => data[key] !== undefined; if (options.all) { return keys.every(func); @@ -295,7 +295,7 @@ module.exports = { } values.push(data[k][key]); }); - const averageFunc = arr => Math.round(arr.reduce((a, b) => a + b, 0) / arr.length); + const averageFunc = (arr) => Math.round(arr.reduce((a, b) => a + b, 0) / arr.length); return averageFunc(values); }, @@ -368,7 +368,7 @@ module.exports = { const data = args.data; const accumulateSubKeys = (arg, dataKeys) => dataKeys - .map(key => data[key][arg]) + .map((key) => data[key][arg]) .reduce((acc, val) => acc + val); if (args.nestedObjects && typeof data === 'object') { @@ -380,7 +380,7 @@ module.exports = { } // this should result in a number between 0 and 100 (percentage) - let ret = Math.round(data[args.partialKey] / data[args.totalKey] * 100); + let ret = Math.round((data[args.partialKey] / data[args.totalKey]) * 100); ret = args.inverse ? 100 - ret : ret; return ret; }, @@ -466,7 +466,7 @@ module.exports = { throw new Error(`convertMapToArray() object required: ${util.stringify(data)}`); } - Object.keys(data).forEach(key => ret.push(data[key])); + Object.keys(data).forEach((key) => ret.push(data[key])); return ret; }, @@ -488,7 +488,7 @@ module.exports = { const vsAndServer = statsEntry.nestedStats.selfLink.split('/members/')[1].split('/stats')[0]; const vs = vsAndServer.split(':')[0]; const server = vsAndServer.split(':')[1]; - const item = data.membersReference.items.find(i => i.selfLink.includes(`${server}:${vs}`)); + const item = data.membersReference.items.find((i) => i.selfLink.includes(`${server}:${vs}`)); Object.assign(data.membersReference.entries[key].nestedStats.entries, item); }); } else { @@ -517,12 +517,12 @@ module.exports = { Object.keys(data).forEach((key) => { const item = data[key]; if (item.pools) { - item.pools = item.pools.map(p => buildFullPath(p)); + item.pools = item.pools.map((p) => buildFullPath(p)); } if (item.poolsCname) { item.pools = item.pools || []; - item.pools = item.pools.concat(item.poolsCname.map(p => buildFullPath(p))); + item.pools = item.pools.concat(item.poolsCname.map((p) => buildFullPath(p))); } if (item.lastResortPool) { diff --git a/src/lib/utils/promise.js b/src/lib/utils/promise.js index 1dd0693e..3ceaba33 100644 --- a/src/lib/utils/promise.js +++ b/src/lib/utils/promise.js @@ -66,10 +66,10 @@ module.exports = { * given promises have either fulfilled or rejected */ allSettled(promises) { - return Promise.all(promises.map(p => Promise.resolve(p) + return Promise.all(promises.map((p) => Promise.resolve(p) .then( - val => ({ status: 'fulfilled', value: val }), - err => ({ status: 'rejected', reason: err }) + (val) => ({ status: 'fulfilled', value: val }), + (err) => ({ status: 'rejected', reason: err }) ))); }, diff --git a/src/lib/utils/requests.js b/src/lib/utils/requests.js index 70c4570e..c274caf9 100644 --- a/src/lib/utils/requests.js +++ b/src/lib/utils/requests.js @@ -12,7 +12,6 @@ const request = require('request'); const constants = require('../constants'); const util = require('./misc'); - /** @module requestsUtil */ /* Helper functions for making requests */ diff --git a/src/lib/utils/systemStats.js b/src/lib/utils/systemStats.js index a9581160..2f02e34d 100644 --- a/src/lib/utils/systemStats.js +++ b/src/lib/utils/systemStats.js @@ -166,7 +166,6 @@ function preprocessProperty(contextData, property) { return property; } - module.exports = { /** * Render property based on template and conditionals diff --git a/src/lib/utils/timers.js b/src/lib/utils/timers.js index 45ef9b18..26fcf9fb 100644 --- a/src/lib/utils/timers.js +++ b/src/lib/utils/timers.js @@ -395,7 +395,7 @@ class SlidingInterval { * @returns {boolean} true if instance was disabled */ isDisabled() { - return !SlidingInterval.INSTANCES.some(si => si === this); + return !SlidingInterval.INSTANCES.some((si) => si === this); } /** @@ -418,7 +418,7 @@ class SlidingInterval { */ new Promise((resolve, reject) => Promise.resolve(this.func.apply(null, this.args)) .then(resolve, reject)) - .catch(error => (this.onError ? this.onError(error) : Promise.reject(error))) + .catch((error) => (this.onError ? this.onError(error) : Promise.reject(error))) .then(() => { const duration = Date.now() - startTime; if (duration > this.interval && this.onIntervalSlide) { @@ -442,8 +442,7 @@ SlidingInterval.INSTANCES = []; * * @returns {SlidingInterval | undefined} SlidingInterval instance or undefined if not found */ -SlidingInterval.findByRef = ref => SlidingInterval.INSTANCES.find(si => si.ref === ref); - +SlidingInterval.findByRef = (ref) => SlidingInterval.INSTANCES.find((si) => si.ref === ref); module.exports = { TimerInterface, diff --git a/src/lib/utils/tracer.js b/src/lib/utils/tracer.js index dacd968c..62516808 100644 --- a/src/lib/utils/tracer.js +++ b/src/lib/utils/tracer.js @@ -213,7 +213,7 @@ class Tracer { this.logger.debug(`Creating dir '${baseDir}'`); return util.fs.mkdir(baseDir); }) - .catch(mkdirError => (mkdirError.code === 'EEXIST' ? Promise.resolve() : Promise.reject(mkdirError))); + .catch((mkdirError) => (mkdirError.code === 'EEXIST' ? Promise.resolve() : Promise.reject(mkdirError))); } /** @@ -304,8 +304,8 @@ class Tracer { this._cacheReset = true; return this._mergeAndResetCache(readData); }) - .then(data => util.maskSecrets(util.stringify(data, true))) - .then(dataToWrite => util.fs.ftruncate(this.fd, 0) + .then((data) => util.maskSecrets(util.stringify(data, true))) + .then((dataToWrite) => util.fs.ftruncate(this.fd, 0) .then(() => util.fs.write(this.fd, dataToWrite, 0, this.encoding))) .catch((err) => { // close trace, lost data @@ -376,7 +376,7 @@ function fromConfig(config) { * @returns {Array} registered tracers */ function registered() { - return Object.keys(INSTANCES).map(key => INSTANCES[key]); + return Object.keys(INSTANCES).map((key) => INSTANCES[key]); } /** @@ -395,7 +395,7 @@ function unregister(tracer, catchErr) { // new tracer will be created if needed promise = promise.then(() => tracer.stop()); if (catchErr) { - promise = promise.catch(err => logger.debugException(`Uncaught error on attempt to unregister tracer for file '${tracer.path}'`, err)); + promise = promise.catch((err) => logger.debugException(`Uncaught error on attempt to unregister tracer for file '${tracer.path}'`, err)); } delete INSTANCES[tracer.path]; } @@ -408,11 +408,11 @@ function unregister(tracer, catchErr) { * @returns {Promise} resolved once all tracers registered */ function unregisterAll() { - return Promise.all(registered().map(tracer => unregister(tracer, true))); + return Promise.all(registered().map((tracer) => unregister(tracer, true))); } // config worker change event -configWorker.on('change', config => Promise.resolve() +configWorker.on('change', (config) => Promise.resolve() .then(() => { /** * This event might be handled by other listeners already @@ -436,9 +436,9 @@ configWorker.on('change', config => Promise.resolve() acc.push(component.traceInput); return acc; }, []) - .filter(traceConf => traceConf) - .map(traceConf => fromConfig(traceConf)) - .filter(tracer => tracer); + .filter((traceConf) => traceConf) + .map((traceConf) => fromConfig(traceConf)) + .filter((tracer) => tracer); registeredTracers.forEach((tracer) => { if (configuredTracers.indexOf(tracer) === -1) { diff --git a/src/nodejs/restWorker.js b/src/nodejs/restWorker.js index c9d1d200..61710d52 100644 --- a/src/nodejs/restWorker.js +++ b/src/nodejs/restWorker.js @@ -44,7 +44,6 @@ configListenerModulesToLoad.forEach((module) => { } }); - /** * Rest Worker class processes user's requests and responsible for * application state/stop. See more details on F5 DevCentral. diff --git a/src/schema/1.25.0/actions_schema.json b/src/schema/1.25.0/actions_schema.json new file mode 100644 index 00000000..c658439b --- /dev/null +++ b/src/schema/1.25.0/actions_schema.json @@ -0,0 +1,187 @@ +{ + "$id": "actions_schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Telemetry Streaming Actions schema", + "description": "", + "type": "object", + "definitions": { + "baseActionsChain": { + "title": "Chain of Actions", + "description": "Actions to be performed on the data.", + "type": "array", + "items": { + "$ref": "#/definitions/baseActionObject" + } + }, + "baseActionObject": { + "title": "Base Action object", + "description": "Base object to build actions.", + "type": "object", + "properties": { + "enable": { + "title": "Enable", + "description": "Whether to enable this action in the declaration or not.", + "type": "boolean", + "default": true + } + } + }, + "baseConditionalActionObject": { + "title": "Base Action object with support for conditional statements", + "description": "Base Action object with support for conditional statements.", + "type": "object", + "allOf": [ + { "$ref": "#/definitions/baseActionObject" }, + { + "anyOf": [ + { + "properties": { + "ifAllMatch": { + "title": "If All Match", + "description": "The conditions that will be checked against. All must be true.", + "type": "object", + "additionalProperties": true + } + }, + "not": { "required": ["ifAnyMatch"] } + }, + { + "properties": { + "ifAnyMatch": { + "title": "If Any Match", + "description": "An array of ifAllMatch objects. Any individual ifAllMatch object may match, but each condition within an ifAllMatch object must be true", + "type": "array" + } + }, + "not": { "required": ["ifAllMatch"] } + } + ] + } + ] + }, + "subLocation": { + "title": "Location", + "description": "Used to specify a location in TS data. Use boolean type with value true to specify the location.", + "oneOf": [ + { + "type": "boolean", + "const": true + }, + { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/subLocation" + } + } + ] + }, + "locations": { + "title": "Location", + "description": "The location(s) to apply the action.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/subLocation" + } + }, + "setTagAction": { + "title": "setTag Action", + "description": "Action to assign a tag(s) to particular or default location", + "type": "object", + "allOf": [ + { "$ref": "#/definitions/baseConditionalActionObject" }, + { + "properties": { + "setTag": { + "title": "Set Tag", + "description": "The tag values to be added.", + "type": "object", + "additionalProperties": true + }, + "locations": { + "title": "Location", + "description": "The location(s) to apply the action.", + "allOf": [{ "$ref": "#/definitions/locations" }] + }, + "enable": {}, + "ifAllMatch": {}, + "ifAnyMatch": {} + }, + "additionalProperties": false, + "required": ["setTag"] + } + ] + }, + "includeDataAction": { + "title": "includeData Action", + "description": "Action to specify data fields to include in the output", + "type": "object", + "allOf": [ + { "$ref": "#/definitions/baseConditionalActionObject" }, + { + "properties": { + "includeData": { + "title": "Include Data", + "description": "The data fields to include in the output", + "type": "object", + "additionalProperties": false + }, + "locations": { + "title": "Location", + "description": "The location(s) to apply the action.", + "allOf": [{ "$ref": "#/definitions/locations" }] + }, + "enable": {}, + "ifAllMatch": {}, + "ifAnyMatch": {} + }, + "additionalProperties": false, + "required": ["includeData", "locations"] + } + ] + }, + "excludeDataAction": { + "title": "excludeData Action", + "description": "Action to specify data fields to exclude form the output", + "type": "object", + "allOf": [ + { "$ref": "#/definitions/baseConditionalActionObject" }, + { + "properties": { + "excludeData": { + "title": "Exclude Data", + "description": "The data fields to exclude from the output", + "type": "object", + "additionalProperties": false + }, + "locations": { + "title": "Location", + "description": "The location(s) to apply the action.", + "allOf": [{ "$ref": "#/definitions/locations" }] + }, + "enable": {}, + "ifAllMatch": {}, + "ifAnyMatch": {} + }, + "additionalProperties": false, + "required": ["excludeData", "locations"] + } + ] + }, + "inputDataStreamActionsChain": { + "title": "", + "description": "", + "allOf": [ + { "$ref": "#/definitions/baseActionsChain" }, + { + "items": { + "oneOf": [ + { "$ref": "#/definitions/excludeDataAction" }, + { "$ref": "#/definitions/includeDataAction" }, + { "$ref": "#/definitions/setTagAction" } + ] + } + } + ] + } + } +} \ No newline at end of file diff --git a/src/schema/1.25.0/base_schema.json b/src/schema/1.25.0/base_schema.json new file mode 100644 index 00000000..5b6b4904 --- /dev/null +++ b/src/schema/1.25.0/base_schema.json @@ -0,0 +1,310 @@ +{ + "$id": "base_schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Telemetry Streaming", + "description": "", + "type": "object", + "definitions": { + "enable": { + "title": "Enable", + "description": "This property can be used to enable/disable the poller/listener" , + "type": "boolean" + }, + "trace": { + "title": "Trace", + "description": "Enables data dumping to file. Boolean uses pre-defined file location, however value could be a string which contains path to a specific file instead" , + "minLength": 1, + "type": ["boolean", "string"] + }, + "traceConfig": { + "title": "Trace (v2)", + "description": "Enables data dumping to file. Boolean uses pre-defined file location, however value could be a string which contains path to a specific file instead", + "type": "object", + "properties": { + "type": { + "title": "Trace type", + "description": "Trace type - output data or input data", + "type": "string", + "enum": ["output", "input"] + }, + "path": { + "title": "Path to trace file", + "description": "Path to trace file to write data to", + "type": "string", + "minLength": 1 + } + }, + "required": ["type"] + }, + "traceV2": { + "title": "Trace (v2)", + "description": "Enables data dumping to file. Boolean uses pre-defined file location, however value could be a string which contains path to a specific file instead", + "oneOf": [ + { "$ref": "#/definitions/traceConfig" }, + { + "type": "array", + "minItems": 1, + "maxItems": 2, + "uniqueItemProperties": ["type"], + "items": { + "allOf": [{ + "$ref": "#/definitions/traceConfig" + }] + } + } + ] + }, + "secret": { + "title": "Passphrase (secret)", + "description": "" , + "type": "object", + "properties": { + "class": { + "title": "Class", + "description": "Telemetry streaming secret class", + "type": "string", + "enum": [ "Secret" ], + "default": "Secret" + }, + "cipherText": { + "title": "Cipher Text: this contains a secret to encrypt", + "type": "string" + }, + "environmentVar": { + "title": "Environment Variable: this contains the named env var where the secret resides", + "type": "string", + "minLength": 1 + }, + "protected": { + "$comment": "Meta property primarily used to determine if 'cipherText' needs to be encrypted", + "title": "Protected", + "type": "string", + "enum": [ "plainText", "plainBase64", "SecureVault" ], + "default": "plainText" + } + }, + "oneOf": [ + { "required": [ "cipherText" ] }, + { "required": [ "environmentVar" ] } + ], + "f5secret": true + }, + "username": { + "$comment": "Common field for username to use everywhere in scheme", + "title": "Username", + "type": "string", + "minLength": 1 + }, + "stringOrSecret": { + "allOf": [ + { + "if": { "type": "string" }, + "then": {}, + "else": {} + }, + { + "if": { "type": "object" }, + "then": { "$ref": "base_schema.json#/definitions/secret" }, + "else": {} + } + ] + }, + "constants": { + "title": "Constants", + "description": "" , + "type": "object", + "properties": { + "class": { + "title": "Class", + "description": "Telemetry streaming constants class", + "type": "string", + "enum": [ "Constants" ] + } + }, + "additionalProperties": true + }, + "tag": { + "$comment": "Defaults do not get applied for $ref objects, so place defaults alongside instead.", + "title": "Tag", + "description": "" , + "type": "object", + "properties": { + "tenant": { + "title": "Tenant tag", + "type": "string", + "minLength": 1 + }, + "application": { + "title": "Application tag", + "type": "string", + "minLength": 1 + } + }, + "additionalProperties": true + }, + "match": { + "$comment": "Defaults do not get applied for $ref objects, so place defaults alongside instead.", + "title": "Pattern to filter data", + "description": "", + "type": "string" + }, + "enableHostConnectivityCheck": { + "$comment": "This property can be used to enable/disable the host connectivity check in configurations where this is in effect", + "title": "Host", + "description": "" , + "type": "boolean" + }, + "allowSelfSignedCert": { + "$comment": "This property can be used by consumers, system pollers to enable/disable SSL Cert check", + "title": "Allow Self-Signed Certificate", + "description": "" , + "type": "boolean" + }, + "host": { + "$comment": "This property can be used by consumers, system pollers", + "title": "Host", + "description": "" , + "type": "string", + "minLength": 1, + "anyOf": [ + { "format": "ipv4" }, + { "format": "ipv6" }, + { "format": "hostname" } + ], + "hostConnectivityCheck": true + }, + "port": { + "title": "Port", + "description": "" , + "type": "integer", + "minimum": 0, + "maximum": 65535 + }, + "protocol": { + "title": "Protocol", + "description": "" , + "type": "string", + "enum": [ "http", "https" ] + }, + "proxy": { + "title": "Proxy Configuration", + "description": "", + "type": "object", + "dependencies": { + "passphrase": [ "username" ] + }, + "required": [ "host" ], + "properties": { + "host": { + "$ref": "#/definitions/host" + }, + "port": { + "default": 80, + "allOf": [ + { + "$ref": "#/definitions/port" + } + ] + }, + "protocol": { + "default": "http", + "allOf": [ + { + "$ref": "#/definitions/protocol" + } + ] + }, + "enableHostConnectivityCheck": { + "$ref": "#/definitions/enableHostConnectivityCheck" + }, + "allowSelfSignedCert": { + "$ref": "#/definitions/allowSelfSignedCert" + }, + "username": { + "$ref": "#/definitions/username" + }, + "passphrase": { + "$ref": "#/definitions/secret" + } + }, + "additionalProperties": false + } + }, + "properties": { + "class": { + "title": "Class", + "description": "Telemetry streaming top level class", + "type": "string", + "enum": [ "Telemetry" ] + }, + "schemaVersion": { + "title": "Schema version", + "description": "Version of ADC Declaration schema this declaration uses", + "type": "string", + "$comment": "IMPORTANT: In enum array, please put current schema version first, oldest-supported version last. Keep enum array sorted most-recent-first.", + "enum": [ "1.25.0", "1.24.0", "1.23.0", "1.22.0", "1.21.0", "1.20.1", "1.20.0", "1.19.0", "1.18.0", "1.17.0", "1.16.0", "1.15.0", "1.14.0", "1.13.0", "1.12.0", "1.11.0", "1.10.0", "1.9.0", "1.8.0", "1.7.0", "1.6.0", "1.5.0", "1.4.0", "1.3.0", "1.2.0", "1.1.0", "1.0.0", "0.9.0" ], + "default": "1.25.0" + }, + "$schema": { + "title": "Schema", + "description": "", + "type": "string" + } + }, + "additionalProperties": { + "$comment": "AJV does not resolve defaults inside oneOf/anyOf, so instead use allOf. Any schema refs should also use allOf with an if/then/else on class", + "properties": { + "class": { + "title": "Class", + "type": "string", + "enum": [ + "Telemetry_System", + "Telemetry_System_Poller", + "Telemetry_Listener", + "Telemetry_Consumer", + "Telemetry_Pull_Consumer", + "Telemetry_iHealth_Poller", + "Telemetry_Endpoints", + "Telemetry_Namespace", + "Controls", + "Shared" + ] + } + }, + "allOf": [ + { + "$ref": "system_schema.json#" + }, + { + "$ref": "system_poller_schema.json#" + }, + { + "$ref": "listener_schema.json#" + }, + { + "$ref": "consumer_schema.json#" + }, + { + "$ref": "pull_consumer_schema.json#" + }, + { + "$ref": "ihealth_poller_schema.json#" + }, + { + "$ref": "endpoints_schema.json#" + }, + { + "$ref": "controls_schema.json#" + }, + { + "$ref": "shared_schema.json#" + }, + { + "$ref": "namespace_schema.json#" + } + ] + }, + "required": [ + "class" + ] +} diff --git a/src/schema/1.25.0/consumer_schema.json b/src/schema/1.25.0/consumer_schema.json new file mode 100644 index 00000000..2bbe0818 --- /dev/null +++ b/src/schema/1.25.0/consumer_schema.json @@ -0,0 +1,1357 @@ +{ + "$id": "consumer_schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Telemetry Streaming Consumer schema", + "description": "", + "type": "object", + "definitions": { + "jmesPathAction": { + "title": "JMESPath Action", + "description": "Will use a JMESPath expression to modify the incoming data payload", + "type": "object", + "allOf": [ + { "$ref": "actions_schema.json#/definitions/baseActionObject" }, + { + "properties": { + "JMESPath": { + "title": "JMESPath", + "description": "Will use a JMESPath expression to modify the incoming data payload", + "type": "object", + "additionalProperties": false + }, + "expression": { + "title": "Expression", + "description": "The JMESPath expression to be applied to the incoming data payload", + "type": "string", + "minLength": 1 + }, + "enable": {} + }, + "additionalProperties": false, + "required": ["JMESPath", "expression"] + } + ] + }, + "autoTaggingStatsd": { + "title": "Statsd auto tagging", + "description": "Will parse incoming payload for values to automatically add as tags.", + "type": "object", + "properties": { + "method": { + "title": "AutoTagging method", + "description": "AutoTagging method to use to fetch tags", + "type": "string", + "enum": ["sibling"] + } + }, + "additionalProperties": false, + "required": ["method"] + }, + "genericHttpActions": { + "title": "Actions", + "description": "Actions to be performed on the Generic HTTP Consumer.", + "allOf": [ + { "$ref": "actions_schema.json#/definitions/baseActionsChain" }, + { + "items": { + "oneOf": [ + { "$ref": "#/definitions/jmesPathAction" } + ] + } + } + ] + }, + "host": { + "$comment": "Required for certain consumers: standard property", + "title": "Host", + "description": "FQDN or IP address" , + "type": "string", + "minLength": 1, + "anyOf": [ + { "format": "ipv4" }, + { "format": "ipv6" }, + { "format": "hostname" } + ], + "hostConnectivityCheck": true + }, + "protocols": { + "$comment": "Required for certain consumers: standard property", + "title": "Protocols (all)", + "description": "" , + "type": "string", + "enum": [ "https", "http", "tcp", "udp", "binaryTcpTls", "binaryTcp" ] + }, + "port": { + "$comment": "Required for certain consumers: standard property", + "title": "Port", + "description": "" , + "type": "integer", + "minimum": 0, + "maximum": 65535 + }, + "path": { + "$comment": "Required for certain consumers: standard property", + "title": "Path", + "description": "Path to post data to", + "type": ["string", "object"], + "minLength": 1, + "f5expand": true, + "allOf": [ + { + "$ref": "base_schema.json#/definitions/stringOrSecret" + } + ] + }, + "method": { + "$comment": "Required for certain consumers: standard property", + "title": "Method", + "description": "HTTP method to use (limited to sensical choices)" , + "type": "string", + "enum": [ "POST", "GET", "PUT" ] + }, + "headers": { + "$comment": "Required for certain consumers: standard property", + "title": "Headers", + "description": "HTTP headers to use" , + "type": "array", + "items": { + "properties": { + "name": { + "description": "Name of this header", + "type": "string", + "f5expand": true, + "minLength": 1 + }, + "value": { + "description": "Value of this header", + "type": ["string", "object"], + "f5expand": true, + "allOf": [ + { + "$ref": "base_schema.json#/definitions/stringOrSecret" + } + ] + } + }, + "required": [ + "name", + "value" + ], + "additionalProperties": false + } + }, + "customOpts": { + "$comment": "Required for certain consumers: standard property", + "title": "Custom Opts (Client Library Dependent)", + "description": "Additional options for use by consumer client library. Refer to corresponding consumer lib documentation for acceptable keys and values." , + "type": "array", + "items": { + "properties": { + "name": { + "description": "Name of the option", + "type": "string", + "f5expand": true, + "minLength": 1 + }, + "value": { + "description": "Value of the option", + "minLength": 1, + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "number" + }, + { + "allOf": [ + { + "f5expand": true + }, + { + "$ref": "base_schema.json#/definitions/stringOrSecret" + } + ] + } + ] + } + }, + "required": [ + "name", + "value" + ], + "additionalProperties": false + }, + "minItems": 1 + }, + "format": { + "$comment": "Required for certain consumers: Splunk and Azure_Log_Analytics", + "title": "Format (informs consumer additional formatting may be required)", + "description": "", + "type": "string" + }, + "username": { + "$comment": "Required for certain consumers: standard property", + "title": "Username", + "description": "" , + "minLength": 1, + "type": "string", + "f5expand": true + }, + "region": { + "$comment": "Required for certain consumers: AWS_CloudWatch, AWS_S3, Azure_Log_Analytics, Azure_App_Insights, DataDog", + "title": "Region", + "description": "" , + "type": "string", + "minLength": 1, + "f5expand": true + }, + "endpointUrl": { + "$comment": "Required for certain consumers: AWS_CloudWatch, AWS_S3", + "title": "endpoint url", + "description": "The full endpoint URL for service requests", + "type": "string", + "minLength": 1, + "f5expand": true + }, + "bucket": { + "$comment": "Required for certain consumers: AWS_S3", + "title": "Bucket", + "description": "" , + "type": "string", + "minLength": 1, + "f5expand": true + }, + "logGroup": { + "$comment": "Required for certain consumers: AWS_CloudWatch", + "title": "Log Group", + "description": "" , + "type": "string", + "minLength": 1, + "f5expand": true + }, + "logStream": { + "$comment": "Required for certain consumers: AWS_CloudWatch", + "title": "Log Stream", + "description": "" , + "type": "string", + "minLength": 1, + "f5expand": true + }, + "metricNamespace": { + "$comment": "Required for certain consumers: AWS_CloudWatch", + "title": "Metric Namespace", + "description": "The namespace for the metrics", + "type": "string", + "f5expand": true, + "minLength": 1 + }, + "metricPrefix": { + "$comment": "Required for certain consumers: DataDog", + "title": "Metric Prefix", + "description": "The string value(s) to use as a metric prefix", + "type": "array", + "minItems": 1, + "items": { + "allOf": [{ + "type": "string", + "f5expand": true, + "minLength": 1 + }] + } + }, + "workspaceId": { + "$comment": "Required for certain consumers: Azure_Log_Analytics", + "title": "Workspace ID", + "description": "" , + "type": "string", + "minLength": 1, + "f5expand": true + }, + "useManagedIdentity": { + "$comment": "Required for certain consumers: Azure_Log_Analytics and Azure_Application_Insights", + "title": "Use Managed Identity", + "description": "Determines whether to use Managed Identity to perform authorization for Azure services", + "type": "boolean", + "default": false + }, + "appInsightsResourceName": { + "$comment": "Required for certain consumers: Azure_Application_Insights", + "title": "Application Insights Resource Name (Pattern)", + "description": "Name filter used to determine which App Insights resource to send metrics to. If not provided, TS will send metrics to App Insights in the subscription in which the managed identity has permissions to", + "type": "string", + "minLength": 1 + }, + "instrumentationKey": { + "$comment": "Required for certain consumers: Azure_Application_Insights", + "title": "Instrumentation Key", + "description": "Used to determine which App Insights resource to send metrics to", + "anyOf": [ + { + "type": "string", + "f5expand": true, + "minLength": 1 + }, + { + "type":"array", + "items": { + "type": "string", + "f5expand": true, + "minLength": 1 + }, + "minItems": 1 + } + ] + }, + "maxBatchIntervalMs": { + "$comment": "Required for certain consumers: Azure_Application_Insights", + "title": "Maximum Batch Interval (ms)", + "description": "The maximum amount of time to wait in milliseconds to for payload to reach maxBatchSize", + "type": "integer", + "minimum": 1000, + "default": 5000 + }, + "maxBatchSize": { + "$comment": "Required for certain consumers: Azure_Application_Insights", + "title": "Maximum Batch Size", + "description": "The maximum number of telemetry items to include in a payload to the ingestion endpoint", + "type": "integer", + "minimum": 1, + "default": 250 + }, + "topic": { + "$comment": "Required for certain consumers: Kafka", + "title": "Topic", + "description": "" , + "type": "string", + "f5expand": true + }, + "index": { + "$comment": "Required for certain consumers: ElasticSearch", + "title": "Index Name", + "description": "" , + "type": "string", + "minLength": 1, + "f5expand": true + }, + "apiVersion": { + "$comment": "Required for certain consumers: ElasticSearch", + "title": "API Version", + "description": "" , + "type": "string", + "minLength": 1, + "f5expand": true + }, + "dataType": { + "$comment": "Required for certain consumers: AWS_CloudWatch, ElasticSearch", + "title": "Data type", + "description": "" , + "type": "string", + "f5expand": true + }, + "authenticationProtocol": { + "$comment": "Required for certain consumers: Kafka", + "title": "Authentication Protocol", + "description": "" , + "type": "string", + "f5expand": true, + "enum": [ + "SASL-PLAIN", + "TLS", + "None" + ] + }, + "clientCertificate": { + "$comment": "Required for certain consumers: Kafka, Generic HTTP", + "title": "Client Certificate", + "description": "Certificate(s) to use when connecting to a secured endpoint.", + "type": "object", + "f5expand": true, + "allOf": [ + { + "$ref": "base_schema.json#/definitions/secret" + } + ] + }, + "rootCertificate": { + "$comment": "Required for certain consumers: Kafka, Generic HTTP", + "title": "Root Certificate", + "description": "Certificate Authority root certificate, used to validate certificate chains.", + "type": "object", + "f5expand": true, + "allOf": [ + { + "$ref": "base_schema.json#/definitions/secret" + } + ] + }, + "projectId": { + "$comment": "Required for certain consumers: Google_Cloud_Monitoring", + "title": "Project ID", + "description": "The ID of the relevant project.", + "type": "string", + "minLength": 1, + "f5expand": true + }, + "serviceEmail": { + "$comment": "Required for certain consumers: Google_Cloud_Monitoring, Google_Cloud_Logging", + "title": "Service Email", + "description": "The service email.", + "type": "string", + "minLength": 1, + "f5expand": true + }, + "privateKeyId": { + "$comment": "Required for certain consumers when Service Account Token is not used: Google_Cloud_Monitoring, Google_Cloud_Logging", + "title": "Private Key ID", + "description": "The private key ID.", + "type": "string", + "minLength": 1, + "f5expand": true + }, + "useServiceAccountToken": { + "$comment": "Used by certain consumers: Google_Cloud_Monitoring, Google_Cloud_Logging", + "title": "Use Service Account Token", + "description": "Determines whether to use Service Account Token to perform authorization for Google services", + "type": "boolean", + "default": false + }, + "logScope": { + "$comment": "Required for certain consumers: Google_Cloud_Logging", + "title": "Logging Scope Type", + "description": "" , + "enum": ["projects", "organizations", "billingAccounts", "folders"], + "f5expand": true + }, + "logScopeId": { + "$comment": "Required for certain consumers: Google_Cloud_Logging", + "title": "Logging Scope ID", + "description": "" , + "type": "string", + "minLength": 1, + "f5expand": true + }, + "logId": { + "$comment": "Required for certain consumers: Google_Cloud_Logging", + "title": "Logging ID", + "description": "" , + "type": "string", + "format": "regex", + "pattern": "^[a-zA-z0-9._-]+$", + "minLength": 1, + "f5expand": true + }, + "privateKey": { + "$comment": "Required for certain consumers: Kafka, Generic HTTP", + "title": "Private Key", + "description": "Private Key", + "type": "object", + "f5expand": true, + "allOf": [ + { + "$ref": "base_schema.json#/definitions/secret" + } + ] + }, + "eventSchemaVersion": { + "$comment": "Required for certain consumers: F5_Cloud", + "title": "Event Schema Version", + "description": "" , + "type": "string", + "minLength": 1, + "f5expand": true, + "default": "1" + }, + "f5csTenantId": { + "$comment": "Required for certain consumers: F5_Cloud", + "title": "F5CS Tenant ID", + "description": "" , + "type": "string", + "minLength": 1, + "f5expand": true + }, + "f5csSensorId": { + "$comment": "Required for certain consumers: F5_Cloud", + "title": "F5CS Sensor ID", + "description": "" , + "type": "string", + "minLength": 1, + "f5expand": true + }, + "payloadSchemaNid": { + "$comment": "Required for certain consumers: F5_Cloud", + "title": "Namespace ID for payloadSchema", + "description": "" , + "type": "string", + "minLength": 1, + "f5expand": true + }, + "serviceAccount": { + "$comment": "Required for certain consumers: F5_Cloud", + "title": "Service Account", + "description": "Service Account to authentication" , + "type": "object", + "properties": { + "authType": { + "$comment": "Required for certain consumers: F5_Cloud", + "title": "SA Type", + "description": "" , + "type": "string", + "enum": ["google-auth" ] + }, + "type": { + "$comment": "Required for certain consumers: F5_Cloud", + "title": "SA Type", + "description": "" , + "type": "string", + "minLength": 1, + "f5expand": true + }, + "projectId": { + "$comment": "Required for certain consumers: F5_Cloud", + "title": "Project Id", + "description": "" , + "type": "string", + "minLength": 1, + "f5expand": true + }, + "privateKeyId": { + "$comment": "Required for certain consumers: F5_Cloud", + "title": "Private Key Id", + "description": "" , + "type": "string", + "minLength": 1, + "f5expand": true + }, + "privateKey": { + "$ref": "base_schema.json#/definitions/secret" + }, + "clientEmail": { + "$comment": "Required for certain consumers: F5_Cloud", + "title": "Client Email", + "description": "" , + "type": "string", + "minLength": 1, + "f5expand": true + }, + "clientId": { + "$comment": "Required for certain consumers: F5_Cloud", + "title": "Client Id", + "description": "" , + "type": "string", + "minLength": 1, + "f5expand": true + }, + "authUri": { + "$comment": "Required for certain consumers: F5_Cloud", + "title": "Auth Uri", + "description": "" , + "type": "string", + "minLength": 1, + "f5expand": true + }, + "tokenUri": { + "$comment": "Required for certain consumers: F5_Cloud", + "title": "Token Uri", + "description": "" , + "type": "string", + "minLength": 1, + "f5expand": true + }, + "authProviderX509CertUrl": { + "$comment": "Required for certain consumers: F5_Cloud", + "title": "Auth Provider X509 Cert Url", + "description": "" , + "type": "string", + "minLength": 1, + "f5expand": true + }, + "clientX509CertUrl": { + "$comment": "Required for certain consumers: F5_Cloud", + "title": "Client X509 Cert Url", + "description": "" , + "type": "string", + "minLength": 1, + "f5expand": true + } + }, + "additionalProperties": false, + "allOf": [ + { + "if": { "properties": { "authType": { "const": "google-auth" } } }, + "then": { + "required": [ + "type", + "projectId", + "privateKeyId", + "privateKey", + "clientEmail", + "clientId", + "authUri", + "tokenUri", + "authProviderX509CertUrl", + "clientX509CertUrl" + ] + }, + "else": {} + }] + }, + "targetAudience": { + "$comment": "Required for certain consumers: F5_Cloud", + "title": "Target Audience", + "description": "" , + "type": "string", + "minLength": 1, + "f5expand": true + }, + "useSSL": { + "$comment": "Required for certain consumers: F5_Cloud", + "title": "useSSL", + "description": "To decide if GRPC connection should use SSL and then it is secured" , + "type": "boolean", + "f5expand": true + }, + "compressionType": { + "$comment": "Required for certain consumers: DataDog, Splunk", + "title": "Data compression", + "description": "Whether or not to compress data and what compression to use before sending it to destination", + "type": "string", + "enum": ["none", "gzip"] + }, + "reportInstanceMetadata": { + "$comment": "Required for certain consumers: Google_Cloud_Monitoring, Google_Cloud_Logging", + "title": "Instance metadata reporting", + "description": "Enables instance metadata collection and reporting" , + "type": "boolean", + "f5expand": true + }, + "apiKey": { + "$comment": "Required for certain consumers: DataDog", + "title": "API key to use to push data", + "type": "string", + "minLength": 1, + "f5expand": true + }, + "service": { + "$comment": "Required for certain consumers: DataDog", + "title": "The name of the service generating telemetry data", + "type": "string", + "minLength": 1, + "f5expand": true + }, + "convertBooleansToMetrics": { + "$comment": "Required for certain consumers: DataDog, Statsd, OpenTelemetry_Exporter", + "title": "Convert boolean values to metrics", + "description": "Whether or not to convert boolean values to metrics. True becomes 1, False becomes 0" , + "type": "boolean", + "f5expand": true, + "default": false + }, + "customTags": { + "$comment": "Required for certain consumers: DataDog", + "title": "Custom tags", + "description": "A collection of custom tags that are appended to the dynamically generated telemetry tags", + "type": "array", + "minItems": 1, + "items": { + "properties": { + "name": { + "description": "Name of this tag", + "type": "string", + "f5expand": true, + "minLength": 1 + }, + "value": { + "description": "Value of this tag", + "type": "string", + "f5expand": true, + "minLength": 1 + } + }, + "additionalProperties": false + } + } + }, + "allOf": [ + { + "if": { "properties": { "class": { "const": "Telemetry_Consumer" } } }, + "then": { + "required": [ + "class", + "type" + ], + "properties": { + "class": { + "title": "Class", + "description": "Telemetry Streaming Consumer class", + "type": "string", + "enum": [ "Telemetry_Consumer" ] + }, + "enable": { + "default": true, + "allOf": [ + { + "$ref": "base_schema.json#/definitions/enable" + } + ] + }, + "trace": { + "default": false, + "allOf": [ + { + "$ref": "base_schema.json#/definitions/trace" + } + ] + }, + "type": { + "title": "Type", + "description": "" , + "type": "string", + "enum": [ + "AWS_CloudWatch", + "AWS_S3", + "Azure_Log_Analytics", + "Azure_Application_Insights", + "DataDog", + "default", + "ElasticSearch", + "Generic_HTTP", + "Google_Cloud_Logging", + "Google_Cloud_Monitoring", + "Google_StackDriver", + "Graphite", + "Kafka", + "OpenTelemetry_Exporter", + "Splunk", + "Statsd", + "Sumo_Logic", + "F5_Cloud" + ] + }, + "enableHostConnectivityCheck": { + "$ref": "base_schema.json#/definitions/enableHostConnectivityCheck" + }, + "allowSelfSignedCert": { + "default": false, + "allOf": [ + { + "$ref": "base_schema.json#/definitions/allowSelfSignedCert" + } + ] + } + }, + "allOf": [ + { + "$comment": "This allows enforcement of no additional properties in this nested schema - could reuse above properties but prefer a separate block", + "properties": { + "addTags": {}, + "actions": {}, + "apiKey": {}, + "class": {}, + "customTags": {}, + "enable": {}, + "trace": {}, + "type": {}, + "enableHostConnectivityCheck": {}, + "allowSelfSignedCert": {}, + "host": {}, + "protocol": {}, + "port": {}, + "path": {}, + "method": {}, + "headers": {}, + "customOpts": {}, + "username": {}, + "passphrase": {}, + "format": {}, + "workspaceId": {}, + "useManagedIdentity": {}, + "instrumentationKey": {}, + "appInsightsResourceName": {}, + "maxBatchIntervalMs": {}, + "maxBatchSize": {}, + "region": {}, + "endpointUrl": {}, + "logGroup": {}, + "logStream": {}, + "metricNamespace": {}, + "metricPrefix": {}, + "bucket": {}, + "topic": {}, + "apiVersion": {}, + "index": {}, + "dataType": {}, + "authenticationProtocol": {}, + "projectId": {}, + "serviceEmail": {}, + "privateKey": {}, + "privateKeyId": {}, + "useServiceAccountToken": {}, + "clientCertificate": {}, + "rootCertificate": {}, + "fallbackHosts": {}, + "eventSchemaVersion": {}, + "f5csTenantId": {}, + "f5csSensorId": {}, + "payloadSchemaNid": {}, + "serviceAccount": {}, + "targetAudience": {}, + "useSSL": {}, + "proxy": {}, + "compressionType": {}, + "logScope": {}, + "logScopeId": {}, + "logId": {}, + "reportInstanceMetadata": {}, + "metricsPath": {}, + "service": {}, + "convertBooleansToMetrics": {} + }, + "additionalProperties": false, + "dependencies": { + "actions": { + "allOf": [ + { + "properties": { "type": { "const": "Generic_HTTP" } } + } + ] + } + } + }, + { + "if": { "properties": { "type": { "const": "default" } } }, + "then": { + "required": [], + "properties": {} + }, + "else": {} + }, + { + "if": { "properties": { "type": { "const": "Generic_HTTP" } } }, + "then": { + "required": [ + "host" + ], + "properties": { + "host": { "$ref": "#/definitions/host" }, + "fallbackHosts": { + "type": "array", + "description": "List FQDNs or IP addresses to be used as fallback hosts" , + "minItems": 1, + "items": { + "allOf": [{ + "$ref": "#/definitions/host" + }] + } + }, + "protocol": { "$ref": "#/definitions/protocols", "default": "https" }, + "port": { "$ref": "#/definitions/port", "default": 443 }, + "path": { "$ref": "#/definitions/path", "default": "/" }, + "method": { "$ref": "#/definitions/method", "default": "POST" }, + "headers": { "$ref": "#/definitions/headers" }, + "passphrase": { "$ref": "base_schema.json#/definitions/secret" }, + "proxy": { "$ref": "base_schema.json#/definitions/proxy" }, + "privateKey": { "$ref": "#/definitions/privateKey" }, + "clientCertificate": { "$ref": "#/definitions/clientCertificate" }, + "rootCertificate": { "$ref": "#/definitions/rootCertificate" }, + "actions": { "$ref": "#/definitions/genericHttpActions" } + }, + "allOf": [ + { + "if": { "required": [ "clientCertificate" ] }, + "then": { "required": [ "privateKey" ] } + }, + { + "if": { "required": [ "privateKey" ] }, + "then": { "required": [ "clientCertificate" ] } + } + ] + }, + "else": {} + }, + { + "if": { "properties": { "type": { "const": "Splunk" } } }, + "then": { + "required": [ + "host", + "passphrase" + ], + "properties": { + "host": { "$ref": "#/definitions/host" }, + "protocol": { "$ref": "#/definitions/protocols", "default": "https" }, + "port": { "$ref": "#/definitions/port", "default": 8088 }, + "passphrase": { "$ref": "base_schema.json#/definitions/secret" }, + "format": { "$ref": "#/definitions/format", "enum": [ "default", "legacy", "multiMetric" ], "default": "default" }, + "proxy": { "$ref": "base_schema.json#/definitions/proxy" }, + "compressionType": { "$ref": "#/definitions/compressionType", "default": "gzip" } + } + }, + "else": {} + }, + { + "if": { "properties": { "type": { "const": "Azure_Log_Analytics" } } }, + "then": { + "required": [ + "workspaceId" + ], + "properties": { + "workspaceId": { "$ref": "#/definitions/workspaceId" }, + "format": { "$ref": "#/definitions/format", "enum": [ "default", "propertyBased" ], "default": "default" }, + "passphrase": { "$ref": "base_schema.json#/definitions/secret" }, + "useManagedIdentity": { "$ref": "#/definitions/useManagedIdentity", "default": false }, + "region": { "$ref": "#/definitions/region" } + }, + "allOf": [ + { + "dependencies": { + "passphrase": { + "anyOf": [ + { "not": {"required": [ "useManagedIdentity" ] } }, + { "properties": { "useManagedIdentity": { "const": false } } } + ] + } + } + }, + { + "if": { "not": { "required" : [ "useManagedIdentity"] } }, + "then": { "required": ["passphrase"] }, + "else": { + "if": { "properties": { "useManagedIdentity": { "const": true } } }, + "then": { "not": { "required": ["passphrase"] } }, + "else": { "required": ["passphrase"]} + } + } + ] + }, + "else": {} + }, + { + "if": { "properties": { "type": { "const": "Azure_Application_Insights" } } }, + "then": { + "properties": { + "instrumentationKey": { "$ref": "#/definitions/instrumentationKey" }, + "maxBatchSize": { "$ref": "#/definitions/maxBatchSize", "default": 250 }, + "maxBatchIntervalMs": { "$ref": "#/definitions/maxBatchIntervalMs", "default": 5000 }, + "customOpts": { "$ref": "#/definitions/customOpts" }, + "useManagedIdentity": { "$ref": "#/definitions/useManagedIdentity", "default": false }, + "appInsightsResourceName": { "$ref": "#/definitions/appInsightsResourceName" }, + "region": { "$ref": "#/definitions/region" } + }, + "allOf": [ + { + "dependencies": { + "instrumentationKey": { + "allOf": [ + { + "anyOf": [ + { "not": { "required": [ "useManagedIdentity" ] } }, + { "properties": { "useManagedIdentity": { "const": false } } } + ] + }, + { + "not": { "required": ["appInsightsResourceName"] } + } + ] + } + } + }, + { + "if": { "not": { "required" : [ "useManagedIdentity"] } }, + "then": { "required": ["instrumentationKey"] }, + "else": { + "if": { "properties": { "useManagedIdentity": { "const": true } } }, + "then": { "not": { "required": ["instrumentationKey"] } }, + "else": { + "allOf": [ + { "required": [ "instrumentationKey" ]}, + { "not": { "required": [ "appInsightsResourceName" ] } } + ] + } + } + }, + { + "if": { "required": [ "appInsightsResourceName" ] }, + "then": { "properties": { "appInsightsResourceName": { "minLength": 1 } }} + } + ] + }, + "else": {} + }, + { + "if": { "properties": { "type": { "const": "AWS_CloudWatch" } } }, + "then": { + "required": [ + "region", + "dataType" + ], + "properties": { + "region": { "$ref": "#/definitions/region" }, + "dataType": { "$ref": "#/definitions/dataType", "default": "logs" }, + "username": { "$ref": "#/definitions/username" }, + "passphrase": { "$ref": "base_schema.json#/definitions/secret" }, + "endpointUrl": { "$ref": "#/definitions/endpointUrl" } + }, + "allOf": [ + { "not": { "required": ["username"], "not": { "required": ["passphrase"] }}}, + { "not": { "required": ["passphrase"], "not": { "required": ["username"] }}}, + { "oneOf": + [ + { + "allOf": [ + { + "properties": { + "logGroup": { "$ref": "#/definitions/logGroup" }, + "logStream": { "$ref": "#/definitions/logStream" }, + "dataType": { + "allOf": + [ + { "$ref": "#/definitions/dataType"}, + { "enum": ["logs", null] } + ] + } + } + }, + { "required":[ "logGroup", "logStream" ] }, + { "not": { "required": ["metricNamespace"] }} + ] + }, + { + "allOf": [ + { + "properties": { + "metricNamespace": { "$ref": "#/definitions/metricNamespace" }, + "dataType": { + "allOf": [ + { "$ref": "#/definitions/dataType"}, + { "enum": ["metrics"] } + ] + } + } + }, + { "required":[ "metricNamespace" ] }, + { "not": { "required":[ "logStream" ] }}, + { "not": { "required": [ "logGroup" ] }} + ] + } + ] + } + ] + }, + "else": {} + }, + { + "if": { "properties": { "type": { "const": "AWS_S3" } } }, + "then": { + "required": [ + "region", + "bucket" + ], + "properties": { + "region": { "$ref": "#/definitions/region" }, + "bucket": { "$ref": "#/definitions/bucket" }, + "username": { "$ref": "#/definitions/username" }, + "passphrase": { "$ref": "base_schema.json#/definitions/secret" }, + "endpointUrl": { "$ref": "#/definitions/endpointUrl" } + }, + "dependencies": { + "passphrase": [ "username" ], + "username":[ "passphrase" ] + } + }, + "else": {} + }, + { + "if": { "properties": { "type": { "const": "Graphite" } } }, + "then": { + "required": [ + "host" + ], + "properties": { + "host": { "$ref": "#/definitions/host" }, + "protocol": { "$ref": "#/definitions/protocols", "default": "https" }, + "port": { "$ref": "#/definitions/port", "default": 443 }, + "path": { "$ref": "#/definitions/path", "default": "/events/" } + } + }, + "else": {} + }, + { + "if": { "properties": { "type": { "const": "Kafka" } } }, + "then": { + "required": [ + "host", + "topic" + ], + "properties": { + "authenticationProtocol": { "$ref": "#/definitions/authenticationProtocol", "default": "None" }, + "host": { "$ref": "#/definitions/host" }, + "protocol": { "$ref": "#/definitions/protocols", "default": "binaryTcpTls" }, + "port": { "$ref": "#/definitions/port", "default": 9092 }, + "topic": { "$ref": "#/definitions/topic" } + }, + "allOf": [ + { + "if": { "properties": { "authenticationProtocol": { "const": "SASL-PLAIN" } } }, + "then": { + "required": [ + "username" + ], + "properties": { + "username": { "$ref": "#/definitions/username" }, + "passphrase": { "$ref": "base_schema.json#/definitions/secret" } + }, + "dependencies": { + "passphrase": [ "username" ] + } + }, + "else": {} + }, + { + "if": { "properties": { "authenticationProtocol": { "const": "TLS" } } }, + "then": { + "required": [ + "privateKey", + "clientCertificate" + ], + "allOf": [ + { "not": { "required": [ "username" ] } }, + { "not": { "required": [ "passphrase" ] } } + ], + "properties": { + "privateKey": { "$ref": "#/definitions/privateKey" }, + "clientCertificate": { "$ref": "#/definitions/clientCertificate" }, + "rootCertificate": { "$ref": "#/definitions/rootCertificate" }, + "protocol": { "const": "binaryTcpTls" } + } + }, + "else": {} + } + ] + }, + "else": {} + }, + { + "if": { "properties": { "type": { "const": "ElasticSearch" } } }, + "then": { + "required": [ + "host", + "index" + ], + "properties": { + "host": { "$ref": "#/definitions/host" }, + "protocol": { "$ref": "#/definitions/protocols", "default": "https" }, + "port": { "$ref": "#/definitions/port", "default": 9200 }, + "path": { "$ref": "#/definitions/path" }, + "username": { "$ref": "#/definitions/username" }, + "passphrase": { "$ref": "base_schema.json#/definitions/secret" }, + "apiVersion": { "$ref": "#/definitions/apiVersion", "default": "6.0" }, + "index": { "$ref": "#/definitions/index" } + }, + "allOf": [ + { + "if": { "properties": { "apiVersion": { "pattern": "^[0-6][.]|^[0-6]$" } } }, + "then": { + "properties": { + "dataType": { + "$ref": "#/definitions/dataType", + "default": "f5.telemetry", + "minLength": 1 + } + } + }, + "else": { + "if": { "properties": { "apiVersion": { "pattern": "^7[.]|^7$" } } }, + "then": { + "properties": { + "dataType": { + "$ref": "#/definitions/dataType", + "default": "_doc", + "minLength": 1 + } + } + }, + "else": { + "allOf": [ + { "not": { "required": [ "dataType" ] } } + ] + } + } + } + ] + }, + "else": {} + }, + { + "if": { "properties": { "type": { "const": "Sumo_Logic" } } }, + "then": { + "required": [ + "host", + "passphrase" + ], + "properties": { + "host": { "$ref": "#/definitions/host" }, + "protocol": { "$ref": "#/definitions/protocols", "default": "https" }, + "port": { "$ref": "#/definitions/port", "default": 443 }, + "path": { "$ref": "#/definitions/path", "default": "/receiver/v1/http/" }, + "passphrase": { "$ref": "base_schema.json#/definitions/secret" } + } + }, + "else": {} + }, + { + "if": { "properties": { "type": { "const": "Statsd" } } }, + "then": { + "required": [ + "host" + ], + "properties": { + "host": { "$ref": "#/definitions/host" }, + "protocol": { + "title": "Protocol", + "type": "string", + "enum": [ "tcp", "udp" ], + "default": "udp" + }, + "port": { "$ref": "#/definitions/port", "default": 8125 }, + "addTags": { "$ref": "#/definitions/autoTaggingStatsd" }, + "convertBooleansToMetrics": { "$ref": "#/definitions/convertBooleansToMetrics", "default": "false" } + } + }, + "else": {} + }, + { + "if": { + "properties": { "type": { "enum": ["Google_Cloud_Monitoring", "Google_StackDriver", "Google_Cloud_Logging"] } } + }, + "then": { + "required": [ + "serviceEmail" + ], + "properties": { + "privateKeyId": { "$ref": "#/definitions/privateKeyId" }, + "serviceEmail": { "$ref": "#/definitions/serviceEmail" }, + "privateKey": { "$ref": "base_schema.json#/definitions/secret" }, + "useServiceAccountToken": { "$ref": "#/definitions/useServiceAccountToken", "default": false }, + "reportInstanceMetadata": { "$ref": "#/definitions/reportInstanceMetadata", "default": false } + }, + "allOf": [ + { + "dependencies": { + "privateKeyId": { + "anyOf": [ + { "not": {"required": [ "useServiceAccountToken" ] } }, + { "properties": { "useServiceAccountToken": { "const": false } } } + ] + } + } + }, + { + "dependencies": { + "privateKey": { + "anyOf": [ + { "not": {"required": [ "useServiceAccountToken" ] } }, + { "properties": { "useServiceAccountToken": { "const": false } } } + ] + } + } + }, + { + "if": { + "anyOf": [ + { "not": { "required" : [ "useServiceAccountToken"] } }, + { "properties": { "useServiceAccountToken": { "const": false } } } + ] + }, + "then": { "required": ["privateKeyId", "privateKey"] }, + "else": { "not": { "required": ["privateKeyId", "privateKey"] } } + }, + { + "if": { "properties": { "type": { "enum": ["Google_Cloud_Monitoring", "Google_StackDriver"] } } }, + "then": { + "properties": { + "projectId": { "$ref": "#/definitions/projectId"} + }, + "required": ["projectId"] + } + }, + { + "if": { "properties": { "type": { "const": "Google_Cloud_Logging" } } }, + "then": { + "properties": { + "logScope": { "$ref": "#/definitions/logScope", "default": "projects" }, + "logScopeId": { "$ref": "#/definitions/logScopeId"}, + "logId": { "$ref": "#/definitions/logId"} + }, + "required": ["logScope", "logScopeId", "logId"] + } + } + ] + }, + "else": {} + }, + { + "if": { "properties": { "type": { "const": "F5_Cloud" } } }, + "then": { + "required": [ + "f5csTenantId", + "f5csSensorId", + "payloadSchemaNid", + "serviceAccount", + "targetAudience" + ], + "properties": { + "port": { "$ref": "#/definitions/port", "default": 443 }, + "eventSchemaVersion": { "$ref": "#/definitions/eventSchemaVersion" }, + "f5csTenantId": { "$ref": "#/definitions/f5csTenantId" }, + "f5csSensorId": { "$ref": "#/definitions/f5csSensorId" }, + "payloadSchemaNid": { "$ref": "#/definitions/payloadSchemaNid" }, + "serviceAccount": { "$ref": "#/definitions/serviceAccount" }, + "targetAudience": { "$ref": "#/definitions/targetAudience" }, + "useSSL": { "$ref": "#/definitions/useSSL", "default": true } + }, + "nodeSupportVersion": "8.11.1" + }, + "else": {} + }, + { + "if": { "properties": { "type": { "const": "DataDog" } } }, + "then": { + "required": [ + "apiKey" + ], + "properties": { + "apiKey": { "$ref": "#/definitions/apiKey" }, + "compressionType": { "$ref": "#/definitions/compressionType", "default": "none" }, + "region": { "$ref": "#/definitions/region", "enum": ["US1", "US3", "EU1", "US1-FED"], "default": "US1" }, + "service": { "$ref": "#/definitions/service", "default": "f5-telemetry" }, + "metricPrefix": { "$ref": "#/definitions/metricPrefix" }, + "convertBooleansToMetrics": { "$ref": "#/definitions/convertBooleansToMetrics", "default": "false" }, + "customTags": { "$ref": "#/definitions/customTags" } + } + }, + "else": {} + }, + { + "if": { "properties": { "type": { "const": "OpenTelemetry_Exporter" } } }, + "then": { + "required": [ + "host", + "port" + ], + "properties": { + "host": { "$ref": "#/definitions/host" }, + "port": { "$ref": "#/definitions/port" }, + "headers": { "$ref": "#/definitions/headers" }, + "metricsPath": { "$ref": "#/definitions/path" }, + "convertBooleansToMetrics": { "$ref": "#/definitions/convertBooleansToMetrics", "default": "false" } + }, + "nodeSupportVersion": "8.11.1" + }, + "else": {} + } + ] + }, + "else": {} + } + ] +} diff --git a/src/schema/1.25.0/controls_schema.json b/src/schema/1.25.0/controls_schema.json new file mode 100644 index 00000000..2bbfcff8 --- /dev/null +++ b/src/schema/1.25.0/controls_schema.json @@ -0,0 +1,52 @@ +{ + "$id": "controls_schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Telemetry Streaming Controls schema", + "description": "", + "type": "object", + "allOf": [ + { + "if": { "properties": { "class": { "const": "Controls" } } }, + "then": { + "required": [ + "class" + ], + "properties": { + "class": { + "title": "Class", + "description": "Telemetry Streaming Controls class", + "type": "string", + "enum": [ "Controls" ] + }, + "logLevel": { + "title": "Logging Level", + "description": "", + "type": "string", + "default": "info", + "enum": [ + "debug", + "info", + "error" + ] + }, + "debug": { + "title": "Enable debug mode", + "description": "", + "type": "boolean", + "default": false + }, + "memoryThresholdPercent": { + "title": "Memory Usage Threshold (Percentage of Available Process Memory)", + "description": "Once memory usage reaches this value, processing may temporarily cease until levels return below threshold. Defaults to 90%", + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 90 + } + }, + "additionalProperties": false + }, + "else": {} + } + ] +} \ No newline at end of file diff --git a/src/schema/1.25.0/endpoints_schema.json b/src/schema/1.25.0/endpoints_schema.json new file mode 100644 index 00000000..73fc5ee7 --- /dev/null +++ b/src/schema/1.25.0/endpoints_schema.json @@ -0,0 +1,158 @@ +{ + "$id": "endpoints_schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Telemetry Streaming Endpoints schema", + "description": "", + "type": "object", + "definitions": { + "endpoint": { + "title": "Telemetry Endpoint", + "description": "", + "type": "object", + "properties": { + "enable": { + "title": "Enable endpoint", + "default": true, + "allOf": [ + { + "$ref": "base_schema.json#/definitions/enable" + } + ] + }, + "name": { + "title": "Endpoint name", + "type": "string", + "minLength": 1 + }, + "path": { + "title": "Path to query data from", + "type": "string", + "minLength": 1 + } + }, + "additionalProperties": false + }, + "endpoints": { + "title": "Telemetry Endpoints", + "description": "", + "type": "object", + "properties": { + "enable": { + "title": "Enable endpoints", + "default": true, + "allOf": [ + { + "$ref": "base_schema.json#/definitions/enable" + } + ] + }, + "basePath": { + "title": "Base Path", + "description": "Optional base path value to prepend to each individual endpoint paths", + "type": "string", + "default": "" + }, + "items": { + "title": "Items", + "description": "Object with each property an endpoint with their own properties", + "type": "object", + "additionalProperties": { + "allOf": [ + { + "$ref": "#/definitions/endpoint" + }, + { + "required": [ "path" ] + } + ] + }, + "minProperties": 1 + } + } + }, + "endpointsObjectRef": { + "allOf": [ + { + "$ref": "#/definitions/endpoints" + }, + { + "properties": { + "enable": {}, + "basePath": {}, + "items": {} + }, + "required": [ "items" ], + "additionalProperties": false + } + ] + }, + "endpointObjectRef": { + "allOf": [ + { + "$ref": "#/definitions/endpoint" + }, + { + "properties": { + "enable": {}, + "name": {}, + "path": {} + }, + "required": [ "name", "path" ], + "additionalProperties": false + } + ] + }, + "endpointsPointerRef": { + "title": "Telemetry_Endpoints Name", + "description": "Name of the Telemetry_Endpoints object", + "type": "string", + "declarationClass": "Telemetry_Endpoints", + "minLength": 1 + }, + "endpointsItemPointerRef": { + "title": "Telemetry_Endpoints Name and Item Key", + "description": "Name of the Telemetry_Endpoints object and the endpoint item key, e.g endpointsA/item1", + "type": "string", + "declarationClassProp": { + "path" :"Telemetry_Endpoints/items", + "partsNum": 2 + }, + "minLength": 1 + } + }, + "allOf": [ + { + "if": { "properties": { "class": { "const": "Telemetry_Endpoints" } } }, + "then": { + "required": [ + "class", + "items" + ], + "properties": { + "class": { + "title": "Class", + "description": "Telemetry Streaming Endpoints class", + "type": "string", + "enum": [ "Telemetry_Endpoints" ] + } + }, + "allOf": [ + { + "$comment": "This allows enforcement of no additional properties in this nested schema - could reuse above properties but prefer a separate block", + "properties": { + "class": {}, + "enable": {}, + "basePath": {}, + "items": {} + }, + "additionalProperties": false + }, + { + "$ref": "#/definitions/endpoints" + } + ] + }, + "else": {} + } + ] +} \ No newline at end of file diff --git a/src/schema/1.25.0/ihealth_poller_schema.json b/src/schema/1.25.0/ihealth_poller_schema.json new file mode 100644 index 00000000..d5bcb9cf --- /dev/null +++ b/src/schema/1.25.0/ihealth_poller_schema.json @@ -0,0 +1,238 @@ +{ + "$id": "ihealth_poller_schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Telemetry Streaming iHealth Poller schema", + "description": "", + "type": "object", + "definitions": { + "time24hr": { + "title": "Time in HH:MM, 24hr", + "description": "", + "type": "string", + "pattern": "^([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]?$" + }, + "iHealthPoller": { + "$comment": "system_schema.json should be updated when new property added", + "title": "iHealth Poller", + "description": "", + "type": "object", + "required": [ + "interval", + "username", + "passphrase" + ], + "properties": { + "enable": { + "default": true, + "allOf": [ + { + "$ref": "base_schema.json#/definitions/enable" + } + ] + }, + "trace": { + "default": false, + "allOf": [ + { + "$ref": "base_schema.json#/definitions/trace" + } + ] + }, + "proxy": { + "title": "Proxy configuration", + "properties": { + "port": { + "default": 80 + }, + "protocol": { + "default": "http" + } + }, + "allOf": [ + { + "$ref": "base_schema.json#/definitions/proxy" + } + ] + }, + "username": { + "title": "iHealth Username", + "$ref": "base_schema.json#/definitions/username" + }, + "passphrase": { + "title": "iHealth Passphrase", + "$ref": "base_schema.json#/definitions/secret" + }, + "downloadFolder": { + "title": "Directory to download Qkview to", + "description": "", + "type": "string", + "minLength": 1, + "pathExists": true + }, + "interval": { + "title": "Operating interval", + "description": "" , + "type": "object", + "properties": { + "timeWindow": { + "title": "Two or more hours window in 24hr format that iHealth data can be sent", + "description": "", + "type": "object", + "properties": { + "start": { + "title": "Time when the window starts", + "$ref": "#/definitions/time24hr" + }, + "end": { + "title": "Time when the window ends", + "$ref": "#/definitions/time24hr" + } + }, + "timeWindowMinSize": 120, + "required": [ "start", "end" ], + "additionalProperties": false + }, + "frequency": { + "title": "Interval frequency", + "description": "", + "type": "string", + "default": "daily", + "enum": [ + "daily", + "weekly", + "monthly" + ] + } + + }, + "required": [ + "timeWindow" + ], + "allOf": [ + { + "if": { "properties": { "frequency": { "const": "daily" } } }, + "then": { + "properties": { + "timeWindow": {}, + "frequency": {} + }, + "additionalProperties": false + } + }, + { + "if": { "properties": { "frequency": { "const": "weekly" } } }, + "then": { + "properties": { + "timeWindow": {}, + "frequency": {}, + "day": { + "title": "", + "description": "", + "oneOf": [ + { + "type": "string", + "pattern": "^([mM]onday|[tT]uesday|[wW]ednesday|[tT]hursday|[fF]riday|[sS]aturday|[sS]unday)$" + }, + { + "$comment": "0 and 7 eq. Sunday", + "type": "integer", + "minimum": 0, + "maximum": 7 + } + ] + } + }, + "required": [ "day" ], + "additionalProperties": false + } + }, + { + "if": { "properties": { "frequency": { "const": "monthly" } } }, + "then": { + "properties": { + "timeWindow": {}, + "frequency": {}, + "day": { + "title": "", + "description": "", + "type": "integer", + "minimum": 1, + "maximum": 31 + } + }, + "required": [ "day" ], + "additionalProperties": false + } + } + ] + } + } + }, + "iHealthPollerPointerRef": { + "type": "string", + "minLength": 1, + "declarationClass": "Telemetry_iHealth_Poller" + }, + "iHealthPollerObjectRef": { + "allOf": [ + { + "$comment": "This allows enforcement of no additional properties in this nested schema - could reuse above properties but prefer a separate block", + "properties": { + "enable": {}, + "trace": {}, + "interval": {}, + "proxy": {}, + "username": {}, + "passphrase": {}, + "downloadFolder": {} + }, + "additionalProperties": false + }, + { + "$ref": "ihealth_poller_schema.json#/definitions/iHealthPoller" + } + ] + } + }, + "allOf": [ + { + "if": { "properties": { "class": { "const": "Telemetry_iHealth_Poller" } } }, + "then": { + "required": [ + "class", + "username", + "passphrase" + ], + "properties": { + "class": { + "title": "Class", + "description": "Telemetry Streaming iHealth Poller class", + "type": "string", + "enum": [ "Telemetry_iHealth_Poller" ] + } + }, + "allOf": [ + { + "$comment": "This allows enforcement of no additional properties in this nested schema - could reuse above properties but prefer a separate block", + "properties": { + "class": {}, + "enable": {}, + "trace": {}, + "interval": {}, + "proxy": {}, + "username": {}, + "passphrase": {}, + "downloadFolder": {} + }, + "additionalProperties": false + }, + { + "$ref": "#/definitions/iHealthPoller" + } + ] + }, + "else": {}, + "$comment": "Telemetry_iHealth_Poller should be either built-in within Telemetry_System or referenced by Telemetry_System(s), otherwise it will be treated as disabled" + } + ] +} \ No newline at end of file diff --git a/src/schema/1.25.0/listener_schema.json b/src/schema/1.25.0/listener_schema.json new file mode 100644 index 00000000..d3b9434e --- /dev/null +++ b/src/schema/1.25.0/listener_schema.json @@ -0,0 +1,85 @@ +{ + "$id": "listener_schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Telemetry Streaming event listener schema", + "description": "", + "type": "object", + "allOf": [ + { + "if": { "properties": { "class": { "const": "Telemetry_Listener" } } }, + "then": { + "required": [ + "class" + ], + "properties": { + "class": { + "title": "Class", + "description": "Telemetry Streaming Event Listener class", + "type": "string", + "enum": [ "Telemetry_Listener" ] + }, + "enable": { + "default": true, + "allOf": [ + { + "$ref": "base_schema.json#/definitions/enable" + } + ] + }, + "trace": { + "default": false, + "oneOf": [ + { + "$ref": "base_schema.json#/definitions/trace" + }, + { + "$ref": "base_schema.json#/definitions/traceV2" + } + ] + }, + "port": { + "minimum": 1024, + "maximum": 65535, + "default": 6514, + "allOf": [ + { + "$ref": "base_schema.json#/definitions/port" + } + ] + }, + "tag": { + "$comment": "Deprecated! Use actions with a setTag action.", + "allOf": [ + { + "$ref": "base_schema.json#/definitions/tag" + } + ] + }, + "match": { + "default": "", + "allOf": [ + { + "$ref": "base_schema.json#/definitions/match" + } + ] + }, + "actions": { + "title": "Actions", + "description": "Actions to be performed on the listener.", + "default": [ + { + "setTag": { + "tenant": "`T`", + "application": "`A`" + } + } + ], + "allOf": [{ "$ref": "actions_schema.json#/definitions/inputDataStreamActionsChain" }] + } + }, + "additionalProperties": false + }, + "else": {} + } + ] +} \ No newline at end of file diff --git a/src/schema/1.25.0/namespace_schema.json b/src/schema/1.25.0/namespace_schema.json new file mode 100644 index 00000000..f6cb09fc --- /dev/null +++ b/src/schema/1.25.0/namespace_schema.json @@ -0,0 +1,92 @@ +{ + "$id": "namespace_schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Telemetry Streaming Namespace schema", + "description": "", + "type": "object", + "definitions": { + "namespace": { + "required": [ + "class" + ], + "type": "object", + "properties": { + "class": { + "title": "Class", + "description": "Telemetry Streaming Namespace class", + "type": "string", + "enum": [ "Telemetry_Namespace" ] + } + }, + "additionalProperties": { + "$comment": "All objects supported under a Telemetry Namespace", + "properties": { + "class": { + "title": "Class", + "type": "string", + "enum": [ + "Telemetry_System", + "Telemetry_System_Poller", + "Telemetry_Listener", + "Telemetry_Consumer", + "Telemetry_Pull_Consumer", + "Telemetry_iHealth_Poller", + "Telemetry_Endpoints", + "Shared" + ] + } + }, + "allOf": [ + { + "$ref": "system_schema.json#" + }, + { + "$ref": "system_poller_schema.json#" + }, + { + "$ref": "listener_schema.json#" + }, + { + "$ref": "consumer_schema.json#" + }, + { + "$ref": "pull_consumer_schema.json#" + }, + { + "$ref": "ihealth_poller_schema.json#" + }, + { + "$ref": "endpoints_schema.json#" + }, + { + "$ref": "shared_schema.json#" + } + ] + } + } + }, + "allOf": [ + { + "if": { "properties": { "class": { "const": "Telemetry_Namespace" } } }, + "then": { + "required": [ + "class" + ], + "properties": { + "class": { + "title": "Class", + "description": "Telemetry Streaming Namespace class", + "type": "string", + "enum": [ "Telemetry_Namespace" ] + } + }, + "allOf": [ + { + "$ref": "#/definitions/namespace" + } + ] + }, + "else": {} + } + ] +} \ No newline at end of file diff --git a/src/schema/1.25.0/pull_consumer_schema.json b/src/schema/1.25.0/pull_consumer_schema.json new file mode 100644 index 00000000..0747cbfd --- /dev/null +++ b/src/schema/1.25.0/pull_consumer_schema.json @@ -0,0 +1,101 @@ +{ + "$id": "pull_consumer_schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Telemetry Streaming Pull Consumer schema", + "description": "", + "type": "object", + "allOf": [ + { + "if": { "properties": { "class": { "const": "Telemetry_Pull_Consumer" } } }, + "then": { + "required": [ + "class", + "type", + "systemPoller" + ], + "properties": { + "class": { + "title": "Class", + "description": "Telemetry Streaming Pull Consumer class", + "type": "string", + "enum": [ "Telemetry_Pull_Consumer" ] + }, + "enable": { + "default": true, + "allOf": [ + { + "$ref": "base_schema.json#/definitions/enable" + } + ] + }, + "trace": { + "default": false, + "allOf": [ + { + "$ref": "base_schema.json#/definitions/trace" + } + ] + }, + "type": { + "title": "Type", + "description": "" , + "type": "string", + "enum": [ + "default", + "Prometheus" + ] + }, + "systemPoller": { + "title": "Pointer to System Poller(s)", + "anyOf": [ + { + "$ref": "system_poller_schema.json#/definitions/systemPollerPointerRef" + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "system_poller_schema.json#/definitions/systemPollerPointerRef" + } + ] + }, + "minItems": 1 + } + ] + } + }, + "allOf": [ + { + "$comment": "This allows enforcement of no additional properties in this nested schema - could reuse above properties but prefer a separate block", + "properties": { + "class": {}, + "enable": {}, + "trace": {}, + "type": {}, + "systemPoller": {} + }, + "additionalProperties": false + }, + { + "if": { "properties": { "type": { "const": "default" } } }, + "then": { + "required": [], + "properties": {} + }, + "else": {} + }, + { + "if": { "properties": { "type": { "const": "Prometheus" } } }, + "then": { + "required": [], + "properties": {} + }, + "else": {} + } + ] + }, + "else": {} + } + ] +} diff --git a/src/schema/1.25.0/shared_schema.json b/src/schema/1.25.0/shared_schema.json new file mode 100644 index 00000000..aa96cb2e --- /dev/null +++ b/src/schema/1.25.0/shared_schema.json @@ -0,0 +1,50 @@ +{ + "$id": "shared_schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Telemetry streaming Shared schema", + "description": "", + "type": "object", + "allOf": [ + { + "if": { "properties": { "class": { "const": "Shared" } } }, + "then": { + "required": [ + "class" + ], + "properties": { + "class": { + "title": "Class", + "description": "Telemetry streaming Shared class", + "type": "string", + "enum": [ "Shared" ] + } + }, + "additionalProperties": { + "properties": { + "class": { + "title": "Class", + "type": "string", + "enum": [ + "Constants", + "Secret" + ] + } + }, + "allOf": [ + { + "if": { "properties": { "class": { "const": "Constants" } } }, + "then": { "$ref": "base_schema.json#/definitions/constants" }, + "else": {} + }, + { + "if": { "properties": { "class": { "const": "Secret" } } }, + "then": { "$ref": "base_schema.json#/definitions/secret" }, + "else": {} + } + ] + } + }, + "else": {} + } + ] +} \ No newline at end of file diff --git a/src/schema/1.25.0/system_poller_schema.json b/src/schema/1.25.0/system_poller_schema.json new file mode 100644 index 00000000..dcb3a454 --- /dev/null +++ b/src/schema/1.25.0/system_poller_schema.json @@ -0,0 +1,242 @@ +{ + "$id": "system_poller_schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Telemetry Streaming system poller schema", + "description": "", + "type": "object", + "definitions": { + "systemPoller": { + "$comment": "system_schema.json should be updated when new property added", + "title": "System Poller", + "description": "", + "type": "object", + "properties": { + "enable": { + "default": true, + "allOf": [ + { + "$ref": "base_schema.json#/definitions/enable" + } + ] + }, + "interval": { + "title": "Collection interval (in seconds)", + "description": "If endpointList is specified, minimum=1. Without endpointList, minimum=60 and maximum=60000. Allows setting interval=0 to not poll on an interval.", + "type": "integer", + "default": 300 + }, + "trace": { + "$ref": "base_schema.json#/definitions/trace" + }, + "tag": { + "$comment": "Deprecated! Use actions with a setTag action.", + "allOf": [ + { + "$ref": "base_schema.json#/definitions/tag" + } + ] + }, + "actions": { + "title": "Actions", + "description": "Actions to be performed on the systemPoller.", + "default": [ + { + "setTag": { + "tenant": "`T`", + "application": "`A`" + } + } + ], + "allOf": [{ "$ref": "actions_schema.json#/definitions/inputDataStreamActionsChain" }] + }, + "endpointList": { + "title": "Endpoint List", + "description": "List of endpoints to use in data collection", + "oneOf": [ + { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "endpoints_schema.json#/definitions/endpointsPointerRef" + }, + { + "$ref": "endpoints_schema.json#/definitions/endpointsItemPointerRef" + }, + { + "if": { "required": [ "items" ]}, + "then": { + "$ref": "endpoints_schema.json#/definitions/endpointsObjectRef" + }, + "else": { + "$ref": "endpoints_schema.json#/definitions/endpointObjectRef" + } + } + + ] + }, + "minItems": 1 + }, + { + "$ref": "endpoints_schema.json#/definitions/endpointsPointerRef" + }, + { + "$ref": "endpoints_schema.json#/definitions/endpointsObjectRef" + } + ] + } + }, + "oneOf": [ + { + "allOf": [ + { + "if": { "required": [ "endpointList" ] }, + "then": { + "properties": { + "interval": { + "minimum": 1 + } + } + }, + "else": { + "properties":{ + "interval": { + "minimum": 60, + "maximum": 6000 + } + } + } + } + ] + }, + { + "allOf": [ + { + "properties": { + "interval": { + "enum": [0] + } + } + } + ] + } + ] + }, + "systemPollerPointerRef": { + "type": "string", + "minLength": 1, + "declarationClass": "Telemetry_System_Poller" + }, + "systemPollerObjectRef": { + "allOf": [ + { + "$comment": "This allows enforcement of no additional properties in this nested schema - could reuse above properties but prefer a separate block", + "properties": { + "enable": {}, + "trace": {}, + "interval": {}, + "tag": {}, + "actions": {}, + "endpointList": {} + }, + "additionalProperties": false + }, + { + "$ref": "#/definitions/systemPoller" + } + ] + } + }, + "allOf": [ + { + "if": { "properties": { "class": { "const": "Telemetry_System_Poller" } } }, + "then": { + "required": [ + "class" + ], + "properties": { + "class": { + "title": "Class", + "description": "Telemetry Streaming System Poller class", + "type": "string", + "enum": [ "Telemetry_System_Poller" ] + }, + "host": { + "$comment": "Deprecated! Use Telemetry_System to define target device", + "default": "localhost", + "allOf": [ + { + "$ref": "base_schema.json#/definitions/host" + } + ] + }, + "port": { + "$comment": "Deprecated! Use Telemetry_System to define target device", + "default": 8100, + "allOf": [ + { + "$ref": "base_schema.json#/definitions/port" + } + ] + }, + "protocol": { + "$comment": "Deprecated! Use Telemetry_System to define target device", + "default": "http", + "allOf": [ + { + "$ref": "base_schema.json#/definitions/protocol" + } + ] + }, + "allowSelfSignedCert": { + "$comment": "Deprecated! Use Telemetry_System to define target device", + "title": "Allow Self-Signed Certificate", + "default": false, + "allOf": [ + { + "$ref": "base_schema.json#/definitions/allowSelfSignedCert" + } + ] + }, + "enableHostConnectivityCheck": { + "$comment": "Deprecated! Use Telemetry_System to define target device", + "$ref": "base_schema.json#/definitions/enableHostConnectivityCheck" + }, + "username": { + "$comment": "Deprecated! Use Telemetry_System to define target device", + "$ref": "base_schema.json#/definitions/username" + }, + "passphrase": { + "$comment": "Deprecated! Use Telemetry_System to define target device", + "$ref": "base_schema.json#/definitions/secret" + } + }, + "allOf": [ + { + "$comment": "This allows enforcement of no additional properties in this nested schema - could reuse above properties but prefer a separate block", + "properties": { + "class": {}, + "enable": {}, + "trace": {}, + "interval": {}, + "tag": {}, + "host": {}, + "port": {}, + "protocol": {}, + "allowSelfSignedCert": {}, + "enableHostConnectivityCheck": {}, + "username": {}, + "passphrase": {}, + "actions": {}, + "endpointList": {} + }, + "additionalProperties": false + }, + { + "$ref": "#/definitions/systemPoller" + } + ] + } + } + ] +} \ No newline at end of file diff --git a/src/schema/1.25.0/system_schema.json b/src/schema/1.25.0/system_schema.json new file mode 100644 index 00000000..cba58faa --- /dev/null +++ b/src/schema/1.25.0/system_schema.json @@ -0,0 +1,121 @@ +{ + "$id": "system_schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Telemetry Streaming System schema", + "description": "", + "type": "object", + "allOf": [ + { + "if": { "properties": { "class": { "const": "Telemetry_System" } } }, + "then": { + "required": [ + "class" + ], + "properties": { + "class": { + "title": "Class", + "description": "Telemetry Streaming System class", + "type": "string", + "enum": [ "Telemetry_System" ] + }, + "enable": { + "title": "Enable all pollers attached to device", + "default": true, + "allOf": [ + { + "$ref": "base_schema.json#/definitions/enable" + } + ] + }, + "trace": { + "$ref": "base_schema.json#/definitions/trace" + }, + "host": { + "title": "System connection address", + "default": "localhost", + "allOf": [ + { + "$ref": "base_schema.json#/definitions/host" + } + ] + }, + "port": { + "title": "System connection port", + "default": 8100, + "allOf": [ + { + "$ref": "base_schema.json#/definitions/port" + } + ] + }, + "protocol": { + "title": "System connection protocol", + "default": "http", + "allOf": [ + { + "$ref": "base_schema.json#/definitions/protocol" + } + ] + }, + "allowSelfSignedCert": { + "title": "Allow Self-Signed Certificate", + "default": false, + "allOf": [ + { + "$ref": "base_schema.json#/definitions/allowSelfSignedCert" + } + ] + }, + "enableHostConnectivityCheck": { + "$ref": "base_schema.json#/definitions/enableHostConnectivityCheck" + }, + "username": { + "title": "System Username", + "$ref": "base_schema.json#/definitions/username" + }, + "passphrase": { + "title": "System Passphrase", + "$ref": "base_schema.json#/definitions/secret" + }, + "systemPoller": { + "title": "System Poller declaration", + "oneOf": [ + { + "$ref": "system_poller_schema.json#/definitions/systemPollerPointerRef" + }, + { + "$ref": "system_poller_schema.json#/definitions/systemPollerObjectRef" + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "system_poller_schema.json#/definitions/systemPollerObjectRef" + }, + { + "$ref": "system_poller_schema.json#/definitions/systemPollerPointerRef" + } + ] + }, + "minItems": 1 + } + ] + }, + "iHealthPoller": { + "title": "iHealth Poller declaration", + "oneOf": [ + { + "$ref": "ihealth_poller_schema.json#/definitions/iHealthPollerPointerRef" + }, + { + "$ref": "ihealth_poller_schema.json#/definitions/iHealthPollerObjectRef" + } + ] + } + }, + "additionalProperties": false + } + } + ] +} \ No newline at end of file diff --git a/src/schema/latest/base_schema.json b/src/schema/latest/base_schema.json index 8bc202ed..5b6b4904 100644 --- a/src/schema/latest/base_schema.json +++ b/src/schema/latest/base_schema.json @@ -242,8 +242,8 @@ "description": "Version of ADC Declaration schema this declaration uses", "type": "string", "$comment": "IMPORTANT: In enum array, please put current schema version first, oldest-supported version last. Keep enum array sorted most-recent-first.", - "enum": [ "1.24.0", "1.23.0", "1.22.0", "1.21.0", "1.20.1", "1.20.0", "1.19.0", "1.18.0", "1.17.0", "1.16.0", "1.15.0", "1.14.0", "1.13.0", "1.12.0", "1.11.0", "1.10.0", "1.9.0", "1.8.0", "1.7.0", "1.6.0", "1.5.0", "1.4.0", "1.3.0", "1.2.0", "1.1.0", "1.0.0", "0.9.0" ], - "default": "1.24.0" + "enum": [ "1.25.0", "1.24.0", "1.23.0", "1.22.0", "1.21.0", "1.20.1", "1.20.0", "1.19.0", "1.18.0", "1.17.0", "1.16.0", "1.15.0", "1.14.0", "1.13.0", "1.12.0", "1.11.0", "1.10.0", "1.9.0", "1.8.0", "1.7.0", "1.6.0", "1.5.0", "1.4.0", "1.3.0", "1.2.0", "1.1.0", "1.0.0", "0.9.0" ], + "default": "1.25.0" }, "$schema": { "title": "Schema", @@ -307,4 +307,4 @@ "required": [ "class" ] -} \ No newline at end of file +} diff --git a/src/schema/latest/consumer_schema.json b/src/schema/latest/consumer_schema.json index 29a2c908..2bbe0818 100644 --- a/src/schema/latest/consumer_schema.json +++ b/src/schema/latest/consumer_schema.json @@ -206,6 +206,14 @@ "minLength": 1, "f5expand": true }, + "endpointUrl": { + "$comment": "Required for certain consumers: AWS_CloudWatch, AWS_S3", + "title": "endpoint url", + "description": "The full endpoint URL for service requests", + "type": "string", + "minLength": 1, + "f5expand": true + }, "bucket": { "$comment": "Required for certain consumers: AWS_S3", "title": "Bucket", @@ -232,12 +240,26 @@ }, "metricNamespace": { "$comment": "Required for certain consumers: AWS_CloudWatch", - "title": "Metric Name", - "description": "The namespace for the metrics" , + "title": "Metric Namespace", + "description": "The namespace for the metrics", "type": "string", "f5expand": true, "minLength": 1 }, + "metricPrefix": { + "$comment": "Required for certain consumers: DataDog", + "title": "Metric Prefix", + "description": "The string value(s) to use as a metric prefix", + "type": "array", + "minItems": 1, + "items": { + "allOf": [{ + "type": "string", + "f5expand": true, + "minLength": 1 + }] + } + }, "workspaceId": { "$comment": "Required for certain consumers: Azure_Log_Analytics", "title": "Workspace ID", @@ -380,13 +402,20 @@ "f5expand": true }, "privateKeyId": { - "$comment": "Required for certain consumers: Google_Cloud_Monitoring, Google_Cloud_Logging", + "$comment": "Required for certain consumers when Service Account Token is not used: Google_Cloud_Monitoring, Google_Cloud_Logging", "title": "Private Key ID", "description": "The private key ID.", "type": "string", "minLength": 1, "f5expand": true }, + "useServiceAccountToken": { + "$comment": "Used by certain consumers: Google_Cloud_Monitoring, Google_Cloud_Logging", + "title": "Use Service Account Token", + "description": "Determines whether to use Service Account Token to perform authorization for Google services", + "type": "boolean", + "default": false + }, "logScope": { "$comment": "Required for certain consumers: Google_Cloud_Logging", "title": "Logging Scope Type", @@ -609,6 +638,38 @@ "type": "string", "minLength": 1, "f5expand": true + }, + "convertBooleansToMetrics": { + "$comment": "Required for certain consumers: DataDog, Statsd, OpenTelemetry_Exporter", + "title": "Convert boolean values to metrics", + "description": "Whether or not to convert boolean values to metrics. True becomes 1, False becomes 0" , + "type": "boolean", + "f5expand": true, + "default": false + }, + "customTags": { + "$comment": "Required for certain consumers: DataDog", + "title": "Custom tags", + "description": "A collection of custom tags that are appended to the dynamically generated telemetry tags", + "type": "array", + "minItems": 1, + "items": { + "properties": { + "name": { + "description": "Name of this tag", + "type": "string", + "f5expand": true, + "minLength": 1 + }, + "value": { + "description": "Value of this tag", + "type": "string", + "f5expand": true, + "minLength": 1 + } + }, + "additionalProperties": false + } } }, "allOf": [ @@ -687,6 +748,7 @@ "actions": {}, "apiKey": {}, "class": {}, + "customTags": {}, "enable": {}, "trace": {}, "type": {}, @@ -709,9 +771,11 @@ "maxBatchIntervalMs": {}, "maxBatchSize": {}, "region": {}, + "endpointUrl": {}, "logGroup": {}, "logStream": {}, "metricNamespace": {}, + "metricPrefix": {}, "bucket": {}, "topic": {}, "apiVersion": {}, @@ -722,6 +786,7 @@ "serviceEmail": {}, "privateKey": {}, "privateKeyId": {}, + "useServiceAccountToken": {}, "clientCertificate": {}, "rootCertificate": {}, "fallbackHosts": {}, @@ -739,7 +804,8 @@ "logId": {}, "reportInstanceMetadata": {}, "metricsPath": {}, - "service": {} + "service": {}, + "convertBooleansToMetrics": {} }, "additionalProperties": false, "dependencies": { @@ -922,7 +988,8 @@ "region": { "$ref": "#/definitions/region" }, "dataType": { "$ref": "#/definitions/dataType", "default": "logs" }, "username": { "$ref": "#/definitions/username" }, - "passphrase": { "$ref": "base_schema.json#/definitions/secret" } + "passphrase": { "$ref": "base_schema.json#/definitions/secret" }, + "endpointUrl": { "$ref": "#/definitions/endpointUrl" } }, "allOf": [ { "not": { "required": ["username"], "not": { "required": ["passphrase"] }}}, @@ -983,7 +1050,8 @@ "region": { "$ref": "#/definitions/region" }, "bucket": { "$ref": "#/definitions/bucket" }, "username": { "$ref": "#/definitions/username" }, - "passphrase": { "$ref": "base_schema.json#/definitions/secret" } + "passphrase": { "$ref": "base_schema.json#/definitions/secret" }, + "endpointUrl": { "$ref": "#/definitions/endpointUrl" } }, "dependencies": { "passphrase": [ "username" ], @@ -1145,7 +1213,8 @@ "default": "udp" }, "port": { "$ref": "#/definitions/port", "default": 8125 }, - "addTags": { "$ref": "#/definitions/autoTaggingStatsd" } + "addTags": { "$ref": "#/definitions/autoTaggingStatsd" }, + "convertBooleansToMetrics": { "$ref": "#/definitions/convertBooleansToMetrics", "default": "false" } } }, "else": {} @@ -1156,17 +1225,46 @@ }, "then": { "required": [ - "privateKeyId", - "privateKey", "serviceEmail" ], "properties": { "privateKeyId": { "$ref": "#/definitions/privateKeyId" }, "serviceEmail": { "$ref": "#/definitions/serviceEmail" }, "privateKey": { "$ref": "base_schema.json#/definitions/secret" }, + "useServiceAccountToken": { "$ref": "#/definitions/useServiceAccountToken", "default": false }, "reportInstanceMetadata": { "$ref": "#/definitions/reportInstanceMetadata", "default": false } }, "allOf": [ + { + "dependencies": { + "privateKeyId": { + "anyOf": [ + { "not": {"required": [ "useServiceAccountToken" ] } }, + { "properties": { "useServiceAccountToken": { "const": false } } } + ] + } + } + }, + { + "dependencies": { + "privateKey": { + "anyOf": [ + { "not": {"required": [ "useServiceAccountToken" ] } }, + { "properties": { "useServiceAccountToken": { "const": false } } } + ] + } + } + }, + { + "if": { + "anyOf": [ + { "not": { "required" : [ "useServiceAccountToken"] } }, + { "properties": { "useServiceAccountToken": { "const": false } } } + ] + }, + "then": { "required": ["privateKeyId", "privateKey"] }, + "else": { "not": { "required": ["privateKeyId", "privateKey"] } } + }, { "if": { "properties": { "type": { "enum": ["Google_Cloud_Monitoring", "Google_StackDriver"] } } }, "then": { @@ -1225,7 +1323,10 @@ "apiKey": { "$ref": "#/definitions/apiKey" }, "compressionType": { "$ref": "#/definitions/compressionType", "default": "none" }, "region": { "$ref": "#/definitions/region", "enum": ["US1", "US3", "EU1", "US1-FED"], "default": "US1" }, - "service": { "$ref": "#/definitions/service", "default": "f5-telemetry" } + "service": { "$ref": "#/definitions/service", "default": "f5-telemetry" }, + "metricPrefix": { "$ref": "#/definitions/metricPrefix" }, + "convertBooleansToMetrics": { "$ref": "#/definitions/convertBooleansToMetrics", "default": "false" }, + "customTags": { "$ref": "#/definitions/customTags" } } }, "else": {} @@ -1241,7 +1342,8 @@ "host": { "$ref": "#/definitions/host" }, "port": { "$ref": "#/definitions/port" }, "headers": { "$ref": "#/definitions/headers" }, - "metricsPath": { "$ref": "#/definitions/path" } + "metricsPath": { "$ref": "#/definitions/path" }, + "convertBooleansToMetrics": { "$ref": "#/definitions/convertBooleansToMetrics", "default": "false" } }, "nodeSupportVersion": "8.11.1" }, diff --git a/stryker.conf.js b/stryker.conf.js new file mode 100644 index 00000000..8d8ad450 --- /dev/null +++ b/stryker.conf.js @@ -0,0 +1,29 @@ +'use strict'; + +const reporters = ['html', 'progress']; +let badgeReporter; +let plugins; + +try { + const badgeReporterPath = require.resolve('stryker-mutator-badge-reporter'); + plugins = ['@stryker-mutator/*', badgeReporterPath]; + reporters.push('badge'); + badgeReporter = { + label: 'mutation' + }; +} catch (e) { + // ignore failure +} + +module.exports = { + packageManager: 'npm', + plugins, + reporters, + badgeReporter, + testRunner: 'mocha', + coverageAnalysis: 'perTest', + mutate: ['src/**/*.js'], + mochaOptions: { + spec: ['test/unit/**/*.js'] + } +}; diff --git a/test/README.md b/test/README.md index 463a5026..8155e18e 100644 --- a/test/README.md +++ b/test/README.md @@ -33,6 +33,15 @@ Best practices: - You can specify 'testOpts' property on the same level as 'name'. The following options available: - only (bool) - run this test only (it.only) +#### Running individual unit tests +Running the ```npm run test``` command will execute all of the tests in the `./test/unit/` directory (recursively). + +However, individual unit tests can be run via mocha, by running the `npm run test-specific` command, along with a custom file glob. + +Examples: +* `npm run test-specific -- test/unit/systemPollerTests.js` +* `npm run test-specific -- test/unit/system*` + ## Functional All functional tests reside inside the ```functional``` folder and are run using ```npm run test-functional```. diff --git a/test/functional/cloud/awsTests.js b/test/functional/cloud/awsTests.js index 3c97624e..8d4ee9c6 100644 --- a/test/functional/cloud/awsTests.js +++ b/test/functional/cloud/awsTests.js @@ -11,8 +11,8 @@ const fs = require('fs'); const assert = require('assert'); const AWS = require('aws-sdk'); -const constants = require('./../shared/constants'); -const testUtil = require('./../shared/util'); +const constants = require('../shared/constants'); +const testUtil = require('../shared/util'); const awsUtil = require('../../../src/lib/consumers/shared/awsUtil'); const ENV_FILE = process.env[constants.ENV_VARS.CLOUD.FILE]; @@ -42,7 +42,7 @@ describe('AWS Cloud-based Tests', function () { password: VM_PWD }; - const assertPost = declaration => testUtil.postDeclaration(deviceInfo, declaration) + const assertPost = (declaration) => testUtil.postDeclaration(deviceInfo, declaration) .then((response) => { testUtil.logger.info('Response from declaration post', { host: VM_IP, response }); return assert.strictEqual(response.message, 'success', 'POST declaration should return success'); @@ -86,7 +86,7 @@ describe('AWS Cloud-based Tests', function () { it('should verify TS service is running', () => { const uri = `${constants.BASE_ILX_URI}/info`; - return new Promise(resolve => setTimeout(resolve, 5000)) + return new Promise((resolve) => setTimeout(resolve, 5000)) .then(() => testUtil.makeRequest(VM_IP, uri, options)) .then((data) => { data = data || {}; @@ -120,7 +120,7 @@ describe('AWS Cloud-based Tests', function () { it('should retrieve systemPoller info from bucket', function () { this.timeout(180000); - return new Promise(resolve => setTimeout(resolve, 90000)) + return new Promise((resolve) => setTimeout(resolve, 90000)) .then(() => new Promise((resolve, reject) => { s3.listObjects({ Bucket: BUCKET, MaxKeys: 5 }, (err, data) => { if (err) reject(err); @@ -130,7 +130,7 @@ describe('AWS Cloud-based Tests', function () { resolve(bucketContents); }); })) - .then(bucketContents => new Promise((resolve, reject) => { + .then((bucketContents) => new Promise((resolve, reject) => { const key = bucketContents[0].Key; s3.getObject({ Bucket: BUCKET, Key: key, ResponseContentType: 'application/json' }, (err, data) => { if (err) reject(err); @@ -184,7 +184,7 @@ describe('AWS Cloud-based Tests', function () { const startTime = new Date().toISOString(); // metrics take around 2-3 minutes to show up - return new Promise(resolve => setTimeout(resolve, 180000)) + return new Promise((resolve) => setTimeout(resolve, 180000)) .then(() => { // get system poller data const uri = `${constants.BASE_ILX_URI}/systempoller/My_System`; diff --git a/test/functional/cloud/azureTests.js b/test/functional/cloud/azureTests.js index c61b6e9d..e3c5c314 100644 --- a/test/functional/cloud/azureTests.js +++ b/test/functional/cloud/azureTests.js @@ -9,9 +9,9 @@ 'use strict'; const assert = require('assert'); -const constants = require('./../shared/constants'); -const testUtil = require('./../shared/util'); -const azureUtil = require('./../shared/azureUtil'); +const constants = require('../shared/constants'); +const testUtil = require('../shared/util'); +const azureUtil = require('../shared/azureUtil'); const VM_HOSTNAME = process.env[constants.ENV_VARS.AZURE.VM_HOSTNAME]; const VM_IP = process.env[constants.ENV_VARS.AZURE.VM_IP]; @@ -37,7 +37,7 @@ describe('Azure Cloud-based Tests', function () { password: VM_PWD }; - const assertPost = declaration => testUtil.postDeclaration(deviceInfo, declaration) + const assertPost = (declaration) => testUtil.postDeclaration(deviceInfo, declaration) .then((response) => { testUtil.logger.info('Response from declaration post', { hostname: VM_HOSTNAME, response }); return assert.strictEqual(response.message, 'success', 'POST declaration should return success'); @@ -72,7 +72,7 @@ describe('Azure Cloud-based Tests', function () { it('should verify TS service is running', () => { const uri = `${constants.BASE_ILX_URI}/info`; - return new Promise(resolve => setTimeout(resolve, 5000)) + return new Promise((resolve) => setTimeout(resolve, 5000)) .then(() => testUtil.makeRequest(VM_IP, uri, options)) .then((data) => { data = data || {}; @@ -119,7 +119,7 @@ describe('Azure Cloud-based Tests', function () { 'where TimeGenerated > ago(5m)' ].join(' | '); - return new Promise(resolve => setTimeout(resolve, 60000)) + return new Promise((resolve) => setTimeout(resolve, 60000)) .then(() => azureUtil.queryLogs(laReaderToken, WORKSPACE_ID, queryString, CLOUD_TYPE)) .then((results) => { testUtil.logger.info('Response from Log Analytics:', { hostname: VM_HOSTNAME, results }); @@ -158,7 +158,7 @@ describe('Azure Cloud-based Tests', function () { it('should retrieve system poller info from Application Insights', () => { testUtil.logger.info('Delay 120000ms to ensure App Insights api data ready'); - return new Promise(resolve => setTimeout(resolve, 120000)) + return new Promise((resolve) => setTimeout(resolve, 120000)) .then(() => azureUtil.queryAppInsights(APPINS_APP_ID, APPINS_API_KEY, CLOUD_TYPE)) .then((response) => { testUtil.logger.info(response); diff --git a/test/functional/consumerSystemTests.js b/test/functional/consumerSystemTests.js index 47407bc5..450494b5 100644 --- a/test/functional/consumerSystemTests.js +++ b/test/functional/consumerSystemTests.js @@ -49,7 +49,7 @@ function loadConsumers() { // filter consumers by module name if needed if (consumerFilter) { util.logger.info(`Using filter '${consumerFilter}' to filter modules from '${consumerDir}'`); - consumers = consumers.filter(fName => fName.match(new RegExp(consumerFilter, 'i')) !== null); + consumers = consumers.filter((fName) => fName.match(new RegExp(consumerFilter, 'i')) !== null); } const mapping = {}; @@ -100,7 +100,6 @@ function hasMeetRequirements(consumer) { return meet; } - function setup() { describe('Load modules with tests for consumers', () => { // should be loaded at the beginning of process diff --git a/test/functional/consumersTests/azureApplicationInsightsTests.js b/test/functional/consumersTests/azureApplicationInsightsTests.js index 5add1964..c7cb049e 100644 --- a/test/functional/consumersTests/azureApplicationInsightsTests.js +++ b/test/functional/consumersTests/azureApplicationInsightsTests.js @@ -36,7 +36,6 @@ const DECLARATION = JSON.parse(fs.readFileSync(constants.DECL.BASIC)); */ let APPINS_API_DATA; - function setup() { const fileName = process.env[constants.ENV_VARS.AZURE.APPINS_API_DATA]; assert.ok(fileName, `should define env variable ${constants.ENV_VARS.AZURE.APPINS_API_DATA} (path to file with Azure App Insights API info)`); @@ -79,7 +78,7 @@ function test() { instrumentationKey: null, maxBatchIntervalMs: 2000 }; - DUTS.forEach(dut => it(`should configure TS - ${dut.hostalias}`, () => { + DUTS.forEach((dut) => it(`should configure TS - ${dut.hostalias}`, () => { const declaration = testUtil.deepCopy(referenceDeclaration); const apiInfo = getAppInsightAPIInfo(dut.ip); declaration.My_Consumer.instrumentationKey = apiInfo.instrKey; diff --git a/test/functional/consumersTests/azureLogAnalyticsTests.js b/test/functional/consumersTests/azureLogAnalyticsTests.js index 007cd724..7faf7dfa 100644 --- a/test/functional/consumersTests/azureLogAnalyticsTests.js +++ b/test/functional/consumersTests/azureLogAnalyticsTests.js @@ -29,7 +29,6 @@ const AZURE_LA_CONSUMER_NAME = 'Azure_LA_Consumer'; let oauthToken = null; - function setup() { describe('Consumer Setup: Azure Log Analytics - OAuth token', () => { it('should get OAuth token', () => azureUtil.getOAuthToken(CLIENT_ID, CLIENT_SECRET, TENANT_ID) @@ -57,14 +56,14 @@ function test() { cipherText: PASSPHRASE } }; - DUTS.forEach(dut => it( + DUTS.forEach((dut) => it( `should configure TS - ${dut.hostalias}`, () => dutUtils.postDeclarationToDUT(dut, util.deepCopy(consumerDeclaration)) )); it('should send event to TS Event Listener', () => { const msg = `timestamp="${testDataTimestamp}",test="${testDataTimestamp}",testType="${AZURE_LA_CONSUMER_NAME}"`; - return dutUtils.sendDataToEventListeners(dut => `hostname="${dut.hostname}",${msg}`); + return dutUtils.sendDataToEventListeners((dut) => `hostname="${dut.hostname}",${msg}`); }); }); @@ -79,7 +78,7 @@ function test() { `where hostname_s == "${dut.hostname}"`, 'where TimeGenerated > ago(5m)' ].join(' | '); - return new Promise(resolve => setTimeout(resolve, 30000)) + return new Promise((resolve) => setTimeout(resolve, 30000)) .then(() => azureUtil.queryLogs(oauthToken, WORKSPACE_ID, queryString)) .then((results) => { util.logger.info('Response from Log Analytics:', { hostname: dut.hostname, results }); @@ -95,7 +94,7 @@ function test() { `where hostname_s == "${dut.hostname}"`, `where test_s == "${testDataTimestamp}"` ].join(' | '); - return new Promise(resolve => setTimeout(resolve, 10000)) + return new Promise((resolve) => setTimeout(resolve, 10000)) .then(() => azureUtil.queryLogs(oauthToken, WORKSPACE_ID, queryString)) .then((results) => { util.logger.info('Response from Log Analytics:', { hostname: dut.hostname, results }); diff --git a/test/functional/consumersTests/elasticsearchTests.js b/test/functional/consumersTests/elasticsearchTests.js index 45d0263a..9a471f76 100644 --- a/test/functional/consumersTests/elasticsearchTests.js +++ b/test/functional/consumersTests/elasticsearchTests.js @@ -84,7 +84,7 @@ function test() { port: ES_HTTP_PORT }; - return new Promise(resolve => setTimeout(resolve, 5000)) + return new Promise((resolve) => setTimeout(resolve, 5000)) .then(() => util.makeRequest(CONSUMER_HOST.ip, uri, options)) .then((data) => { const nodeInfo = data._nodes; @@ -155,7 +155,7 @@ function test() { ); }; - it('should check for event listener data for', () => new Promise(resolve => setTimeout(resolve, 10000)) + it('should check for event listener data for', () => new Promise((resolve) => setTimeout(resolve, 10000)) .then(() => query(`size=1&q=data.testType:${ES_CONSUMER_NAME}%20AND%20data.hostname=${dut.hostname}`)) .then((data) => { util.logger.info('ElasticSearch response:', data); @@ -179,7 +179,7 @@ function test() { return Promise.resolve(); })); - it('should have system poller data', () => new Promise(resolve => setTimeout(resolve, 10000)) + it('should have system poller data', () => new Promise((resolve) => setTimeout(resolve, 10000)) .then(() => query(`size=1&q=system.hostname:${dut.hostname}`)) .then((data) => { util.logger.info('ElasticSearch response:', data); diff --git a/test/functional/consumersTests/f5CloudTests.js b/test/functional/consumersTests/f5CloudTests.js index 86bcbd1e..eeddee61 100644 --- a/test/functional/consumersTests/f5CloudTests.js +++ b/test/functional/consumersTests/f5CloudTests.js @@ -65,7 +65,7 @@ function setup() { assert.ok(VALID_SERVICE_ACCOUNT.type, 'service account is not valid'); describe('Consumer Setup Check: check bigip requirements', () => { - DUTS.forEach(dut => it( + DUTS.forEach((dut) => it( `get bigip version and check if version is good for F5 Cloud - ${dut.hostalias}`, () => sharedUtil.getBigipVersion(dut) .then((response) => { @@ -133,11 +133,11 @@ function setup() { function test() { const testDataTimestamp = Date.now(); - const msg = hostName => `hostname="${hostName}",testDataTimestamp="${testDataTimestamp}",test="true",testType="${F5_CLOUD_NAME}"`; + const msg = (hostName) => `hostname="${hostName}",testDataTimestamp="${testDataTimestamp}",test="true",testType="${F5_CLOUD_NAME}"`; // .skip() until F5_Cloud interactions resolved describe.skip('Consumer Test: F5 Cloud - Configure TS', () => { - DUTS.forEach(dut => it(`should configure TS - ${dut.hostalias}`, function () { + DUTS.forEach((dut) => it(`should configure TS - ${dut.hostalias}`, function () { if (!SHOULD_RUN_TESTS[dut.hostalias]) { this.skip(); } @@ -158,7 +158,7 @@ function test() { }; return dutUtils.postDeclarationToDUT(dut, sharedUtil.deepCopy(consumerDeclaration)); })); - DUTS.forEach(dut => it(`should send event to TS Event Listener - ${dut.hostalias}`, function () { + DUTS.forEach((dut) => it(`should send event to TS Event Listener - ${dut.hostalias}`, function () { if (!SHOULD_RUN_TESTS[dut.hostalias]) { this.skip(); } @@ -168,7 +168,7 @@ function test() { // .skip() until F5_Cloud interactions resolved describe.skip('Consumer Test: F5 Cloud - Test', () => { - DUTS.forEach(dut => it(`should find the right interactions on mock server - ${dut.hostalias}`, function () { + DUTS.forEach((dut) => it(`should find the right interactions on mock server - ${dut.hostalias}`, function () { if (!SHOULD_RUN_TESTS[dut.hostalias]) { this.skip(); } @@ -190,8 +190,8 @@ function test() { responseDataJSONList.push(jsonData); } }); - assert(responseDataJSONList.some(responseDataJSON => responseDataJSON.hostname === dut.hostname), `Test Error: ${dut.hostname} does not exist`); - assert(responseDataJSONList.every(responseDataJSON => responseDataJSON.testDataTimestamp === testDataTimestamp.toString()), `Test Error: testDataTimestamp should be ${testDataTimestamp}`); + assert(responseDataJSONList.some((responseDataJSON) => responseDataJSON.hostname === dut.hostname), `Test Error: ${dut.hostname} does not exist`); + assert(responseDataJSONList.every((responseDataJSON) => responseDataJSON.testDataTimestamp === testDataTimestamp.toString()), `Test Error: testDataTimestamp should be ${testDataTimestamp}`); } else { assert(false, 'no response from mock server'); } diff --git a/test/functional/consumersTests/fluentdTests.js b/test/functional/consumersTests/fluentdTests.js index 769beff9..645484aa 100644 --- a/test/functional/consumersTests/fluentdTests.js +++ b/test/functional/consumersTests/fluentdTests.js @@ -44,7 +44,6 @@ const FLUENTD_CONF = ` // read in example config const DECLARATION = JSON.parse(fs.readFileSync(constants.DECL.BASIC)); - function runRemoteCmd(cmd) { return util.performRemoteCmd(CONSUMER_HOST.ip, CONSUMER_HOST.username, cmd, { password: CONSUMER_HOST.password }); } @@ -90,14 +89,14 @@ function test() { } ] }; - DUTS.forEach(dut => it( + DUTS.forEach((dut) => it( `should configure TS - ${dut.hostalias}`, () => dutUtils.postDeclarationToDUT(dut, util.deepCopy(consumerDeclaration)) )); it('should send event to TS Event Listener', () => { const msg = `testDataTimestamp="${testDataTimestamp}",test="true",testType="${FLUENTD_CONSUMER_NAME}"`; - return dutUtils.sendDataToEventListeners(dut => `hostname="${dut.hostname}",${msg}`); + return dutUtils.sendDataToEventListeners((dut) => `hostname="${dut.hostname}",${msg}`); }); }); @@ -105,7 +104,7 @@ function test() { const systemPollerData = {}; const fluentLogs = []; - before(() => new Promise(resolve => setTimeout(resolve, 30 * 1000)) + before(() => new Promise((resolve) => setTimeout(resolve, 30 * 1000)) .then(() => dutUtils.getSystemPollersData((hostObj, data) => { systemPollerData[hostObj.hostname] = data[0]; }))); diff --git a/test/functional/consumersTests/googleCloudMonitoringTests.js b/test/functional/consumersTests/googleCloudMonitoringTests.js index 0bcc4d7d..c6ee589c 100644 --- a/test/functional/consumersTests/googleCloudMonitoringTests.js +++ b/test/functional/consumersTests/googleCloudMonitoringTests.js @@ -84,7 +84,7 @@ function test() { serviceEmail: SERVICE_EMAIL, privateKeyId: PRIVATE_KEY_ID }; - DUTS.forEach(dut => it( + DUTS.forEach((dut) => it( `should configure TS - ${dut.hostalias}`, () => dutUtils.postDeclarationToDUT(dut, sharedUtil.deepCopy(consumerDeclaration)) )); @@ -113,7 +113,7 @@ function test() { `interval.endTime=${timeEnd}`, `filter=metric.type="custom.googleapis.com/system/tmmCpu" AND resource.labels.namespace="${dut.hostname}"` ].join('&'); - return new Promise(resolve => setTimeout(resolve, 30000)) + return new Promise((resolve) => setTimeout(resolve, 30000)) .then(() => queryGoogle(queryString)) .then((timeSeries) => { sharedUtil.logger.info('Response from Google Cloud Monitoring:', { hostname: dut.hostname, timeSeries }); diff --git a/test/functional/consumersTests/kafkaTests.js b/test/functional/consumersTests/kafkaTests.js index 03b92ca9..c3323e99 100644 --- a/test/functional/consumersTests/kafkaTests.js +++ b/test/functional/consumersTests/kafkaTests.js @@ -36,7 +36,6 @@ const KAFKA_CONSUMER_NAME = 'Consumer_Kafka'; // read in example config const DECLARATION = JSON.parse(fs.readFileSync(constants.DECL.BASIC)); - function runRemoteCmd(cmd) { return util.performRemoteCmd(CONSUMER_HOST.ip, CONSUMER_HOST.username, cmd, { password: CONSUMER_HOST.password }); } @@ -44,7 +43,7 @@ function runRemoteCmd(cmd) { function setup() { describe('Consumer Setup: Kafka', () => { [KAFKA_IMAGE_NAME, ZOOKEEPER_IMAGE_NAME].forEach( - imageName => it(`should pull ${imageName} docker image`, () => runRemoteCmd(`docker pull ${imageName}`)) + (imageName) => it(`should pull ${imageName} docker image`, () => runRemoteCmd(`docker pull ${imageName}`)) ); it('should start Zookeeper and Kafka docker containers', () => { @@ -87,7 +86,7 @@ function test() { topic: KAFKA_TOPIC, authenticationProtocol: KAFKA_AUTH_PROTOCOL }; - DUTS.forEach(dut => it( + DUTS.forEach((dut) => it( `should configure TS - ${dut.hostalias}`, () => dutUtils.postDeclarationToDUT(dut, util.deepCopy(consumerDeclaration)) )); diff --git a/test/functional/consumersTests/openTelemetryExporterTests.js b/test/functional/consumersTests/openTelemetryExporterTests.js index cf8ade4f..bad41a12 100644 --- a/test/functional/consumersTests/openTelemetryExporterTests.js +++ b/test/functional/consumersTests/openTelemetryExporterTests.js @@ -55,7 +55,6 @@ service: // read in example config const DECLARATION = JSON.parse(fs.readFileSync(constants.DECL.BASIC)); - function runRemoteCmd(cmd) { return testUtil.performRemoteCmd( CONSUMER_HOST.ip, CONSUMER_HOST.username, cmd, { password: CONSUMER_HOST.password } @@ -82,7 +81,7 @@ function setup() { }); }); - DUTS.forEach(dut => it( + DUTS.forEach((dut) => it( `get bigip version and check if version is high enough for OpenTelemetry Exporter - ${dut.hostalias}`, () => testUtil.getBigipVersion(dut) .then((response) => { @@ -164,7 +163,7 @@ function test() { testUtil.logger.info('Delay 15000ms to ensure data is sent to OpenTelemetry Collector'); return testUtil.sleep(15 * 1000) .then(() => testUtil.makeRequest(OTEL_COLLECTOR_HOST, '/metrics', httpOptions)) - .then(response => verifyResponse(response, dut.hostname)); + .then((response) => verifyResponse(response, dut.hostname)); }); }); }); diff --git a/test/functional/consumersTests/splunkTests.js b/test/functional/consumersTests/splunkTests.js index 9ce46535..74eacc17 100644 --- a/test/functional/consumersTests/splunkTests.js +++ b/test/functional/consumersTests/splunkTests.js @@ -40,7 +40,6 @@ const SPLUNK_CONSUMER_NAME = 'Splunk_Consumer'; // read in example config const DECLARATION = JSON.parse(fs.readFileSync(constants.DECL.BASIC)); - function runRemoteCmd(cmd) { return util.performRemoteCmd(CONSUMER_HOST.ip, CONSUMER_HOST.username, cmd, { password: CONSUMER_HOST.password }); } @@ -84,7 +83,7 @@ function test() { }; // splunk container takes about 30 seconds to come up - return new Promise(resolve => setTimeout(resolve, 10000)) + return new Promise((resolve) => setTimeout(resolve, 10000)) .then(() => util.makeRequest(CONSUMER_HOST.ip, uri, options)) .then((data) => { util.logger.info(`Splunk response ${uri}`, data); @@ -116,7 +115,7 @@ function test() { data = data || {}; // check for existence of the token first if (data.entry && data.entry.length) { - const exists = data.entry.filter(item => item.name.indexOf(tokenName) !== -1); + const exists = data.entry.filter((item) => item.name.indexOf(tokenName) !== -1); if (exists.length) { return Promise.resolve({ entry: exists }); // exists, continue } @@ -169,7 +168,7 @@ function test() { data = data || {}; // check for existence of the token first if (data.entry && data.entry.length) { - const exists = data.entry.filter(item => item.name.indexOf(tokenName) !== -1); + const exists = data.entry.filter((item) => item.name.indexOf(tokenName) !== -1); if (exists.length) { return Promise.resolve({ entry: exists }); // exists, continue } @@ -246,7 +245,7 @@ function test() { }; }); - DUTS.forEach(dut => it( + DUTS.forEach((dut) => it( `should configure TS - ${dut.hostalias}`, () => dutUtils.postDeclarationToDUT(dut, util.deepCopy(consumerDeclaration)) )); @@ -258,7 +257,7 @@ function test() { it('should send event to TS Event Listener', () => { const msg = `testDataTimestamp="${testDataTimestamp}",test="true",testType="${SPLUNK_CONSUMER_NAME}"`; - return dutUtils.sendDataToEventListeners(dut => `hostname="${dut.hostname}",${msg}`); + return dutUtils.sendDataToEventListeners((dut) => `hostname="${dut.hostname}",${msg}`); }); } }); @@ -296,7 +295,7 @@ function test() { return new Promise((resolve, reject) => { const waitUntilDone = () => { uri = `${baseUri}/${sid}?${outputMode}`; - return new Promise(resolveTimer => setTimeout(resolveTimer, 100)) + return new Promise((resolveTimer) => setTimeout(resolveTimer, 100)) .then(() => util.makeRequest(CONSUMER_HOST.ip, uri, options)) .then((status) => { const dispatchState = status.entry[0].content.dispatchState; @@ -325,7 +324,7 @@ function test() { const searchQueryEL = () => `search source=f5.telemetry | spath testType | search testType="${SPLUNK_CONSUMER_NAME}" | search hostname="${dut.hostname}" | search testDataTimestamp="${testDataTimestamp}" | head 1`; if (testSetup.format.queryEventsTests) { - it(`should check for system poller data from:${dut.hostalias}`, () => new Promise(resolve => setTimeout(resolve, 30000)) + it(`should check for system poller data from:${dut.hostalias}`, () => new Promise((resolve) => setTimeout(resolve, 30000)) .then(() => { util.logger.info(`Splunk search query for system poller data: ${searchQuerySP()}`); return query(searchQuerySP()); @@ -352,7 +351,7 @@ function test() { } if (testSetup.format.metricsTests) { - it(`should check for system poller metrics from:${dut.hostalias}`, () => new Promise(resolve => setTimeout(resolve, 30000)) + it(`should check for system poller metrics from:${dut.hostalias}`, () => new Promise((resolve) => setTimeout(resolve, 30000)) .then(() => { util.logger.info(`Splunk search query for system poller data: ${searchMetrics()}`); return query(searchMetrics()); @@ -376,7 +375,7 @@ function test() { } if (testSetup.format.eventListenerTests) { - it(`should check for event listener data from:${dut.hostalias}`, () => new Promise(resolve => setTimeout(resolve, 30000)) + it(`should check for event listener data from:${dut.hostalias}`, () => new Promise((resolve) => setTimeout(resolve, 30000)) .then(() => { util.logger.info(`Splunk search query for event listener data: ${searchQueryEL()}`); return query(searchQueryEL()); diff --git a/test/functional/consumersTests/statsdTests.js b/test/functional/consumersTests/statsdTests.js index 42f820b7..08e303fb 100644 --- a/test/functional/consumersTests/statsdTests.js +++ b/test/functional/consumersTests/statsdTests.js @@ -34,7 +34,6 @@ const STATSD_PROTOCOLS = ['tcp', 'udp']; // read in example config const DECLARATION = JSON.parse(fs.readFileSync(constants.DECL.BASIC)); - function runRemoteCmd(cmd) { return util.performRemoteCmd(CONSUMER_HOST.ip, CONSUMER_HOST.username, cmd, { password: CONSUMER_HOST.password }); } @@ -46,7 +45,7 @@ function setup() { } function test() { - STATSD_PROTOCOLS.forEach(protocol => describe(`Consumer Test: Statsd | Protocol: ${protocol}`, () => { + STATSD_PROTOCOLS.forEach((protocol) => describe(`Consumer Test: Statsd | Protocol: ${protocol}`, () => { const containerName = `${STATSD_CONTAINER_NAME}-${protocol}`; describe('Consumer Test: Statsd - Configure Service', () => { it('should start container', () => { @@ -73,7 +72,7 @@ function test() { }; // splunk container takes about 15 seconds to come up - return new Promise(resolve => setTimeout(resolve, 1500)) + return new Promise((resolve) => setTimeout(resolve, 1500)) .then(() => util.makeRequest(CONSUMER_HOST.ip, uri, options)) .then((data) => { util.logger.info('Statsd response:', data); @@ -92,7 +91,7 @@ function test() { protocol, port: STATSD_DATA_PORT }; - DUTS.forEach(dut => it( + DUTS.forEach((dut) => it( `should configure TS - ${dut.hostalias}`, () => dutUtils.postDeclarationToDUT(dut, util.deepCopy(consumerDeclaration)) )); @@ -112,7 +111,7 @@ function test() { }; return util.makeRequest(CONSUMER_HOST.ip, uri, options) - .then(data => Promise.resolve([searchString, data])); + .then((data) => Promise.resolve([searchString, data])); }; const stripMetrics = (data) => { @@ -135,10 +134,10 @@ function test() { const hostnamePrefix = data.system ? data.system.hostname : DEFAULT_HOSTNAME; // account for item in path having '.' or '/' - return diff.map(item => [ + return diff.map((item) => [ basePrefix, hostnamePrefix - ].concat(item.path).map(i => i.replace(/\.|\/|:/g, '-')).join('.')); + ].concat(item.path).map((i) => i.replace(/\.|\/|:/g, '-')).join('.')); }; const verifyMetrics = (metrics) => { @@ -178,7 +177,7 @@ function test() { * Sleep for 30 second(s) and return Promise.reject to allow retry */ util.logger.info('Waiting for data to be indexed...'); - return new Promise(resolveTimer => setTimeout(resolveTimer, 30000)) + return new Promise((resolveTimer) => setTimeout(resolveTimer, 30000)) .then(() => Promise.reject(new Error('Metrics are empty / not indexed'))); }); }; @@ -215,7 +214,7 @@ function test() { function teardown() { describe('Consumer Test: Statsd - teardown', () => { - STATSD_PROTOCOLS.forEach(protocol => it( + STATSD_PROTOCOLS.forEach((protocol) => it( `should remove ${protocol} container(s)`, () => runRemoteCmd(`docker container rm -f ${STATSD_CONTAINER_NAME}-${protocol}`) )); }); diff --git a/test/functional/deployment/declaration.yml b/test/functional/deployment/declaration.yml index 33fff80f..93139976 100644 --- a/test/functional/deployment/declaration.yml +++ b/test/functional/deployment/declaration.yml @@ -38,13 +38,6 @@ request: _copy: request/software/bigip_15_1 type: bigip flavor: F5-BIGIP-small - - harness: - _copy: request/hypervisor - name: bigip_16_0 - software: - _copy: request/software/bigip_16_0 - type: bigip - flavor: F5-BIGIP-small - harness: _copy: request/hypervisor name: bigip_16_1 @@ -69,33 +62,24 @@ request: image: BIGIP-13.0.0.0.0.1650 desired: branch: '' - build: 0.0.8 + build: 0.0.4 iso_file: '' - version: 14.1.4.3 + version: 14.1.4.4 bigip_15_1: default: force: false image: BIGIP-13.0.0.0.0.1650 desired: branch: '' - build: 0.0.18 + build: 0.0.15 iso_file: '' - version: 15.1.3.1 - bigip_16_0: - default: - force: false - image: BIGIP-16.0.0-0.0.12 - desired: - branch: '' - build: 0.0.8 - iso_file: '' - version: 16.0.1.2 + version: 15.1.4.1 bigip_16_1: default: force: false image: BIGIP-16.0.0-0.0.12 desired: branch: '' - build: 0.0.19 + build: 0.0.18 iso_file: '' - version: 16.1.0 \ No newline at end of file + version: 16.1.2 \ No newline at end of file diff --git a/test/functional/dutTests.js b/test/functional/dutTests.js index a4838b31..643b01cb 100644 --- a/test/functional/dutTests.js +++ b/test/functional/dutTests.js @@ -27,7 +27,6 @@ const packageDetails = util.getPackageDetails(); const basicDeclaration = JSON.parse(fs.readFileSync(constants.DECL.BASIC)); const namespaceDeclaration = JSON.parse(fs.readFileSync(constants.DECL.BASIC_NAMESPACE)); - /** * Post declaration to TS on DUT * @@ -56,7 +55,7 @@ function postDeclarationToDUT(dut, declaration) { * @returns {Object} Promise resolved when all requests succeed */ function postDeclarationToDUTs(callback) { - return Promise.all(duts.map(dut => postDeclarationToDUT(dut, callback(dut)))); + return Promise.all(duts.map((dut) => postDeclarationToDUT(dut, callback(dut)))); } /** @@ -83,7 +82,7 @@ function sendDataToEventListener(dut, message, opts) { resolve(); return; } - new Promise(timeoutResolve => setTimeout(timeoutResolve, opts.delay)) + new Promise((timeoutResolve) => setTimeout(timeoutResolve, opts.delay)) .then(() => util.sendEvent(dut.ip, message)) .then(() => sendData(i + 1)) .catch(reject); @@ -102,7 +101,7 @@ function sendDataToEventListener(dut, message, opts) { * @returns {Object} Promise resolved when all messages were sent to Event Listeners */ function sendDataToEventListeners(callback, numOfMsg, delay) { - return Promise.all(duts.map(dut => sendDataToEventListener(dut, callback(dut), { numOfMsg, delay }))); + return Promise.all(duts.map((dut) => sendDataToEventListener(dut, callback(dut), { numOfMsg, delay }))); } /** @@ -174,8 +173,8 @@ function getSystemPollerData(dut, sysPollerName) { */ function getSystemPollersData(callback) { return Promise.all(duts.map( - dut => getSystemPollerData(dut, constants.DECL.SYSTEM_NAME) - .then(data => callback(dut, data)) + (dut) => getSystemPollerData(dut, constants.DECL.SYSTEM_NAME) + .then((data) => callback(dut, data)) )); } @@ -194,9 +193,9 @@ function uninstallAllTSpackages(host, authToken, options) { let data; return util.getInstalledPackages(host, authToken) - .then(installedPackages => Promise.all(installedPackages - .filter(pkg => pkg.packageName.includes('f5-telemetry')) - .map(pkg => util.uninstallPackage(host, authToken, pkg.packageName)))) + .then((installedPackages) => Promise.all(installedPackages + .filter((pkg) => pkg.packageName.includes('f5-telemetry')) + .map((pkg) => util.uninstallPackage(host, authToken, pkg.packageName)))) .then(() => util.makeRequest(host, uri, options)) .then((resp) => { data = resp; @@ -214,7 +213,6 @@ function uninstallAllTSpackages(host, authToken, options) { }); } - function setup() { // get package details const packageFile = packageDetails.name; @@ -302,7 +300,7 @@ function setup() { it('should verify installation', () => { const uri = `${constants.BASE_ILX_URI}/info`; - return new Promise(resolve => setTimeout(resolve, 5000)) + return new Promise((resolve) => setTimeout(resolve, 5000)) .then(() => util.makeRequest(host, uri, options)) .then((data) => { data = data || {}; @@ -343,7 +341,7 @@ function setup() { .then(() => util.makeRequest(host, uri, options)) .then((data) => { // check if rule already exists - if (data.items.some(i => i.name === ruleName) === true) { + if (data.items.some((i) => i.name === ruleName) === true) { // exists, delete the rule const deleteOptions = { method: 'DELETE', @@ -366,7 +364,7 @@ function setup() { constants.EVENT_LISTENER_SECONDARY_PORT, constants.EVENT_LISTENER_NAMESPACE_PORT, constants.EVENT_LISTENER_NAMESPACE_SECONDARY_PORT - ].map(port => ({ name: String(port) })) + ].map((port) => ({ name: String(port) })) } }); const postOptions = { @@ -560,7 +558,6 @@ function test() { }); }); - it('should get response from systempoller endpoint', () => { const uri = `${constants.BASE_ILX_URI}${namespacePath}/systempoller/${constants.DECL.SYSTEM_NAME}`; // wait 500ms in case if config was not applied yet @@ -581,7 +578,7 @@ function test() { }); it('should ensure event listener is up', () => { - const connectToEventListener = port => new Promise((resolve, reject) => { + const connectToEventListener = (port) => new Promise((resolve, reject) => { const client = net.createConnection({ host, port }, () => { client.end(); }); @@ -594,12 +591,12 @@ function test() { }); // ports = { opened: [], closed: [] } - const checkPorts = ports => Promise.all( + const checkPorts = (ports) => Promise.all( (ports.opened || []).map( - openedPort => assert.isFulfilled(connectToEventListener(openedPort)) + (openedPort) => assert.isFulfilled(connectToEventListener(openedPort)) ).concat( (ports.closed || []).map( - closedPort => connectToEventListener(closedPort) + (closedPort) => connectToEventListener(closedPort) .then( () => Promise.reject(new Error(`Port ${closedPort} expected to be closed`)), () => {} // do nothing on catch @@ -613,14 +610,14 @@ function test() { if (obj.class === 'Telemetry_Listener') { cb(obj); } else { - Object.keys(obj).forEach(key => findListeners(obj[key], cb)); + Object.keys(obj).forEach((key) => findListeners(obj[key], cb)); } } }; const fetchListenerPorts = (decl) => { const ports = []; - findListeners(decl, listener => ports.push(listener.port || 6514)); + findListeners(decl, (listener) => ports.push(listener.port || 6514)); return ports; }; @@ -650,14 +647,14 @@ function test() { }) .then(() => { const ports = { opened: fetchListenerPorts(postOptions.body) }; - ports.closed = allListenerPorts.filter(port => ports.opened.indexOf(port) === -1); + ports.closed = allListenerPorts.filter((port) => ports.opened.indexOf(port) === -1); return checkPorts(ports); }) // post declaration again and check that listeners are still available .then(() => util.makeRequest(host, uri, util.deepCopy(postOptions))) .then(() => { const ports = { opened: fetchListenerPorts(postOptions.body) }; - ports.closed = allListenerPorts.filter(port => ports.opened.indexOf(port) === -1); + ports.closed = allListenerPorts.filter((port) => ports.opened.indexOf(port) === -1); return checkPorts(ports); }) .then(() => { @@ -676,14 +673,14 @@ function test() { }) .then(() => { const ports = { opened: fetchListenerPorts(postOptions.body) }; - ports.closed = allListenerPorts.filter(port => ports.opened.indexOf(port) === -1); + ports.closed = allListenerPorts.filter((port) => ports.opened.indexOf(port) === -1); return checkPorts(ports); }) // post declaration again and check that listeners are still available .then(() => util.makeRequest(host, uri, util.deepCopy(postOptions))) .then(() => { const ports = { opened: fetchListenerPorts(postOptions.body) }; - ports.closed = allListenerPorts.filter(port => ports.opened.indexOf(port) === -1); + ports.closed = allListenerPorts.filter((port) => ports.opened.indexOf(port) === -1); return checkPorts(ports); }) .then(() => { diff --git a/test/functional/pullConsumerSystemTests.js b/test/functional/pullConsumerSystemTests.js index 655be8b9..8c28fb40 100644 --- a/test/functional/pullConsumerSystemTests.js +++ b/test/functional/pullConsumerSystemTests.js @@ -27,7 +27,7 @@ function loadPullConsumers() { // filter consumers by module name if needed if (pullConsumerFilter) { util.logger.info(`Using filter '${pullConsumerFilter}' to filter modules from '${pullConsumerDir}'`); - pullConsumers = pullConsumers.filter(fName => fName.match(new RegExp(pullConsumerFilter, 'i')) !== null); + pullConsumers = pullConsumers.filter((fName) => fName.match(new RegExp(pullConsumerFilter, 'i')) !== null); } const mapping = {}; diff --git a/test/functional/pullConsumersTests/defaultTests.js b/test/functional/pullConsumersTests/defaultTests.js index 16432033..dc92d191 100644 --- a/test/functional/pullConsumersTests/defaultTests.js +++ b/test/functional/pullConsumersTests/defaultTests.js @@ -39,7 +39,7 @@ function test() { describe('Pull Consumer Test: default consumer type - no namespace', () => { describe('default - Configure TS', () => { - DUTS.forEach(dut => it( + DUTS.forEach((dut) => it( `should configure TS - ${dut.hostalias}`, () => dutUtils.postDeclarationToDUT(dut, util.deepCopy(BASIC_DECL)) )); @@ -67,7 +67,7 @@ function test() { describe('Pull Consumer Test: default consumer type - with namespace', () => { describe('default with namespace - Configure TS', () => { - DUTS.forEach(dut => it( + DUTS.forEach((dut) => it( `should configure TS - ${dut.hostalias}`, () => dutUtils.postDeclarationToDUT(dut, util.deepCopy(NAMESPACE_DECL)) )); diff --git a/test/functional/pullConsumersTests/prometheusTests.js b/test/functional/pullConsumersTests/prometheusTests.js index 1ea79582..f6dc0799 100644 --- a/test/functional/pullConsumersTests/prometheusTests.js +++ b/test/functional/pullConsumersTests/prometheusTests.js @@ -60,7 +60,7 @@ function test() { BASIC_DECL[pullConsumerName].type = PROMETHEUS_PULL_CONSUMER_TYPE; }); describe('Prometheus - Configure TS', () => { - DUTS.forEach(dut => it( + DUTS.forEach((dut) => it( `should configure TS - ${dut.hostalias}`, () => dutUtils.postDeclarationToDUT(dut, util.deepCopy(BASIC_DECL)) )); @@ -94,7 +94,7 @@ function test() { NAMESPACE_DECL[namespace][pullConsumerName].type = PROMETHEUS_PULL_CONSUMER_TYPE; }); describe('Prometheus with namespace - Configure TS', () => { - DUTS.forEach(dut => it( + DUTS.forEach((dut) => it( `should configure TS - ${dut.hostalias}`, () => dutUtils.postDeclarationToDUT(dut, util.deepCopy(NAMESPACE_DECL)) )); diff --git a/test/functional/shared/azureUtil.js b/test/functional/shared/azureUtil.js index dd8a8917..ef2cc5a0 100644 --- a/test/functional/shared/azureUtil.js +++ b/test/functional/shared/azureUtil.js @@ -10,7 +10,6 @@ const util = require('./util'); - function getOAuthToken(clientId, clientSecret, tenantId, cloudType) { const loginDomain = cloudType === 'gov' ? 'login.microsoftonline.us' : 'login.microsoftonline.com'; const resource = cloudType === 'gov' ? 'https://api.loganalytics.us/' : 'https://api.loganalytics.io/'; diff --git a/test/functional/shared/util.js b/test/functional/shared/util.js index 99f792cb..e265342e 100644 --- a/test/functional/shared/util.js +++ b/test/functional/shared/util.js @@ -45,7 +45,6 @@ const makeRequestWithRetry = function (makeRequest, interval, maxRetries) { }); }; - module.exports = { makeRequestWithRetry, @@ -106,7 +105,7 @@ module.exports = { const dir = `${__dirname}/../../../dist`; const distFiles = fs.readdirSync(dir); - const packageFiles = distFiles.filter(f => f.endsWith('.rpm')); + const packageFiles = distFiles.filter((f) => f.endsWith('.rpm')); // get latest rpm file (by timestamp since epoch) // note: this might not work if the artifact resets the timestamps @@ -207,7 +206,7 @@ module.exports = { }; return this.makeRequest(host, uri, postOptions) - .then(data => ({ token: data.token.token })) + .then((data) => ({ token: data.token.token })) .catch((err) => { const msg = `getAuthToken: ${err}`; throw new Error(msg); @@ -387,9 +386,9 @@ module.exports = { if (testHarnessFile && fs.existsSync(testHarnessFile)) { let filter; if (harnessType === 'BIGIP') { - filter = item => item.is_f5_device && item.type === 'bigip'; + filter = (item) => item.is_f5_device && item.type === 'bigip'; } else { - filter = item => !item.is_f5_device; + filter = (item) => !item.is_f5_device; } // eslint-disable-next-line import/no-dynamic-require, global-require hosts = require(testHarnessFile).filter(filter).map((item) => { @@ -412,7 +411,7 @@ module.exports = { }); } else if (envVars && envVars.IP && process.env[envVars.IP]) { // straight up environment variables - could be 1+ hosts: x.x.x.x,x.x.x.y - hosts = process.env[envVars.IP].split(',').map(host => ({ + hosts = process.env[envVars.IP].split(',').map((host) => ({ ip: host, username: process.env[envVars.USER], password: process.env[envVars.PWD] @@ -506,7 +505,7 @@ module.exports = { * @returns {Promise} */ sleep(sleepTime) { - return new Promise(resolve => setTimeout(resolve, sleepTime)); + return new Promise((resolve) => setTimeout(resolve, sleepTime)); }, /** @@ -534,6 +533,6 @@ module.exports = { }; return this.makeRequest(host, uri, postOptions); }) - .then(response => response.selfLink.split('ver=')[1]); + .then((response) => response.selfLink.split('ver=')[1]); } }; diff --git a/test/unit/.mocha.opts b/test/unit/.mocha.opts index 5068714a..3cf04427 100644 --- a/test/unit/.mocha.opts +++ b/test/unit/.mocha.opts @@ -1,5 +1,4 @@ # mocha.opts --require ./test/unit/shared/bootstrap.js - --recursive ./test/unit/**/*.js --timeout 600000 --reporter ./test/customMochaReporter.js \ No newline at end of file diff --git a/test/unit/configTests.js b/test/unit/configTests.js index 2ca4dd6c..23df36ed 100644 --- a/test/unit/configTests.js +++ b/test/unit/configTests.js @@ -19,9 +19,12 @@ const configTestsData = require('./data/configTestsData'); const configWorker = require('../../src/lib/config'); const constants = require('../../src/lib/constants'); const deviceUtil = require('../../src/lib/utils/device'); +const dummies = require('./shared/dummies'); +const logger = require('../../src/lib/logger'); const persistentStorage = require('../../src/lib/persistentStorage'); const stubs = require('./shared/stubs'); const teemReporter = require('../../src/lib/teemReporter'); +const testAssert = require('./shared/assert'); const testUtil = require('./shared/util'); const utilMisc = require('../../src/lib/utils/misc'); @@ -41,6 +44,7 @@ describe('Config', () => { coreStub = stubs.coreStub({ configWorker, deviceUtil, + logger, persistentStorage, teemReporter, utilMisc @@ -458,11 +462,11 @@ describe('Config', () => { const testSuites = [ { suite: configTestsData.processDeclaration, - entryPoint: options => configWorker.processDeclaration(declaration, options) + entryPoint: (options) => configWorker.processDeclaration(declaration, options) }, { suite: configTestsData.processNamespaceDeclaration, - entryPoint: options => configWorker.processNamespaceDeclaration(declaration, namespaceName, options) + entryPoint: (options) => configWorker.processNamespaceDeclaration(declaration, namespaceName, options) } ]; testSuites.forEach((testSuite) => { @@ -584,4 +588,34 @@ describe('Config', () => { }); }); }); + + describe('\'error\' event', () => { + it('should log error if caught', () => configWorker.safeEmitAsync('error', new Error('expected error')) + .then(() => { + testAssert.includeMatch( + coreStub.logger.messages.all, + /Unhandled error in ConfigWorker[\s\S]+expected error/gm, + 'should log error message' + ); + })); + }); + + describe('\'change\' event', () => { + it('should set log level', () => configWorker.processDeclaration(dummies.declaration.base.decrypted({ + controls: dummies.declaration.controls.full.decrypted({ + logLevel: 'error' + }) + })) + .then(() => { + assert.deepStrictEqual(coreStub.logger.logLevelHistory.slice(-1), ['error'], 'should set log level to error'); + return configWorker.processDeclaration(dummies.declaration.base.decrypted({ + controls: dummies.declaration.controls.full.decrypted({ + logLevel: 'debug' + }) + })); + }) + .then(() => { + assert.deepStrictEqual(coreStub.logger.logLevelHistory.slice(-1), ['debug'], 'should set log level to debug'); + })); + }); }); diff --git a/test/unit/consumers/awsCloudWatchConsumerTests.js b/test/unit/consumers/awsCloudWatchConsumerTests.js index d6e17f20..3ad4eed5 100644 --- a/test/unit/consumers/awsCloudWatchConsumerTests.js +++ b/test/unit/consumers/awsCloudWatchConsumerTests.js @@ -73,6 +73,47 @@ describe('AWS_CloudWatch', () => { }); }); + describe('Endpoints', () => { + beforeEach(() => { + sinon.stub(AWS.config, 'update').resolves(); + sinon.stub(AWS, 'Endpoint').callsFake((params) => ({ params })); + }); + + it('should supply endpointUrl to Monitoring Logs client', () => { + let cloudWatchLogsConstructorParams; + sinon.stub(AWS, 'CloudWatchLogs').callsFake((cloudWatchLogsParams) => { + cloudWatchLogsConstructorParams = cloudWatchLogsParams; + }); + + const context = testUtil.buildConsumerContext({ + config: { + endpointUrl: 'full-endpoint-url' + } + }); + + return awsCloudWatchIndex(context) + .then(() => assert.deepStrictEqual(cloudWatchLogsConstructorParams.endpoint, { params: 'full-endpoint-url' })); + }); + + it('should supply endpointUrl to Monitoring Metrics client', () => { + let cloudWatchConstructorParams; + sinon.stub(AWS, 'CloudWatch').callsFake((cloudWatchParams) => { + cloudWatchConstructorParams = cloudWatchParams; + }); + + const context = testUtil.buildConsumerContext({ + eventType: 'systemInfo', + config: { + dataType: 'metrics', + endpointUrl: 'full-endpoint-url' + } + }); + + return awsCloudWatchIndex(context) + .then(() => assert.deepStrictEqual(cloudWatchConstructorParams.endpoint, { params: 'full-endpoint-url' })); + }); + }); + describe('Logs', () => { let clock; let putLogEventsStub; @@ -111,7 +152,7 @@ describe('AWS_CloudWatch', () => { ] }) }), - putLogEvents: params => ({ + putLogEvents: (params) => ({ promise: () => putLogEventsStub(params) }) }); @@ -173,7 +214,7 @@ describe('AWS_CloudWatch', () => { beforeEach(() => { getMetricsSpy = sinon.spy(awsUtil, 'getMetrics'); sinon.stub(AWS, 'CloudWatch').returns({ - putMetricData: params => ({ + putMetricData: (params) => ({ promise: () => putMetricsStub(params) }) }); diff --git a/test/unit/consumers/awsS3ConsumerTests.js b/test/unit/consumers/awsS3ConsumerTests.js index 4c6d5a46..d29c21b4 100644 --- a/test/unit/consumers/awsS3ConsumerTests.js +++ b/test/unit/consumers/awsS3ConsumerTests.js @@ -28,6 +28,7 @@ describe('AWS_S3', () => { let clock; let awsConfigUpdate; let s3PutObjectParams; + let s3ConstructorParams; const defaultConsumerConfig = { region: 'us-west-1', @@ -42,12 +43,16 @@ describe('AWS_S3', () => { beforeEach(() => { awsConfigUpdate = sinon.stub(AWS.config, 'update').resolves(); - sinon.stub(AWS, 'S3').returns({ - putObject: (params, cb) => { - s3PutObjectParams = params; - cb(null, ''); - } + sinon.stub(AWS, 'S3').callsFake((s3Params) => { + s3ConstructorParams = s3Params; + return { + putObject: (params, cb) => { + s3PutObjectParams = params; + cb(null, ''); + } + }; }); + // stub getDate() since it attempts to convert into 'local' time. sinon.stub(Date.prototype, 'getDate').returns('4'); // Fake the clock to get a consistent S3 object Key value (which are partitioned by time) @@ -99,6 +104,21 @@ describe('AWS_S3', () => { }); }); + it('should supply endpointUrl to AWS client', () => { + sinon.stub(AWS, 'Endpoint').callsFake((params) => ({ params })); + + const context = testUtil.buildConsumerContext({ + config: { + endpointUrl: 'full-endpoint-url' + } + }); + + return awsS3Index(context) + .then(() => { + assert.deepStrictEqual(s3ConstructorParams.endpoint, { params: 'full-endpoint-url' }); + }); + }); + it('should configure AWS access with custom agent', () => { let optionsParam; awsConfigUpdate.callsFake((options) => { diff --git a/test/unit/consumers/awsUtilTests.js b/test/unit/consumers/awsUtilTests.js index 0baae51d..db5762c5 100644 --- a/test/unit/consumers/awsUtilTests.js +++ b/test/unit/consumers/awsUtilTests.js @@ -17,7 +17,7 @@ const chaiAsPromised = require('chai-as-promised'); const aws = require('aws-sdk'); const https = require('https'); -const testUtil = require('./../shared/util'); +const testUtil = require('../shared/util'); const awsUtil = require('../../../src/lib/consumers/shared/awsUtil'); const awsUtilTestsData = require('./data/awsUtilTestsData'); @@ -107,7 +107,7 @@ describe('AWS Util Tests', () => { const certs = awsUtil.getAWSRootCerts(); assert.ok(Array.isArray(certs), 'certs should be a valid array'); assert.ok(certs.every( - i => i.startsWith('-----BEGIN CERTIFICATE-----') + (i) => i.startsWith('-----BEGIN CERTIFICATE-----') ), 'certs should have \'BEGIN CERTIFICATE\' header'); }); }); @@ -124,7 +124,7 @@ describe('AWS Util Tests', () => { describe('getDefaultDimensions', () => { const testSet = awsUtilTestsData.getDefaultDimensions; - testSet.tests.forEach(testConf => testUtil.getCallableIt(testConf)(testConf.name, () => { + testSet.tests.forEach((testConf) => testUtil.getCallableIt(testConf)(testConf.name, () => { const actualMetrics = awsUtil.getDefaultDimensions(testConf.input.data); return assert.deepStrictEqual(actualMetrics, testConf.expected); })); @@ -132,7 +132,7 @@ describe('AWS Util Tests', () => { describe('getMetrics', () => { const testSet = awsUtilTestsData.getMetrics; - testSet.tests.forEach(testConf => testUtil.getCallableIt(testConf)(testConf.name, () => { + testSet.tests.forEach((testConf) => testUtil.getCallableIt(testConf)(testConf.name, () => { clock = sinon.useFakeTimers(new Date(testSet.timestamp)); const actualMetrics = awsUtil.getMetrics(testConf.input.data, testConf.input.defDimensions); return assert.deepStrictEqual(actualMetrics, testConf.expected); @@ -146,7 +146,7 @@ describe('AWS Util Tests', () => { beforeEach(() => { batches = []; sinon.stub(aws, 'CloudWatch').returns({ - putMetricData: batch => ({ + putMetricData: (batch) => ({ promise: () => putMetricsStub(batch) }) }); @@ -157,7 +157,7 @@ describe('AWS Util Tests', () => { }); const testSet = awsUtilTestsData.sendMetrics; - testSet.tests.forEach(testConf => testUtil.getCallableIt(testConf)(testConf.name, () => { + testSet.tests.forEach((testConf) => testUtil.getCallableIt(testConf)(testConf.name, () => { clock = sinon.useFakeTimers(testSet.timestamp); const context = testUtil.buildConsumerContext({ eventType: 'systemInfo', @@ -182,7 +182,7 @@ describe('AWS Util Tests', () => { beforeEach(() => { sinon.stub(aws, 'CloudWatch').returns({ - putMetricData: batch => ({ + putMetricData: (batch) => ({ promise: () => putMetricsStub(batch) }) }); diff --git a/test/unit/consumers/azureApplicationInsightsConsumerTests.js b/test/unit/consumers/azureApplicationInsightsConsumerTests.js index c4f351b2..7f2a8a0c 100644 --- a/test/unit/consumers/azureApplicationInsightsConsumerTests.js +++ b/test/unit/consumers/azureApplicationInsightsConsumerTests.js @@ -15,6 +15,8 @@ const chai = require('chai'); const chaiAsPromised = require('chai-as-promised'); const sinon = require('sinon'); const appInsights = require('applicationinsights'); +// eslint-disable-next-line import/no-dynamic-require +const appInsightsLogging = require(require.resolve('applicationinsights/out/Library/Logging')); const requestsUtil = require('../../../src/lib/utils/requests'); const azureAppInsightsIndex = require('../../../src/lib/consumers/Azure_Application_Insights/index'); @@ -39,6 +41,7 @@ describe('Azure_Application_Insights', () => { sinon.stub(aiSpy.TelemetryClient.prototype, 'trackMetric').callsFake((metric) => { requests.push(metric); }); + sinon.stub(appInsightsLogging, 'warn'); // stub metadata calls sinon.stub(requestsUtil, 'makeRequest').resolves({}); }); @@ -87,7 +90,7 @@ describe('Azure_Application_Insights', () => { aiSpy.setup.withArgs('should-not-be-called-guid').notCalled, 'The app insights client should not be setup when skipping metrics' ); - assert.strictEqual(aiSpy.TelemetryClient.getCalls().find(c => c.args[0] === 'should-not-be-called-guid'), undefined); + assert.strictEqual(aiSpy.TelemetryClient.getCalls().find((c) => c.args[0] === 'should-not-be-called-guid'), undefined); assert.isEmpty(requests); }); }); @@ -120,7 +123,7 @@ describe('Azure_Application_Insights', () => { return azureAppInsightsIndex(context) .then(() => { const actualClientConfig = aiSpy.TelemetryClient.getCalls() - .find(c => c.args[0] === 'the-instr-key-guid').returnValue.config; + .find((c) => c.args[0] === 'the-instr-key-guid').returnValue.config; assert.strictEqual(actualClientConfig.maxBatchSize, 250); assert.strictEqual(actualClientConfig.maxBatchIntervalMs, 5000); @@ -161,7 +164,7 @@ describe('Azure_Application_Insights', () => { return azureAppInsightsIndex(context) .then(() => { const actualClientConfig = aiSpy.TelemetryClient.getCalls() - .find(c => c.args[0] === 'InstrumentationKey=customized-instr-key-guid;EndpointSuffix=applicationinsights.us').returnValue.config; + .find((c) => c.args[0] === 'InstrumentationKey=customized-instr-key-guid;EndpointSuffix=applicationinsights.us').returnValue.config; // note that maxBatchSize prop overrides value provided in customOpts assert.strictEqual(actualClientConfig.maxBatchSize, 222); assert.strictEqual(actualClientConfig.maxBatchIntervalMs, 3333); @@ -196,8 +199,8 @@ describe('Azure_Application_Insights', () => { return azureAppInsightsIndex(context) .then(() => { const setupCalls = aiSpy.TelemetryClient.getCalls(); - assert.notStrictEqual(setupCalls.find(c => c.args[0] === 'app1-moooooo-mi-guid'), undefined); - assert.notStrictEqual(setupCalls.find(c => c.args[0] === 'InstrumentationKey=optional-conn-props;EndpointSuffix=also-optional'), undefined); + assert.notStrictEqual(setupCalls.find((c) => c.args[0] === 'app1-moooooo-mi-guid'), undefined); + assert.notStrictEqual(setupCalls.find((c) => c.args[0] === 'InstrumentationKey=optional-conn-props;EndpointSuffix=also-optional'), undefined); const expectedMetric1 = { name: 'F5_num', value: 1234 }; const expectedMetric2 = { name: 'F5_strNum', value: 24.1 }; diff --git a/test/unit/consumers/azureLogAnalyticsConsumerTests.js b/test/unit/consumers/azureLogAnalyticsConsumerTests.js index b9a71bbf..776bd267 100644 --- a/test/unit/consumers/azureLogAnalyticsConsumerTests.js +++ b/test/unit/consumers/azureLogAnalyticsConsumerTests.js @@ -39,13 +39,13 @@ describe('Azure_Log_Analytics', () => { }; const getOpsInsightsReq = () => { - const opInsightsReq = requests.find(r => r.fullURI === 'https://myWorkspace.ods.opinsights.azure.com/api/logs?api-version=2016-04-01'); + const opInsightsReq = requests.find((r) => r.fullURI === 'https://myWorkspace.ods.opinsights.azure.com/api/logs?api-version=2016-04-01'); assert.notStrictEqual(opInsightsReq, undefined); return opInsightsReq; }; const getAllOpsInsightsReqs = () => { - const opInsightsReqs = requests.filter(r => r.fullURI === 'https://myWorkspace.ods.opinsights.azure.com/api/logs?api-version=2016-04-01'); + const opInsightsReqs = requests.filter((r) => r.fullURI === 'https://myWorkspace.ods.opinsights.azure.com/api/logs?api-version=2016-04-01'); assert.notStrictEqual(opInsightsReqs, undefined); return opInsightsReqs; }; diff --git a/test/unit/consumers/azureUtilTests.js b/test/unit/consumers/azureUtilTests.js index d225cde0..ce1fe00f 100644 --- a/test/unit/consumers/azureUtilTests.js +++ b/test/unit/consumers/azureUtilTests.js @@ -15,10 +15,10 @@ const chai = require('chai'); const chaiAsPromised = require('chai-as-promised'); const sinon = require('sinon'); -const azureUtil = require('./../../../src/lib/consumers/shared/azureUtil'); +const azureUtil = require('../../../src/lib/consumers/shared/azureUtil'); const azureUtilTestsData = require('./data/azureUtilTestsData'); -const requestsUtil = require('./../../../src/lib/utils/requests'); -const testUtil = require('./../shared/util'); +const requestsUtil = require('../../../src/lib/utils/requests'); +const testUtil = require('../shared/util'); chai.use(chaiAsPromised); const assert = chai.assert; @@ -166,7 +166,7 @@ describe('Azure Util Tests', () => { } }); return azureUtil.getSharedKey(context) - .then(key => assert.strictEqual(key, 'the-hidden-key')); + .then((key) => assert.strictEqual(key, 'the-hidden-key')); }); }); @@ -334,7 +334,7 @@ describe('Azure Util Tests', () => { Object.keys(azureUtilTestsData).forEach((testSetKey) => { const testSet = azureUtilTestsData[testSetKey]; testUtil.getCallableDescribe(testSet)(testSet.name, () => { - testSet.tests.forEach(testConf => testUtil.getCallableIt(testConf)(testConf.name, () => { + testSet.tests.forEach((testConf) => testUtil.getCallableIt(testConf)(testConf.name, () => { const context = testUtil.buildConsumerContext({ config: testConf.config, metadata: testConf.metadata }); diff --git a/test/unit/consumers/data/awsUtilTestsData.js b/test/unit/consumers/data/awsUtilTestsData.js index 3f4d2a10..140e89ea 100644 --- a/test/unit/consumers/data/awsUtilTestsData.js +++ b/test/unit/consumers/data/awsUtilTestsData.js @@ -1,4 +1,3 @@ - /* * Copyright 2021. F5 Networks, Inc. See End User License Agreement ("EULA") for * license terms. Notwithstanding anything to the contrary in the EULA, Licensee diff --git a/test/unit/consumers/data/azureLogAnalyticsConsumerTestsData.js b/test/unit/consumers/data/azureLogAnalyticsConsumerTestsData.js index bab89d34..2a2f0fc8 100644 --- a/test/unit/consumers/data/azureLogAnalyticsConsumerTestsData.js +++ b/test/unit/consumers/data/azureLogAnalyticsConsumerTestsData.js @@ -582,6 +582,7 @@ module.exports = { availabilityState: 'available', enabledState: 'enabled', monitorStatus: 'up', + poolName: '/Common/app.app/app_pool', port: 0, 'serverside.bitsIn': 0, 'serverside.bitsOut': 0, @@ -615,6 +616,7 @@ module.exports = { availabilityState: 'available', enabledState: 'enabled', monitorStatus: 'down', + poolName: '/Common/telemetry-local', port: 0, 'serverside.bitsIn': 0, 'serverside.bitsOut': 0, @@ -648,6 +650,7 @@ module.exports = { availabilityState: 'offline', enabledState: 'enabled', monitorStatus: 'up', + poolName: '/Example_Tenant/A1/hsl_pool', port: 0, 'serverside.bitsIn': 0, 'serverside.bitsOut': 0, @@ -681,6 +684,7 @@ module.exports = { availabilityState: 'offline', enabledState: 'enabled', monitorStatus: 'up', + poolName: '/Example_Tenant/A1/web_pool', port: 0, 'serverside.bitsIn': 0, 'serverside.bitsOut': 0, @@ -696,6 +700,7 @@ module.exports = { availabilityState: 'offline', enabledState: 'enabled', monitorStatus: 'up', + poolName: '/Example_Tenant/A1/web_pool', port: 0, 'serverside.bitsIn': 0, 'serverside.bitsOut': 0, @@ -722,7 +727,7 @@ module.exports = { ], fullURI: 'https://myWorkspace.ods.opinsights.azure.com/api/logs?api-version=2016-04-01', headers: { - Authorization: 'SharedKey myWorkspace:1oFmIkjCcPPDKVHQCVaCRMod3Cvpx4nc15WA/MWjNJw=', + Authorization: 'SharedKey myWorkspace:3rxtwjADHseA7h/Ws12q1LhH0HMX+ubWNizGXTGKztg=', 'Content-Type': 'application/json', 'Log-Type': 'F5Telemetry_pools', 'x-ms-date': 'Thu, 01 Jan 1970 00:00:00 GMT' diff --git a/test/unit/consumers/data/openTelemetryExporterConsumerTestsData.js b/test/unit/consumers/data/openTelemetryExporterConsumerTestsData.js index 4cd61a53..5bc29e00 100644 --- a/test/unit/consumers/data/openTelemetryExporterConsumerTestsData.js +++ b/test/unit/consumers/data/openTelemetryExporterConsumerTestsData.js @@ -5059,6 +5059,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Common/app.app/app_pool' + }, { key: 'port', value: '0' @@ -5094,6 +5098,10 @@ module.exports = { key: 'monitorStatus', value: 'down' }, + { + key: 'poolName', + value: '/Common/telemetry-local' + }, { key: 'port', value: '0' @@ -5129,6 +5137,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Example_Tenant/A1/hsl_pool' + }, { key: 'port', value: '0' @@ -5164,6 +5176,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Example_Tenant/A1/web_pool' + }, { key: 'port', value: '0' @@ -5199,6 +5215,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Example_Tenant/A1/web_pool' + }, { key: 'port', value: '0' @@ -5234,6 +5254,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Common/app.app/app_pool' + }, { key: 'port', value: '0' @@ -5269,6 +5293,10 @@ module.exports = { key: 'monitorStatus', value: 'down' }, + { + key: 'poolName', + value: '/Common/telemetry-local' + }, { key: 'port', value: '0' @@ -5304,6 +5332,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Example_Tenant/A1/hsl_pool' + }, { key: 'port', value: '0' @@ -5339,6 +5371,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Example_Tenant/A1/web_pool' + }, { key: 'port', value: '0' @@ -5374,6 +5410,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Example_Tenant/A1/web_pool' + }, { key: 'port', value: '0' @@ -5409,6 +5449,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Common/app.app/app_pool' + }, { key: 'port', value: '0' @@ -5444,6 +5488,10 @@ module.exports = { key: 'monitorStatus', value: 'down' }, + { + key: 'poolName', + value: '/Common/telemetry-local' + }, { key: 'port', value: '0' @@ -5479,6 +5527,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Example_Tenant/A1/hsl_pool' + }, { key: 'port', value: '0' @@ -5514,6 +5566,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Example_Tenant/A1/web_pool' + }, { key: 'port', value: '0' @@ -5549,6 +5605,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Example_Tenant/A1/web_pool' + }, { key: 'port', value: '0' @@ -5584,6 +5644,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Common/app.app/app_pool' + }, { key: 'port', value: '0' @@ -5619,6 +5683,10 @@ module.exports = { key: 'monitorStatus', value: 'down' }, + { + key: 'poolName', + value: '/Common/telemetry-local' + }, { key: 'port', value: '0' @@ -5654,6 +5722,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Example_Tenant/A1/hsl_pool' + }, { key: 'port', value: '0' @@ -5689,6 +5761,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Example_Tenant/A1/web_pool' + }, { key: 'port', value: '0' @@ -5724,6 +5800,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Example_Tenant/A1/web_pool' + }, { key: 'port', value: '0' @@ -5759,6 +5839,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Common/app.app/app_pool' + }, { key: 'port', value: '0' @@ -5794,6 +5878,10 @@ module.exports = { key: 'monitorStatus', value: 'down' }, + { + key: 'poolName', + value: '/Common/telemetry-local' + }, { key: 'port', value: '0' @@ -5829,6 +5917,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Example_Tenant/A1/hsl_pool' + }, { key: 'port', value: '0' @@ -5864,6 +5956,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Example_Tenant/A1/web_pool' + }, { key: 'port', value: '0' @@ -5899,6 +5995,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Example_Tenant/A1/web_pool' + }, { key: 'port', value: '0' @@ -5934,6 +6034,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Common/app.app/app_pool' + }, { key: 'port', value: '0' @@ -5969,6 +6073,10 @@ module.exports = { key: 'monitorStatus', value: 'down' }, + { + key: 'poolName', + value: '/Common/telemetry-local' + }, { key: 'port', value: '0' @@ -6004,6 +6112,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Example_Tenant/A1/hsl_pool' + }, { key: 'port', value: '0' @@ -6039,6 +6151,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Example_Tenant/A1/web_pool' + }, { key: 'port', value: '0' @@ -6074,6 +6190,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Example_Tenant/A1/web_pool' + }, { key: 'port', value: '0' @@ -6109,6 +6229,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Common/app.app/app_pool' + }, { key: 'port', value: '0' @@ -6144,6 +6268,10 @@ module.exports = { key: 'monitorStatus', value: 'down' }, + { + key: 'poolName', + value: '/Common/telemetry-local' + }, { key: 'port', value: '0' @@ -6179,6 +6307,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Example_Tenant/A1/hsl_pool' + }, { key: 'port', value: '0' @@ -6214,6 +6346,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Example_Tenant/A1/web_pool' + }, { key: 'port', value: '0' @@ -6249,6 +6385,10 @@ module.exports = { key: 'monitorStatus', value: 'up' }, + { + key: 'poolName', + value: '/Example_Tenant/A1/web_pool' + }, { key: 'port', value: '0' diff --git a/test/unit/consumers/data/statsdConsumerTestsData.js b/test/unit/consumers/data/statsdConsumerTestsData.js index 31da96e7..6c2c9755 100644 --- a/test/unit/consumers/data/statsdConsumerTestsData.js +++ b/test/unit/consumers/data/statsdConsumerTestsData.js @@ -1238,6 +1238,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'available', enabledState: 'enabled', + poolName: '/Common/app.app/app_pool', port: 0, 'status.statusReason': 'Pool member is available' } @@ -1250,6 +1251,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'available', enabledState: 'enabled', + poolName: '/Common/app.app/app_pool', port: 0, 'status.statusReason': 'Pool member is available' } @@ -1262,6 +1264,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'available', enabledState: 'enabled', + poolName: '/Common/app.app/app_pool', port: 0, 'status.statusReason': 'Pool member is available' } @@ -1274,6 +1277,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'available', enabledState: 'enabled', + poolName: '/Common/app.app/app_pool', port: 0, 'status.statusReason': 'Pool member is available' } @@ -1286,6 +1290,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'available', enabledState: 'enabled', + poolName: '/Common/app.app/app_pool', port: 0, 'status.statusReason': 'Pool member is available' } @@ -1298,6 +1303,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'available', enabledState: 'enabled', + poolName: '/Common/app.app/app_pool', port: 0, 'status.statusReason': 'Pool member is available' } @@ -1310,6 +1316,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'available', enabledState: 'enabled', + poolName: '/Common/app.app/app_pool', port: 0, 'status.statusReason': 'Pool member is available' } @@ -1410,6 +1417,7 @@ module.exports = { monitorStatus: 'down', availabilityState: 'available', enabledState: 'enabled', + poolName: '/Common/telemetry-local', port: 0, 'status.statusReason': 'Pool member has been marked down by a monitor' } @@ -1422,6 +1430,7 @@ module.exports = { monitorStatus: 'down', availabilityState: 'available', enabledState: 'enabled', + poolName: '/Common/telemetry-local', port: 0, 'status.statusReason': 'Pool member has been marked down by a monitor' } @@ -1434,6 +1443,7 @@ module.exports = { monitorStatus: 'down', availabilityState: 'available', enabledState: 'enabled', + poolName: '/Common/telemetry-local', port: 0, 'status.statusReason': 'Pool member has been marked down by a monitor' } @@ -1446,6 +1456,7 @@ module.exports = { monitorStatus: 'down', availabilityState: 'available', enabledState: 'enabled', + poolName: '/Common/telemetry-local', port: 0, 'status.statusReason': 'Pool member has been marked down by a monitor' } @@ -1458,6 +1469,7 @@ module.exports = { monitorStatus: 'down', availabilityState: 'available', enabledState: 'enabled', + poolName: '/Common/telemetry-local', port: 0, 'status.statusReason': 'Pool member has been marked down by a monitor' } @@ -1470,6 +1482,7 @@ module.exports = { monitorStatus: 'down', availabilityState: 'available', enabledState: 'enabled', + poolName: '/Common/telemetry-local', port: 0, 'status.statusReason': 'Pool member has been marked down by a monitor' } @@ -1482,6 +1495,7 @@ module.exports = { monitorStatus: 'down', availabilityState: 'available', enabledState: 'enabled', + poolName: '/Common/telemetry-local', port: 0, 'status.statusReason': 'Pool member has been marked down by a monitor' } @@ -1590,6 +1604,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'offline', enabledState: 'enabled', + poolName: '/Example_Tenant/A1/hsl_pool', port: 0, 'status.statusReason': 'Pool member is available' } @@ -1602,6 +1617,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'offline', enabledState: 'enabled', + poolName: '/Example_Tenant/A1/hsl_pool', port: 0, 'status.statusReason': 'Pool member is available' } @@ -1614,6 +1630,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'offline', enabledState: 'enabled', + poolName: '/Example_Tenant/A1/hsl_pool', port: 0, 'status.statusReason': 'Pool member is available' } @@ -1626,6 +1643,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'offline', enabledState: 'enabled', + poolName: '/Example_Tenant/A1/hsl_pool', port: 0, 'status.statusReason': 'Pool member is available' } @@ -1638,6 +1656,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'offline', enabledState: 'enabled', + poolName: '/Example_Tenant/A1/hsl_pool', port: 0, 'status.statusReason': 'Pool member is available' } @@ -1650,6 +1669,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'offline', enabledState: 'enabled', + poolName: '/Example_Tenant/A1/hsl_pool', port: 0, 'status.statusReason': 'Pool member is available' } @@ -1662,6 +1682,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'offline', enabledState: 'enabled', + poolName: '/Example_Tenant/A1/hsl_pool', port: 0, 'status.statusReason': 'Pool member is available' } @@ -1770,6 +1791,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'offline', enabledState: 'enabled', + poolName: '/Example_Tenant/A1/web_pool', port: 0, 'status.statusReason': 'Pool member is available' } @@ -1782,6 +1804,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'offline', enabledState: 'enabled', + poolName: '/Example_Tenant/A1/web_pool', port: 0, 'status.statusReason': 'Pool member is available' } @@ -1794,6 +1817,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'offline', enabledState: 'enabled', + poolName: '/Example_Tenant/A1/web_pool', port: 0, 'status.statusReason': 'Pool member is available' } @@ -1806,6 +1830,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'offline', enabledState: 'enabled', + poolName: '/Example_Tenant/A1/web_pool', port: 0, 'status.statusReason': 'Pool member is available' } @@ -1818,6 +1843,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'offline', enabledState: 'enabled', + poolName: '/Example_Tenant/A1/web_pool', port: 0, 'status.statusReason': 'Pool member is available' } @@ -1830,6 +1856,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'offline', enabledState: 'enabled', + poolName: '/Example_Tenant/A1/web_pool', port: 0, 'status.statusReason': 'Pool member is available' } @@ -1842,6 +1869,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'offline', enabledState: 'enabled', + poolName: '/Example_Tenant/A1/web_pool', port: 0, 'status.statusReason': 'Pool member is available' } @@ -1854,6 +1882,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'offline', enabledState: 'enabled', + poolName: '/Example_Tenant/A1/web_pool', port: 0, 'status.statusReason': 'Pool member is available' } @@ -1866,6 +1895,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'offline', enabledState: 'enabled', + poolName: '/Example_Tenant/A1/web_pool', port: 0, 'status.statusReason': 'Pool member is available' } @@ -1878,6 +1908,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'offline', enabledState: 'enabled', + poolName: '/Example_Tenant/A1/web_pool', port: 0, 'status.statusReason': 'Pool member is available' } @@ -1890,6 +1921,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'offline', enabledState: 'enabled', + poolName: '/Example_Tenant/A1/web_pool', port: 0, 'status.statusReason': 'Pool member is available' } @@ -1902,6 +1934,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'offline', enabledState: 'enabled', + poolName: '/Example_Tenant/A1/web_pool', port: 0, 'status.statusReason': 'Pool member is available' } @@ -1914,6 +1947,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'offline', enabledState: 'enabled', + poolName: '/Example_Tenant/A1/web_pool', port: 0, 'status.statusReason': 'Pool member is available' } @@ -1926,6 +1960,7 @@ module.exports = { monitorStatus: 'up', availabilityState: 'offline', enabledState: 'enabled', + poolName: '/Example_Tenant/A1/web_pool', port: 0, 'status.statusReason': 'Pool member is available' } diff --git a/test/unit/consumers/dataDogConsumerTests.js b/test/unit/consumers/dataDogConsumerTests.js index 199034ee..dc965a1c 100644 --- a/test/unit/consumers/dataDogConsumerTests.js +++ b/test/unit/consumers/dataDogConsumerTests.js @@ -189,19 +189,118 @@ describe('DataDog', () => { describe(`region === "${region}"`, () => { DATA_DOG_COMPRESSION_TYPES.forEach((compressionType) => { describe(`compressionType === "${compressionType}"`, () => { - it('should process systemInfo data', () => { + const metricPrefixTests = [ + { + testName: 'no metricPrefix', + isDefined: 'system.cpu', + isUndefined: 'f5.bigip.system.cpu' + }, + { + testName: 'custom metricPrefix, length = 1', + metricPrefix: ['f5'], + isDefined: 'f5.system.cpu', + isUndefined: 'system.cpu' + }, + { + testName: 'custom metricPrefix, length = 3', + metricPrefix: ['f5', 'bigip', 'device'], + isDefined: 'f5.bigip.device.system.cpu', + isUndefined: 'system.cpu' + } + ]; + + metricPrefixTests.forEach((metricPrefixTest) => { + it(`should process systemInfo data (${metricPrefixTest.testName})`, () => { + const context = testUtil.buildConsumerContext({ + eventType: 'systemInfo', + config: addGzipConfigIfNeed(defaultConsumerConfig, compressionType) + }); + context.config.metricPrefix = metricPrefixTest.metricPrefix; + context.config.region = region; + const reqConfig = getRequestConfig(DATA_DOG_TYPES.METRICS, region); + + setupDataDogMockEndpoint(addGzipReqHeadersIfNeed(reqConfig, compressionType, 'deflate')); + return dataDogIndex(context) + .then(() => { + const timeSeries = dataDogRequestData[0].request.series; + const systemCpu = timeSeries.find( + (series) => series.metric === metricPrefixTest.isDefined + ); + + assert.lengthOf(dataDogRequestData, 1, 'should log 1 request'); + assert.isNotEmpty(timeSeries, 'should have some metric data'); + assert.isDefined( + timeSeries.find((series) => series.metric === metricPrefixTest.isDefined), + `should have found '${metricPrefixTest.isDefined}' metric name` + ); + assert.isUndefined( + timeSeries.find((series) => series.metric === metricPrefixTest.isUndefined), + `should have found '${metricPrefixTest.isDefined}' metric name` + ); + assert.includeMembers( + systemCpu.tags, + ['configSyncSucceeded:true'], + 'should have configSyncSucceeded boolean tag on \'system.cpu\' metric' + ); + checkGzipReqHeadersIfNeeded(dataDogRequestData[0], compressionType, 'deflate'); + }); + }); + }); + + it('should process systemInfo data, and append custom tags', () => { + const context = testUtil.buildConsumerContext({ + eventType: 'systemInfo', + config: addGzipConfigIfNeed(defaultConsumerConfig, compressionType) + }); + context.config.region = region; + context.config.customTags = [ + { name: 'deploymentName', value: 'best version' }, + { name: 'instanceId', value: 'instance1' } + ]; + const reqConfig = getRequestConfig(DATA_DOG_TYPES.METRICS, region); + + setupDataDogMockEndpoint(addGzipReqHeadersIfNeed(reqConfig, compressionType, 'deflate')); + return dataDogIndex(context) + .then(() => { + const timeSeriesTagsSample = dataDogRequestData[0].request.series + .filter((_, index) => index % 10 === 0); // Sample 10% + + assert.isAbove(timeSeriesTagsSample.length, 1, 'should have multiple time series'); + timeSeriesTagsSample.forEach((tagSet) => { + assert.includeMembers(tagSet.tags, ['deploymentName:best version', 'instanceId:instance1']); + }); + checkGzipReqHeadersIfNeeded(dataDogRequestData[0], compressionType, 'deflate'); + }); + }); + + it('should process systemInfo data, and convert booleans to metrics', () => { const context = testUtil.buildConsumerContext({ eventType: 'systemInfo', config: addGzipConfigIfNeed(defaultConsumerConfig, compressionType) }); context.config.region = region; + context.config.convertBooleansToMetrics = true; const reqConfig = getRequestConfig(DATA_DOG_TYPES.METRICS, region); setupDataDogMockEndpoint(addGzipReqHeadersIfNeed(reqConfig, compressionType, 'deflate')); return dataDogIndex(context) .then(() => { + const timeSeries = dataDogRequestData[0].request.series; + const configSyncSucceeded = timeSeries.find( + (series) => series.metric === 'system.configSyncSucceeded' + ); + const systemCpu = timeSeries.find( + (series) => series.metric === 'system.cpu' + ); + assert.lengthOf(dataDogRequestData, 1, 'should log 1 request'); - assert.isNotEmpty(dataDogRequestData[0].request.series, 'should have some metric data'); + assert.isNotEmpty(timeSeries, 'should have some metric data'); + assert.isDefined(configSyncSucceeded, 'should include \'system.configSyncSucceeded\' as a metric'); + assert.notIncludeMembers( + systemCpu.tags, + ['configSyncSucceeded:true'], + 'should not have configSyncSucceeded tag on \'system.cpu\' metric' + ); checkGzipReqHeadersIfNeeded(dataDogRequestData[0], compressionType, 'deflate'); }); }); @@ -217,8 +316,10 @@ describe('DataDog', () => { setupDataDogMockEndpoint(addGzipReqHeadersIfNeed(reqConfig, compressionType, 'deflate')); return dataDogIndex(context) .then(() => { + const timeSeries = dataDogRequestData[0].request.series; + assert.lengthOf(dataDogRequestData, 1, 'should log 1 request'); - assert.isNotEmpty(dataDogRequestData[0].request.series, 'should have some metric data'); + assert.isNotEmpty(timeSeries, 'should have some metric data'); checkGzipReqHeadersIfNeeded(dataDogRequestData[0], compressionType, 'deflate'); }); }); @@ -241,6 +342,35 @@ describe('DataDog', () => { assert.lengthOf(dataDogRequestData, 1, 'should log 1 request'); assert.deepStrictEqual(dataDogRequestData[0].request.ddsource, 'LTM', 'should set source type to LTM'); assert.deepStrictEqual(dataDogRequestData[0].request.service, 'f5-telemetry', 'should set service to default'); + assert.strictEqual(dataDogRequestData[0].request.ddtags, 'telemetryEventCategory:LTM,key:value', 'should set tags from event data'); + checkGzipReqHeadersIfNeeded(dataDogRequestData[0], compressionType, 'gzip'); + }); + }); + + it('should process event listener event log without metrics (LTM), and append custom tags', () => { + const context = testUtil.buildConsumerContext({ + config: addGzipConfigIfNeed(defaultConsumerConfig, compressionType) + }); + context.event.type = 'LTM'; + context.event.data = { + key: 'value', + telemetryEventCategory: 'LTM' + }; + context.config.region = region; + context.config.customTags = [ + { name: 'deploymentName', value: 'best version' }, + { name: 'instanceId', value: 'instance1' } + ]; + const reqConfig = getRequestConfig(DATA_DOG_TYPES.LOGS, region); + + setupDataDogMockEndpoint(addGzipReqHeadersIfNeed(reqConfig, compressionType, 'gzip')); + return dataDogIndex(context) + .then(() => { + assert.lengthOf(dataDogRequestData, 1, 'should log 1 request'); + assert.deepStrictEqual(dataDogRequestData[0].request.ddsource, 'LTM', 'should set source type to LTM'); + assert.deepStrictEqual(dataDogRequestData[0].request.service, 'f5-telemetry', 'should set service to default'); + assert.include(dataDogRequestData[0].request.ddtags, 'telemetryEventCategory:LTM,key:value', 'should not overwrite dynamic tags'); + assert.include(dataDogRequestData[0].request.ddtags, 'deploymentName:best version,instanceId:instance1', 'should include custom tags'); checkGzipReqHeadersIfNeeded(dataDogRequestData[0], compressionType, 'gzip'); }); }); @@ -264,6 +394,36 @@ describe('DataDog', () => { assert.lengthOf(dataDogRequestData, 1, 'should log 1 request'); assert.deepStrictEqual(dataDogRequestData[0].request.ddsource, 'syslog', 'should set source type to syslog'); assert.deepStrictEqual(dataDogRequestData[0].request.service, 'f5-telemetry', 'should set service to default'); + assert.strictEqual(dataDogRequestData[0].request.ddtags, 'telemetryEventCategory:syslog', 'should set tags from event data'); + checkGzipReqHeadersIfNeeded(dataDogRequestData[0], compressionType, 'gzip'); + }); + }); + + it('should process event listener event log without metrics (syslog), and append custom tags', () => { + const context = testUtil.buildConsumerContext({ + config: addGzipConfigIfNeed(defaultConsumerConfig, compressionType) + }); + context.event.type = 'syslog'; + context.event.data = { + data: '<134>Jul 6 22:37:49 bigip14.1.2.3.test info httpd(pam_audit)[13810]: 01070417:6: AUDIT - user admin - RAW: httpd(pam_audit): user=admin(admin) partition=[All] level=Administrator tty=(unknown) host=172.18.5.167 attempts=1 start="Mon Jul 6 22:37:49 2020" end="Mon Jul 6 22:37:49 2020"', + hostname: 'bigip14.1.2.3.test', + telemetryEventCategory: 'syslog' + }; + context.config.region = region; + context.config.customTags = [ + { name: 'deploymentName', value: 'best version' }, + { name: 'instanceId', value: 'instance1' } + ]; + const reqConfig = getRequestConfig(DATA_DOG_TYPES.LOGS, region); + + setupDataDogMockEndpoint(addGzipReqHeadersIfNeed(reqConfig, compressionType, 'gzip')); + return dataDogIndex(context) + .then(() => { + assert.lengthOf(dataDogRequestData, 1, 'should log 1 request'); + assert.deepStrictEqual(dataDogRequestData[0].request.ddsource, 'syslog', 'should set source type to syslog'); + assert.deepStrictEqual(dataDogRequestData[0].request.service, 'f5-telemetry', 'should set service to default'); + assert.include(dataDogRequestData[0].request.ddtags, 'telemetryEventCategory:syslog', 'should not overwrite dynamic tags'); + assert.include(dataDogRequestData[0].request.ddtags, 'deploymentName:best version,instanceId:instance1', 'should include custom tags'); checkGzipReqHeadersIfNeeded(dataDogRequestData[0], compressionType, 'gzip'); }); }); diff --git a/test/unit/consumers/gcpUtilTests.js b/test/unit/consumers/gcpUtilTests.js index 903671c6..9eef973a 100644 --- a/test/unit/consumers/gcpUtilTests.js +++ b/test/unit/consumers/gcpUtilTests.js @@ -16,7 +16,7 @@ const chaiAsPromised = require('chai-as-promised'); const sinon = require('sinon'); const jwt = require('jsonwebtoken'); -const gcpUtil = require('./../../../src/lib/consumers/shared/gcpUtil'); +const gcpUtil = require('../../../src/lib/consumers/shared/gcpUtil'); const nock = require('nock'); chai.use(chaiAsPromised); @@ -52,7 +52,11 @@ describe('Google Cloud Util Tests', () => { return accessTokenResponse; }); - return gcpUtil.getAccessToken({ privateKey: 'key', privateKeyId: 'keyId' }) + return gcpUtil.getAccessToken( + { + useServiceAccountToken: false, privateKey: 'key', privateKeyId: 'keyId' + } + ) .then((token) => { assert.isTrue(nock.isDone()); assert.strictEqual(token, 'hereHaveSomeAccess'); @@ -76,4 +80,23 @@ describe('Google Cloud Util Tests', () => { }); }); }); + + describe('getAccessToken', () => { + const accessTokenResponse = { + access_token: 'hereHaveSomeAccess', + expires_in: 1000 + }; + + it('should get an Access Token from the instance metadata', () => { + nock('http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/abc') + .get('/token') + .reply(200, () => accessTokenResponse); + + return gcpUtil.getAccessToken({ useServiceAccountToken: true, serviceEmail: 'abc' }) + .then((token) => { + assert.isTrue(nock.isDone()); + assert.strictEqual(token, 'hereHaveSomeAccess'); + }); + }); + }); }); diff --git a/test/unit/consumers/googleCloudLoggingTests.js b/test/unit/consumers/googleCloudLoggingTests.js index 221961d0..dada3581 100644 --- a/test/unit/consumers/googleCloudLoggingTests.js +++ b/test/unit/consumers/googleCloudLoggingTests.js @@ -89,7 +89,7 @@ describe('Google_Cloud_Logging', () => { }); nock('https://oauth2.googleapis.com/token') - .post('', body => body.assertion === '12345::theprivatekeyvalue') + .post('', (body) => body.assertion === '12345::theprivatekeyvalue') .reply(200, { access_token: 'aToken', expires_in: tokenDuration }); nock('https://logging.googleapis.com/v2/entries:write', { @@ -112,7 +112,7 @@ describe('Google_Cloud_Logging', () => { }); nock('https://oauth2.googleapis.com/token') - .post('', body => body.assertion === '12345::theprivatekeyvalue') + .post('', (body) => body.assertion === '12345::theprivatekeyvalue') .reply(200, { access_token: 'aToken', expires_in: tokenDuration }); nock('https://logging.googleapis.com/v2/entries:write', { @@ -147,7 +147,7 @@ describe('Google_Cloud_Logging', () => { ); nock('https://oauth2.googleapis.com/token') - .post('', body => body.assertion === '12345::theprivatekeyvalue') + .post('', (body) => body.assertion === '12345::theprivatekeyvalue') .reply(200, { access_token: 'aToken', expires_in: tokenDuration }); nock('https://logging.googleapis.com/v2/entries:write', { @@ -171,7 +171,7 @@ describe('Google_Cloud_Logging', () => { ); nock('https://oauth2.googleapis.com/token') - .post('', body => body.assertion === '12345::theprivatekeyvalue') + .post('', (body) => body.assertion === '12345::theprivatekeyvalue') .reply(200, { access_token: 'aToken', expires_in: tokenDuration }); nock('https://logging.googleapis.com/v2/entries:write', { @@ -221,7 +221,7 @@ describe('Google_Cloud_Logging', () => { ); nock('https://oauth2.googleapis.com/token') - .post('', body => body.assertion === '12345::theprivatekeyvalue') + .post('', (body) => body.assertion === '12345::theprivatekeyvalue') .reply(200, { access_token: 'aToken', expires_in: tokenDuration }); nock('https://logging.googleapis.com/v2/entries:write', { @@ -251,7 +251,7 @@ describe('Google_Cloud_Logging', () => { ); nock('https://oauth2.googleapis.com/token') - .post('', body => body.assertion === '12345::theprivatekeyvalue') + .post('', (body) => body.assertion === '12345::theprivatekeyvalue') .reply(200, { access_token: 'aToken', expires_in: tokenDuration }); nock('https://logging.googleapis.com/v2/entries:write', { @@ -276,7 +276,7 @@ describe('Google_Cloud_Logging', () => { ); nock('https://oauth2.googleapis.com/token') - .post('', body => body.assertion === '12345::theprivatekeyvalue') + .post('', (body) => body.assertion === '12345::theprivatekeyvalue') .times(2) .reply(200, { access_token: 'aToken', expires_in: tokenDuration }); @@ -324,11 +324,11 @@ describe('Google_Cloud_Logging', () => { ); nock('https://oauth2.googleapis.com/token') - .post('', body => body.assertion === 'privateKey1::firstKey') + .post('', (body) => body.assertion === 'privateKey1::firstKey') .reply(200, { access_token: 'tokenA', expires_in: tokenDuration }); nock('https://oauth2.googleapis.com/token') - .post('', body => body.assertion === 'privateKey2::secondKey') + .post('', (body) => body.assertion === 'privateKey2::secondKey') .reply(200, { access_token: 'tokenB', expires_in: tokenDuration }); nock('https://logging.googleapis.com/v2/entries:write', { @@ -367,7 +367,7 @@ describe('Google_Cloud_Logging', () => { let tokenCounter = 0; nock('https://oauth2.googleapis.com/token') - .post('', body => body.assertion === '12345::theprivatekeyvalue') + .post('', (body) => body.assertion === '12345::theprivatekeyvalue') .times(2) .reply(200, () => { tokenCounter += 1; @@ -376,7 +376,7 @@ describe('Google_Cloud_Logging', () => { nock('https://logging.googleapis.com/v2/entries:write', { reqheaders: { - authorization: a => a === `Bearer token:${tokenCounter}` + authorization: (a) => a === `Bearer token:${tokenCounter}` } }) .post('') diff --git a/test/unit/consumers/googleCloudMonitoringConsumerTests.js b/test/unit/consumers/googleCloudMonitoringConsumerTests.js index c52ae79f..f63dc8c2 100644 --- a/test/unit/consumers/googleCloudMonitoringConsumerTests.js +++ b/test/unit/consumers/googleCloudMonitoringConsumerTests.js @@ -179,12 +179,12 @@ describe('Google_Cloud_Monitoring', () => { .then((result) => { assert.equal(result, undefined); }) - .catch(err => Promise.reject(err)); + .catch((err) => Promise.reject(err)); }); it('should process systemInfo data', () => { nock('https://oauth2.googleapis.com/token') - .post('', body => body.assertion === '12345::theprivatekeyvalue') + .post('', (body) => body.assertion === '12345::theprivatekeyvalue') .reply(200, { access_token: 'aToken', expires_in: tokenDuration }); nock('https://monitoring.googleapis.com/v3/projects/theProject/metricDescriptors', { @@ -231,7 +231,7 @@ describe('Google_Cloud_Monitoring', () => { }); nock('https://oauth2.googleapis.com/token') - .post('', body => body.assertion === '12345::theprivatekeyvalue') + .post('', (body) => body.assertion === '12345::theprivatekeyvalue') .reply(200, { access_token: 'aToken', expires_in: tokenDuration }); nock('https://monitoring.googleapis.com/v3/projects/theProject/metricDescriptors', { @@ -269,7 +269,7 @@ describe('Google_Cloud_Monitoring', () => { it('should process systemInfo data, when metadata reporting disabled', () => { nock('https://oauth2.googleapis.com/token') - .post('', body => body.assertion === '12345::theprivatekeyvalue') + .post('', (body) => body.assertion === '12345::theprivatekeyvalue') .reply(200, { access_token: 'aToken', expires_in: tokenDuration }); nock('https://monitoring.googleapis.com/v3/projects/theProject/metricDescriptors', { @@ -307,7 +307,7 @@ describe('Google_Cloud_Monitoring', () => { it('should process systemInfo data, with a cached access token', () => { nock('https://oauth2.googleapis.com/token') - .post('', body => body.assertion === '12345::theprivatekeyvalue') + .post('', (body) => body.assertion === '12345::theprivatekeyvalue') .times(2) .reply(200, { access_token: 'aToken', expires_in: tokenDuration }); @@ -362,7 +362,7 @@ describe('Google_Cloud_Monitoring', () => { it('should process systemInfo data, with multiple cached access tokens', () => { // Private Key 1 nocks nock('https://oauth2.googleapis.com/token') - .post('', body => body.assertion === 'privateKey1::firstKey') + .post('', (body) => body.assertion === 'privateKey1::firstKey') .reply(200, { access_token: 'accessTokenOne', expires_in: tokenDuration }); nock('https://monitoring.googleapis.com/v3/projects/theProject/metricDescriptors', { @@ -394,7 +394,7 @@ describe('Google_Cloud_Monitoring', () => { // Private Key 2 nocks nock('https://oauth2.googleapis.com/token') - .post('', body => body.assertion === 'privateKey2::secondKey') + .post('', (body) => body.assertion === 'privateKey2::secondKey') .reply(200, { access_token: 'accessTokenTwo', expires_in: tokenDuration }); nock('https://monitoring.googleapis.com/v3/projects/theProject/metricDescriptors', { @@ -441,7 +441,7 @@ describe('Google_Cloud_Monitoring', () => { let tokenCounter = 0; nock('https://oauth2.googleapis.com/token') - .post('', body => body.assertion === '12345::theprivatekeyvalue') + .post('', (body) => body.assertion === '12345::theprivatekeyvalue') .times(2) .reply(200, () => { tokenCounter += 1; @@ -450,7 +450,7 @@ describe('Google_Cloud_Monitoring', () => { nock('https://monitoring.googleapis.com/v3/projects/theProject/metricDescriptors', { reqheaders: { - authorization: a => a === `Bearer token:${tokenCounter}` + authorization: (a) => a === `Bearer token:${tokenCounter}` } }) .get('') @@ -459,7 +459,7 @@ describe('Google_Cloud_Monitoring', () => { nock('https://monitoring.googleapis.com/v3/projects/theProject/metricDescriptors', { reqheaders: { - authorization: a => a === `Bearer token:${tokenCounter}` + authorization: (a) => a === `Bearer token:${tokenCounter}` } }) .post('', metricDescriptors.onPost) @@ -468,7 +468,7 @@ describe('Google_Cloud_Monitoring', () => { nock('https://monitoring.googleapis.com/v3/projects/theProject/timeSeries', { reqheaders: { - authorization: a => a === `Bearer token:${tokenCounter}` + authorization: (a) => a === `Bearer token:${tokenCounter}` } }) .post('', testUtil.deepCopy(getOriginTimeSeries())) diff --git a/test/unit/consumers/metricsUtilTests.js b/test/unit/consumers/metricsUtilTests.js index 3596e445..f5b27acf 100644 --- a/test/unit/consumers/metricsUtilTests.js +++ b/test/unit/consumers/metricsUtilTests.js @@ -698,5 +698,126 @@ describe('Metrics Util Tests', () => { ['metric', 10, {}] ], 'should collect all expected metrics'); }); + + describe('converting booleans', () => { + let origin; + beforeEach(() => { + origin = { + metric: 10, + trueBoolMetric: true, + falseBoolMetric: false, + thirdBoolMetric: true, + trueBoolInString: 'true', + falseBoolInString: 'false' + }; + }); + const booleanMetricTests = [ + { + name: 'should not convert booleans to integers by default', + expectedTags: { + trueBoolMetric: true, + falseBoolMetric: false, + thirdBoolMetric: true, + trueBoolInString: 'true', + falseBoolInString: 'false' + }, + expectedMetrics: [ + ['metric', 10] + ] + }, + { + name: 'should allow for booleans to be converted to integers', + options: { boolsToMetrics: true }, + expectedTags: { + trueBoolInString: 'true', + falseBoolInString: 'false' + }, + expectedMetrics: [ + ['metric', 10], + ['trueBoolMetric', 1], + ['falseBoolMetric', 0], + ['thirdBoolMetric', 1] + ] + }, + { + name: 'should ignore boolean metric if included in \'metricsToIgnore\'', + options: { boolsToMetrics: true, metricsToIgnore: ['falseBoolMetric'] }, + expectedTags: { + falseBoolMetric: false, + trueBoolInString: 'true', + falseBoolInString: 'false' + }, + expectedMetrics: [ + ['metric', 10], + ['trueBoolMetric', 1], + ['thirdBoolMetric', 1] + ] + }, + { + name: 'should keep a boolean-metric as a tag if included in \'metricsToTags\'', + options: { boolsToMetrics: true, metricsToTags: ['falseBoolMetric'] }, + expectedTags: { + falseBoolMetric: false, + trueBoolInString: 'true', + falseBoolInString: 'false' + }, + expectedMetrics: [ + ['metric', 10], + ['trueBoolMetric', 1], + ['thirdBoolMetric', 1] + ] + }, + { + name: 'should only filter boolean-as-tag if included in \'tagsToIgnore\'', + options: { boolsToMetrics: true, metricsToTags: ['falseBoolMetric', 'thirdBoolMetric'], tagsToIgnore: ['falseBoolMetric'] }, + expectedTags: { + thirdBoolMetric: true, + trueBoolInString: 'true', + falseBoolInString: 'false' + }, + expectedMetrics: [ + ['metric', 10], + ['trueBoolMetric', 1] + ] + }, + { + name: 'should still perform boolean-to-metric conversion if \'tagsToMetrics\' is used', + options: { boolsToMetrics: true, tagsToMetrics: ['falseBoolMetric', 'trueBoolInString'] }, + expectedTags: { + falseBoolInString: 'false' + }, + expectedMetrics: [ + ['metric', 10], + ['trueBoolMetric', 1], + ['falseBoolMetric', 0], + ['thirdBoolMetric', 1] + ] + } + ]; + + booleanMetricTests.forEach((bTest) => { + it(bTest.name, () => { + metricsUtil.findMetricsAndTags( + origin, + Object.assign(defaultOptions, { excludeNameFromPath: true }, bTest.options) + ); + + const expectedMetrics = bTest.expectedMetrics.map((expectedMetric) => { + expectedMetric.push(bTest.expectedTags); + return expectedMetric; + }); + + assert.sameDeepMembers( + collectedMetrics, + expectedMetrics, + 'should collect all expected metrics' + ); + + assert.sameDeepMembers(collectedTags, [ + ['', bTest.expectedTags] + ], 'should collect all expected tags'); + }); + }); + }); }); }); diff --git a/test/unit/consumers/openTelemetryExporterTests.js b/test/unit/consumers/openTelemetryExporterTests.js index 0334845c..17f3e370 100644 --- a/test/unit/consumers/openTelemetryExporterTests.js +++ b/test/unit/consumers/openTelemetryExporterTests.js @@ -38,12 +38,7 @@ describe('OpenTelemetry_Exporter', () => { openTelemetryExporter = require('../../../src/lib/consumers/OpenTelemetry_Exporter/index'); // Note: if a test has no explicit assertions then it relies on 'checkNockActiveMocks' in 'afterEach' - const defaultConsumerConfig = { - port: 80, - host: 'localhost', - metricsPath: '/v1/metrics' - }; - + let defaultConsumerConfig; let ExportRequestProto; before(() => { @@ -63,6 +58,14 @@ describe('OpenTelemetry_Exporter', () => { ExportRequestProto = proto.lookupType('ExportMetricsServiceRequest'); }); + beforeEach(() => { + defaultConsumerConfig = { + port: 80, + host: 'localhost', + metricsPath: '/v1/metrics' + }; + }); + afterEach(() => { testUtil.checkNockActiveMocks(nock); sinon.restore(); @@ -104,8 +107,8 @@ describe('OpenTelemetry_Exporter', () => { results.push({ name: metric.name, description: metric.description, - dataPoints: metric.doubleSum.dataPoints.map(p => ({ - labels: p.labels.map(l => ({ key: l.key, value: l.value })), + dataPoints: metric.doubleSum.dataPoints.map((p) => ({ + labels: p.labels.map((l) => ({ key: l.key, value: l.value })), value: p.value })) }); @@ -176,7 +179,35 @@ describe('OpenTelemetry_Exporter', () => { .post('/v1/metrics') .reply(200, (_, requestBody) => { const metrics = convertExportedMetrics(requestBody); + const systemCpuLabels = metrics.find((metric) => metric.name === 'system_cpu').dataPoints[0].labels; + assert.deepStrictEqual(metrics, openTelemetryExpectedData.systemData[0].expectedData); + assert.isDefined( + systemCpuLabels.find((label) => label.key === 'configSyncSucceeded'), + 'should find configSyncSucceeded as a label' + ); + }); + return openTelemetryExporter(context); + }); + + it('should process systemInfo data, and convert booleans to metrics', () => { + const context = testUtil.buildConsumerContext({ + eventType: 'systemInfo', + config: Object.assign(defaultConsumerConfig, { convertBooleansToMetrics: true }) + }); + + nock('http://localhost:80') + .post('/v1/metrics') + .reply(200, (_, requestBody) => { + const metrics = convertExportedMetrics(requestBody); + const systemCpuLabels = metrics.find((metric) => metric.name === 'system_cpu').dataPoints[0].labels; + const configSyncSucceeded = metrics.find((metric) => metric.name === 'system_configSyncSucceeded'); + + assert.isDefined(configSyncSucceeded, 'should include \'system_configSyncSucceeded\' as a metric'); + assert.isUndefined( + systemCpuLabels.find((label) => label.key === 'configSyncSucceeded'), + 'should not include configSyncSucceeded as a label' + ); }); return openTelemetryExporter(context); }); @@ -228,7 +259,6 @@ describe('OpenTelemetry_Exporter', () => { }); }); - it('should pass http headers when provided', () => { const configWithHeaders = Object.assign(defaultConsumerConfig, { headers: [ diff --git a/test/unit/consumers/splunkConsumerTests.js b/test/unit/consumers/splunkConsumerTests.js index d2a45112..b4210c0f 100644 --- a/test/unit/consumers/splunkConsumerTests.js +++ b/test/unit/consumers/splunkConsumerTests.js @@ -497,17 +497,17 @@ describe('Splunk', () => { return JSON.parse(`{${o}}`); } return undefined; - }).filter(m => m !== undefined); + }).filter((m) => m !== undefined); assert.notStrictEqual( - membersInOutput.find(m => m.event.name === '10.10.1.1:8080' && m.event.addr === '10.10.1.1'), + membersInOutput.find((m) => m.event.name === '10.10.1.1:8080' && m.event.addr === '10.10.1.1'), undefined, 'output should include poolMember 10.10.1.1:8080' ); assert.notStrictEqual( - membersInOutput.find(m => m.event.name === '10.9.8.100:443' && m.event.addr === '10.9.8.100'), + membersInOutput.find((m) => m.event.name === '10.9.8.100:443' && m.event.addr === '10.9.8.100'), undefined, 'output should include poolMember 10.9.8.100:443' ); assert.notStrictEqual( - membersInOutput.find(m => m.event.name === 'FE80:0000:0000:0000:0201:23FF:FE45:6701:8080' && m.event.addr === 'FE80:0000:0000:0000:0201:23FF:FE45:6701'), + membersInOutput.find((m) => m.event.name === 'FE80:0000:0000:0000:0201:23FF:FE45:6701:8080' && m.event.addr === 'FE80:0000:0000:0000:0201:23FF:FE45:6701'), undefined, 'output should include poolMember with addr 10.10.1.3' ); }); diff --git a/test/unit/consumers/statsdConsumerTests.js b/test/unit/consumers/statsdConsumerTests.js index 3f0006ad..f16a0333 100644 --- a/test/unit/consumers/statsdConsumerTests.js +++ b/test/unit/consumers/statsdConsumerTests.js @@ -30,11 +30,7 @@ describe('Statsd', () => { let passedClientParams; let metrics = []; - - const defaultConsumerConfig = { - host: 'statsd-host', - port: '8125' - }; + let defaultConsumerConfig; const statsDClientStub = { close: () => {}, @@ -67,6 +63,13 @@ describe('Statsd', () => { }); }); + beforeEach(() => { + defaultConsumerConfig = { + host: 'statsd-host', + port: '8125' + }; + }); + afterEach(() => { // clean up metrics after each test metrics = []; @@ -122,6 +125,20 @@ describe('Statsd', () => { .then(() => assert.sameDeepMembers(metrics, getExpectedData())); }); + it('should process systemInfo data, and convert booleans to metrics', () => { + const context = testUtil.buildConsumerContext({ + eventType: 'systemInfo', + config: Object.assign(defaultConsumerConfig, { convertBooleansToMetrics: true }) + }); + return statsDIndex(context) + .then(() => { + const configSyncSucceeded = metrics.find( + (metric) => metric.metricName === 'f5telemetry.telemetry-bigip-com.system.configSyncSucceeded' + ); + assert.deepStrictEqual(configSyncSucceeded, { metricName: 'f5telemetry.telemetry-bigip-com.system.configSyncSucceeded', metricValue: 1 }); + }); + }); + it('should process systemInfo data and apply auto-tagging (siblings only)', () => { const context = testUtil.buildConsumerContext({ eventType: 'systemInfo', @@ -203,7 +220,7 @@ describe('Statsd', () => { const traceData = context.tracer.write.firstCall.args[0]; assert.ok(Array.isArray(traceData), 'should be formatted as an array'); const expectedTraceLine = 'f5telemetry.telemetry-bigip-com.system.cpu: 0'; - assert.ok(traceData.find(d => d[0] === expectedTraceLine), 'should find expected line in trace'); + assert.ok(traceData.find((d) => d[0] === expectedTraceLine), 'should find expected line in trace'); }); }); }); diff --git a/test/unit/consumersTests.js b/test/unit/consumersTests.js index a6b367fa..27bf1182 100644 --- a/test/unit/consumersTests.js +++ b/test/unit/consumersTests.js @@ -118,7 +118,7 @@ describe('Consumers', () => { // config will not pass schema validation // but this test allows catching if consumer module/dir is not configured properly return configUtil.normalizeDeclaration(exampleConfig) - .then(normalized => configWorker.emitAsync('change', normalized)) + .then((normalized) => configWorker.emitAsync('change', normalized)) .then(() => { const loadedConsumers = consumers.getConsumers(); assert.strictEqual(Object.keys(loadedConsumers).indexOf('unknowntype'), -1, diff --git a/test/unit/customEndpointsTests.js b/test/unit/customEndpointsTests.js index b6062a9d..63aa96c2 100644 --- a/test/unit/customEndpointsTests.js +++ b/test/unit/customEndpointsTests.js @@ -55,7 +55,7 @@ describe('Custom Endpoints (Telemetry_Endpoints)', () => { endpoints: testConf.endpointList }; const getCollectedData = testConf.getCollectedData - ? testConf.getCollectedData : promise => promise; + ? testConf.getCollectedData : (promise) => promise; const stats = new SystemStats(options); diff --git a/test/unit/data/configUtilTests/getComponentsTestsData.js b/test/unit/data/configUtilTests/getComponentsTestsData.js index 1b725335..45dafc82 100644 --- a/test/unit/data/configUtilTests/getComponentsTestsData.js +++ b/test/unit/data/configUtilTests/getComponentsTestsData.js @@ -197,7 +197,7 @@ module.exports = { } } }, - classFilter: c => c.name === 'Consumer_1', + classFilter: (c) => c.name === 'Consumer_1', expected: [ { allowSelfSignedCert: false, @@ -361,7 +361,7 @@ module.exports = { } } }, - namespaceFilter: c => c.namespace === 'My_Namespace', + namespaceFilter: (c) => c.namespace === 'My_Namespace', expected: [ { allowSelfSignedCert: false, @@ -427,7 +427,7 @@ module.exports = { } }, classFilter: 'Telemetry_Consumer', - namespaceFilter: c => c.namespace === 'f5telemetry_default', + namespaceFilter: (c) => c.namespace === 'f5telemetry_default', expected: [ { allowSelfSignedCert: false, @@ -492,7 +492,7 @@ module.exports = { } } }, - filter: c => c.name === 'Consumer_1', + filter: (c) => c.name === 'Consumer_1', expected: [ { allowSelfSignedCert: false, diff --git a/test/unit/data/configUtilTests/hasEnabledComponentsTestsData.js b/test/unit/data/configUtilTests/hasEnabledComponentsTestsData.js index 374915d7..10407db8 100644 --- a/test/unit/data/configUtilTests/hasEnabledComponentsTestsData.js +++ b/test/unit/data/configUtilTests/hasEnabledComponentsTestsData.js @@ -132,7 +132,7 @@ module.exports = { } } }, - classFilter: c => c.name === 'Consumer_1', + classFilter: (c) => c.name === 'Consumer_1', expected: true }, { @@ -200,7 +200,7 @@ module.exports = { } } }, - namespaceFilter: c => c.namespace === 'My_Namespace', + namespaceFilter: (c) => c.namespace === 'My_Namespace', expected: true }, { @@ -235,7 +235,7 @@ module.exports = { } }, classFilter: 'Telemetry_Consumer', - namespaceFilter: c => c.namespace === 'f5telemetry_default', + namespaceFilter: (c) => c.namespace === 'f5telemetry_default', expected: true }, { @@ -269,7 +269,7 @@ module.exports = { } } }, - filter: c => c.name === 'Consumer_1', + filter: (c) => c.name === 'Consumer_1', expected: true } ] diff --git a/test/unit/data/configUtilTests/normalizeDeclarationIHealthPollerTestsData.js b/test/unit/data/configUtilTests/normalizeDeclarationIHealthPollerTestsData.js index 61d4b21a..2bc0ce70 100644 --- a/test/unit/data/configUtilTests/normalizeDeclarationIHealthPollerTestsData.js +++ b/test/unit/data/configUtilTests/normalizeDeclarationIHealthPollerTestsData.js @@ -615,9 +615,7 @@ module.exports = { expected: { mappings: { 'My_Namespace::My_System_1::My_iHealth_Poller': ['My_Namespace::My_Consumer'], - 'My_Namespace::My_System_2::My_iHealth_Poller': ['My_Namespace::My_Consumer'], - 'f5telemetry_default::My_System_1::My_iHealth_Poller': ['f5telemetry_default::My_Consumer'], - 'f5telemetry_default::My_System_2::My_iHealth_Poller': ['f5telemetry_default::My_Consumer'] + 'f5telemetry_default::My_System_1::My_iHealth_Poller': ['f5telemetry_default::My_Consumer'] }, components: [ { diff --git a/test/unit/data/configUtilTests/normalizeDeclarationPullConsumerTestsData.js b/test/unit/data/configUtilTests/normalizeDeclarationPullConsumerTestsData.js index 9cd44bb2..fc78fc28 100644 --- a/test/unit/data/configUtilTests/normalizeDeclarationPullConsumerTestsData.js +++ b/test/unit/data/configUtilTests/normalizeDeclarationPullConsumerTestsData.js @@ -165,14 +165,8 @@ module.exports = { 'f5telemetry_default::Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer': [ 'f5telemetry_default::My_Pull_Consumer' ], - 'f5telemetry_default::Telemetry_Pull_Consumer_System_Poller_Group_My_Disabled_Pull_Consumer': [ - 'f5telemetry_default::My_Disabled_Pull_Consumer' - ], 'My_Namespace::Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer': [ 'My_Namespace::My_Pull_Consumer' - ], - 'My_Namespace::Telemetry_Pull_Consumer_System_Poller_Group_My_Disabled_Pull_Consumer': [ - 'My_Namespace::My_Disabled_Pull_Consumer' ] }, components: [ @@ -201,11 +195,13 @@ module.exports = { trace: { enable: false }, - pullConsumer: 'My_Pull_Consumer', + pullConsumer: 'f5telemetry_default::My_Pull_Consumer', systemPollers: [ - 'Pull_Poller_1', - 'Pull_Poller_2', - 'Pull_Poller_3' + 'f5telemetry_default::My_System::Pull_Poller_1', + 'f5telemetry_default::My_System_2::Pull_Poller_2', + 'f5telemetry_default::My_System_3::Pull_Poller_1', + 'f5telemetry_default::My_System_3::Pull_Poller_2', + 'f5telemetry_default::Pull_Poller_3::Pull_Poller_3' ], id: 'f5telemetry_default::Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer' }, @@ -234,11 +230,13 @@ module.exports = { trace: { enable: false }, - pullConsumer: 'My_Disabled_Pull_Consumer', + pullConsumer: 'f5telemetry_default::My_Disabled_Pull_Consumer', systemPollers: [ - 'Pull_Poller_1', - 'Pull_Poller_2', - 'Pull_Poller_3' + 'f5telemetry_default::My_System::Pull_Poller_1', + 'f5telemetry_default::My_System_2::Pull_Poller_2', + 'f5telemetry_default::My_System_3::Pull_Poller_1', + 'f5telemetry_default::My_System_3::Pull_Poller_2', + 'f5telemetry_default::Pull_Poller_3::Pull_Poller_3' ], id: 'f5telemetry_default::Telemetry_Pull_Consumer_System_Poller_Group_My_Disabled_Pull_Consumer' }, @@ -267,11 +265,13 @@ module.exports = { trace: { enable: false }, - pullConsumer: 'My_Pull_Consumer', + pullConsumer: 'My_Namespace::My_Pull_Consumer', systemPollers: [ - 'Pull_Poller_1', - 'Pull_Poller_2', - 'Pull_Poller_3' + 'My_Namespace::My_System::Pull_Poller_1', + 'My_Namespace::My_System_2::Pull_Poller_2', + 'My_Namespace::My_System_3::Pull_Poller_1', + 'My_Namespace::My_System_3::Pull_Poller_2', + 'My_Namespace::Pull_Poller_3::Pull_Poller_3' ], id: 'My_Namespace::Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer' }, @@ -300,11 +300,13 @@ module.exports = { trace: { enable: false }, - pullConsumer: 'My_Disabled_Pull_Consumer', + pullConsumer: 'My_Namespace::My_Disabled_Pull_Consumer', systemPollers: [ - 'Pull_Poller_1', - 'Pull_Poller_2', - 'Pull_Poller_3' + 'My_Namespace::My_System::Pull_Poller_1', + 'My_Namespace::My_System_2::Pull_Poller_2', + 'My_Namespace::My_System_3::Pull_Poller_1', + 'My_Namespace::My_System_3::Pull_Poller_2', + 'My_Namespace::Pull_Poller_3::Pull_Poller_3' ], id: 'My_Namespace::Telemetry_Pull_Consumer_System_Poller_Group_My_Disabled_Pull_Consumer' }, @@ -1012,9 +1014,9 @@ module.exports = { trace: { enable: false }, - pullConsumer: 'My_Pull_Consumer', + pullConsumer: 'f5telemetry_default::My_Pull_Consumer', systemPollers: [ - 'Pull_Poller_1' + 'f5telemetry_default::Pull_Poller_1::Pull_Poller_1' ], id: 'f5telemetry_default::Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer' }, @@ -1043,9 +1045,9 @@ module.exports = { trace: { enable: false }, - pullConsumer: 'My_Pull_Consumer', + pullConsumer: 'My_Namespace::My_Pull_Consumer', systemPollers: [ - 'Pull_Poller_1' + 'My_Namespace::Pull_Poller_1::Pull_Poller_1' ], id: 'My_Namespace::Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer' }, diff --git a/test/unit/data/configUtilTests/normalizeDeclarationSystemPollerTestsData.js b/test/unit/data/configUtilTests/normalizeDeclarationSystemPollerTestsData.js index 4eb53fdb..6ee6e07f 100644 --- a/test/unit/data/configUtilTests/normalizeDeclarationSystemPollerTestsData.js +++ b/test/unit/data/configUtilTests/normalizeDeclarationSystemPollerTestsData.js @@ -345,9 +345,7 @@ module.exports = { }, expected: { mappings: { - 'f5telemetry_default::My_System_1::My_Poller_1': ['f5telemetry_default::My_Consumer_1'], 'f5telemetry_default::My_System_2::My_Poller_1': ['f5telemetry_default::My_Consumer_1'], - 'My_Namespace::My_System_1::My_Poller_1': ['My_Namespace::My_Consumer_1'], 'My_Namespace::My_System_2::My_Poller_1': ['My_Namespace::My_Consumer_1'] }, components: [ @@ -627,10 +625,7 @@ module.exports = { } }, expected: { - mappings: { - 'f5telemetry_default::My_Poller_1::My_Poller_1': ['f5telemetry_default::My_Consumer_1'], - 'My_Namespace::My_Poller_1::My_Poller_1': ['My_Namespace::My_Consumer_1'] - }, + mappings: {}, components: [ { class: 'Telemetry_System_Poller', @@ -864,10 +859,7 @@ module.exports = { } }, expected: { - mappings: { - 'f5telemetry_default::My_System::My_Poller_1': ['f5telemetry_default::My_Consumer_1'], - 'My_Namespace::My_System::My_Poller_1': ['My_Namespace::My_Consumer_1'] - }, + mappings: {}, components: [ { class: 'Telemetry_System_Poller', diff --git a/test/unit/data/customEndpointsTestsData.js b/test/unit/data/customEndpointsTestsData.js index cb98edb5..fbc2f761 100644 --- a/test/unit/data/customEndpointsTestsData.js +++ b/test/unit/data/customEndpointsTestsData.js @@ -8,7 +8,6 @@ 'use strict'; - module.exports = { collectVirtualServersCustom: { name: 'virtual servers and stats', diff --git a/test/unit/data/propertiesJsonTests/collectContext.js b/test/unit/data/propertiesJsonTests/collectContext.js index eeed72bf..36167a81 100644 --- a/test/unit/data/propertiesJsonTests/collectContext.js +++ b/test/unit/data/propertiesJsonTests/collectContext.js @@ -35,7 +35,7 @@ module.exports = { { name: 'should collect context data', statsToCollect: () => ({}), - contextToCollect: context => context, + contextToCollect: (context) => context, getCollectedData: (promise, stats) => promise.then(() => stats.contextData), endpoints: [ { @@ -121,7 +121,7 @@ module.exports = { { name: 'should not fail when no data (with items property)', statsToCollect: () => ({}), - contextToCollect: context => context, + contextToCollect: (context) => context, getCollectedData: (promise, stats) => promise.then(() => stats.contextData), endpoints: [ { @@ -164,7 +164,7 @@ module.exports = { { name: 'should not fail when no data (without items property)', statsToCollect: () => ({}), - contextToCollect: context => context, + contextToCollect: (context) => context, getCollectedData: (promise, stats) => promise.then(() => stats.contextData), endpoints: [ { diff --git a/test/unit/data/propertiesJsonTests/collectPools.js b/test/unit/data/propertiesJsonTests/collectPools.js index 92c26b0e..910e21cb 100644 --- a/test/unit/data/propertiesJsonTests/collectPools.js +++ b/test/unit/data/propertiesJsonTests/collectPools.js @@ -93,6 +93,7 @@ module.exports = { availabilityState: 'unknown', enabledState: 'enabled', monitorStatus: 'unchecked', + poolName: '/Common/test_pool_0', port: 80, 'serverside.bitsIn': 0, 'serverside.bitsOut': 0, @@ -109,6 +110,7 @@ module.exports = { availabilityState: 'unknown', enabledState: 'enabled', monitorStatus: 'unchecked', + poolName: '/Common/test_pool_0', port: 80, 'serverside.bitsIn': 0, 'serverside.bitsOut': 0, diff --git a/test/unit/data/propertiesJsonTests/collectSystemStats.js b/test/unit/data/propertiesJsonTests/collectSystemStats.js index 0529f05e..65425d04 100644 --- a/test/unit/data/propertiesJsonTests/collectSystemStats.js +++ b/test/unit/data/propertiesJsonTests/collectSystemStats.js @@ -137,7 +137,7 @@ module.exports = { }); return ret; }, - contextToCollect: context => context, + contextToCollect: (context) => context, expectedData: { system: { hostname: 'test.local', @@ -941,7 +941,7 @@ module.exports = { times: 2 }, method: 'post', - request: body => body.utilCmdArgs.indexOf('profile_access_misc_stat') !== -1, + request: (body) => body.utilCmdArgs.indexOf('profile_access_misc_stat') !== -1, response: { kind: 'tm:util:bash:runstate', commandResult: 'apm_state\nPending Policy Changes' @@ -1328,7 +1328,7 @@ module.exports = { { name: 'should collect cpuInfo on a multi CPU device', statsToCollect: ['system', 'cpu'], - contextToCollect: context => context, + contextToCollect: (context) => context, expectedData: { system: { cpu: 20 @@ -1582,7 +1582,7 @@ module.exports = { { name: 'should collect memory-host on a multi host device', statsToCollect: ['system', 'memory', 'tmmMemory', 'swap'], - contextToCollect: context => context, + contextToCollect: (context) => context, expectedData: { system: { memory: 70, @@ -1704,7 +1704,7 @@ module.exports = { }); return ret; }, - contextToCollect: context => context, + contextToCollect: (context) => context, expectedData: { system: { hostname: 'missing data', @@ -1890,7 +1890,7 @@ module.exports = { times: 2 }, method: 'post', - request: body => body.utilCmdArgs.indexOf('profile_access_misc_stat') !== -1, + request: (body) => body.utilCmdArgs.indexOf('profile_access_misc_stat') !== -1, response: { kind: 'tm:util:bash:runstate' } @@ -1945,7 +1945,7 @@ module.exports = { }); return ret; }, - contextToCollect: context => context, + contextToCollect: (context) => context, expectedData: { system: { hostname: 'missing data', @@ -2128,7 +2128,7 @@ module.exports = { times: 2 }, method: 'post', - request: body => body.utilCmdArgs.indexOf('profile_access_misc_stat') !== -1, + request: (body) => body.utilCmdArgs.indexOf('profile_access_misc_stat') !== -1, response: { kind: 'tm:util:bash:runstate' } diff --git a/test/unit/data/propertiesJsonTests/collectTmstats.js b/test/unit/data/propertiesJsonTests/collectTmstats.js index ba8c5f36..d34ef5e6 100644 --- a/test/unit/data/propertiesJsonTests/collectTmstats.js +++ b/test/unit/data/propertiesJsonTests/collectTmstats.js @@ -12,7 +12,7 @@ const defaultProperties = require('../../../../src/lib/properties.json'); const TMCTL_CMD_REGEXP = /'tmctl\s+-c\s+(.*)'/; -const ADD_DEFAULT_CONTEXT_ENDPOINTS = testEndpoints => testEndpoints.concat([ +const ADD_DEFAULT_CONTEXT_ENDPOINTS = (testEndpoints) => testEndpoints.concat([ { endpoint: '/mgmt/tm/sys/db/systemauth.disablebash', method: 'get', @@ -97,7 +97,7 @@ module.exports = { }); return ret; }, - contextToCollect: context => context, + contextToCollect: (context) => context, expectedData: { tmstats: { asmCpuUtilStats: [ @@ -852,7 +852,7 @@ module.exports = { options: { times: 47 }, - request: body => body && body.utilCmdArgs && body.utilCmdArgs.indexOf('tmctl') !== -1, + request: (body) => body && body.utilCmdArgs && body.utilCmdArgs.indexOf('tmctl') !== -1, response: (uri, requestBody) => { // requestBody is string let tmctlTable = requestBody.match(TMCTL_CMD_REGEXP); @@ -914,7 +914,7 @@ module.exports = { }); return ret; }, - contextToCollect: context => context, + contextToCollect: (context) => context, expectedData: { tmstats: { } }, @@ -925,7 +925,7 @@ module.exports = { options: { times: 47 }, - request: body => body && body.utilCmdArgs && body.utilCmdArgs.indexOf('tmctl') !== -1, + request: (body) => body && body.utilCmdArgs && body.utilCmdArgs.indexOf('tmctl') !== -1, response: (uri, requestBody) => { // requestBody is string let tmctlTable = requestBody.match(TMCTL_CMD_REGEXP); @@ -975,7 +975,7 @@ module.exports = { }); return ret; }, - contextToCollect: context => context, + contextToCollect: (context) => context, expectedData: { tmstats: { } }, @@ -986,7 +986,7 @@ module.exports = { options: { times: 47 }, - request: body => body && body.utilCmdArgs && body.utilCmdArgs.indexOf('tmctl') !== -1, + request: (body) => body && body.utilCmdArgs && body.utilCmdArgs.indexOf('tmctl') !== -1, response: { kind: 'tm:util:bash:runstate', commandResult: 'tmctl: qwerty: No such table' diff --git a/test/unit/declarationTests.js b/test/unit/declarationTests.js index 35f9ede2..b9f70047 100644 --- a/test/unit/declarationTests.js +++ b/test/unit/declarationTests.js @@ -580,7 +580,7 @@ describe('Declarations', () => { }); it('should base64 decode cipherText', () => { - coreStub.deviceUtil.encryptSecret.callsFake(data => Promise.resolve(`$M$${data}`)); + coreStub.deviceUtil.encryptSecret.callsFake((data) => Promise.resolve(`$M$${data}`)); const cipher = 'ZjVzZWNyZXQ='; // f5secret const data = { class: 'Telemetry', @@ -1914,7 +1914,7 @@ describe('Declarations', () => { } }; return declValidator(data) - .then(validated => assert.strictEqual(validated.My_Poller.interval, 0)); + .then((validated) => assert.strictEqual(validated.My_Poller.interval, 0)); }); it('should allow interval=0 when endpointList is not specified', () => { @@ -1926,7 +1926,7 @@ describe('Declarations', () => { } }; return declValidator(data) - .then(validated => assert.strictEqual(validated.My_Poller.interval, 0)); + .then((validated) => assert.strictEqual(validated.My_Poller.interval, 0)); }); it('should restrict minimum to 60 when endpointList is NOT specified', () => { @@ -1967,7 +1967,7 @@ describe('Declarations', () => { } }; return declValidator(data) - .then(validated => assert.strictEqual(validated.My_Poller.interval, 100000)); + .then((validated) => assert.strictEqual(validated.My_Poller.interval, 100000)); }); }); @@ -3697,7 +3697,7 @@ describe('Declarations', () => { addtlContext ); - const basicSchemaTestsValidator = decl => validateMinimal(decl); + const basicSchemaTestsValidator = (decl) => validateMinimal(decl); beforeEach(() => { minimalDeclaration = { @@ -3770,7 +3770,8 @@ describe('Declarations', () => { username: 'username', passphrase: { cipherText: 'sshSecret' - } + }, + endpointUrl: 'userDefinedUrl' }, [ { @@ -3784,7 +3785,8 @@ describe('Declarations', () => { 'logGroup', 'logStream', 'region', - 'username' + 'username', + 'endpointUrl' ], { stringLengthTests: true } ); @@ -3861,7 +3863,8 @@ describe('Declarations', () => { passphrase: { cipherText: 'cipherText' }, - dataType: 'logs' + dataType: 'logs', + endpointUrl: 'userDefinedUrl' }, { type: 'AWS_CloudWatch', @@ -3874,7 +3877,8 @@ describe('Declarations', () => { protected: 'SecureVault', cipherText: '$M$cipherText' }, - dataType: 'logs' + dataType: 'logs', + endpointUrl: 'userDefinedUrl' } )); @@ -3930,7 +3934,8 @@ describe('Declarations', () => { username: 'username', passphrase: { cipherText: 'cipherText' - } + }, + endpointUrl: 'userDefinedUrl' }, { type: 'AWS_CloudWatch', @@ -3942,7 +3947,8 @@ describe('Declarations', () => { class: 'Secret', protected: 'SecureVault', cipherText: '$M$cipherText' - } + }, + endpointUrl: 'userDefinedUrl' } )); @@ -3969,6 +3975,19 @@ describe('Declarations', () => { 'metricNamespace', { stringLengthTests: true, requiredTests: true } ); + + schemaValidationUtil.generateSchemaBasicTests( + basicSchemaTestsValidator, + { + type: 'AWS_CloudWatch', + region: 'region', + dataType: 'metrics', + metricNamespace: 'metricsThingee', + endpointUrl: 'userDefinedUrl' + }, + 'endpointUrl', + { stringLengthTests: true } + ); }); }); @@ -3994,7 +4013,8 @@ describe('Declarations', () => { username: 'username', passphrase: { cipherText: 'cipherText' - } + }, + endpointUrl: 'userDefinedUrl' }, { type: 'AWS_S3', @@ -4005,7 +4025,8 @@ describe('Declarations', () => { class: 'Secret', protected: 'SecureVault', cipherText: '$M$cipherText' - } + }, + endpointUrl: 'userDefinedUrl' } )); @@ -4018,13 +4039,15 @@ describe('Declarations', () => { username: 'username', passphrase: { cipherText: 'cipherText' - } + }, + endpointUrl: 'userDefinedUrl' }, [ 'bucket', { property: 'passphrase', dependenciesTests: 'username', ignoreOther: true }, 'region', - { property: 'username', dependenciesTests: 'passphrase' } + { property: 'username', dependenciesTests: 'passphrase' }, + 'endpointUrl' ], { stringLengthTests: true } ); @@ -4435,7 +4458,8 @@ describe('Declarations', () => { apiKey: 'test', compressionType: 'none', region: 'US1', - service: 'f5-telemetry' + service: 'f5-telemetry', + convertBooleansToMetrics: false } )); @@ -4445,14 +4469,20 @@ describe('Declarations', () => { apiKey: 'test', compressionType: 'gzip', region: 'EU1', - service: 'my-great-application' + service: 'my-great-application', + metricPrefix: ['f5', 'bigip'], + convertBooleansToMetrics: true, + customTags: [{ name: 'deploymentName', value: 'best version' }] }, { type: 'DataDog', apiKey: 'test', compressionType: 'gzip', region: 'EU1', - service: 'my-great-application' + service: 'my-great-application', + metricPrefix: ['f5', 'bigip'], + convertBooleansToMetrics: true, + customTags: [{ name: 'deploymentName', value: 'best version' }] } )); @@ -4461,7 +4491,9 @@ describe('Declarations', () => { { type: 'DataDog', apiKey: 'test', - index: 'index' + index: 'index', + metricPrefix: ['f5', 'bigip'], + customTags: [{ name: 'deploymentName', value: 'best version' }] }, [ { property: 'apiKey', requiredTests: true, stringLengthTests: true }, @@ -4479,9 +4511,32 @@ describe('Declarations', () => { allowed: ['US1', 'US3', 'EU1', 'US1-FED'], notAllowed: ['region'] } + }, + { + property: 'metricPrefix', + ignoreOther: true, + arrayLengthTests: { + minItems: 1 + } + }, + { + property: 'customTags', + ignoreOther: true, + arrayLengthTests: { + minItems: 1 + } } ] ); + + it('should fail when invalid \'convertBooleansToMetrics\' value specified', () => assert.isRejected( + validateMinimal({ + type: 'DataDog', + apiKey: 'test', + convertBooleansToMetrics: 'something' + }), + /convertBooleansToMetrics\/type.*should be boolean/ + )); }); describe('ElasticSearch', () => { @@ -4538,7 +4593,7 @@ describe('Declarations', () => { { apiVersion: 'blah' } - ].map(apiVerionTest => createTestCase(apiVerionTest.apiVersion, apiVerionTest.additionalProps)); + ].map((apiVerionTest) => createTestCase(apiVerionTest.apiVersion, apiVerionTest.additionalProps)); }; generateApiVersionTests().forEach((apiVersionTest) => { @@ -4877,7 +4932,27 @@ describe('Declarations', () => { cipherText: '$M$privateKey' }, serviceEmail: 'serviceEmail', - reportInstanceMetadata: false + reportInstanceMetadata: false, + useServiceAccountToken: false + } + )); + + it('should pass minimal declaration (useServiceAccountToken = true)', () => validateMinimal( + { + type: 'Google_Cloud_Logging', + logScopeId: 'myProject', + logId: 'allMyLogs', + serviceEmail: 'serviceEmail', + useServiceAccountToken: true + }, + { + type: 'Google_Cloud_Logging', + logScope: 'projects', + logScopeId: 'myProject', + logId: 'allMyLogs', + serviceEmail: 'serviceEmail', + reportInstanceMetadata: false, + useServiceAccountToken: true } )); @@ -4906,7 +4981,8 @@ describe('Declarations', () => { cipherText: '$M$privateKey' }, serviceEmail: 'serviceEmail', - reportInstanceMetadata: true + reportInstanceMetadata: true, + useServiceAccountToken: false } )); @@ -4925,6 +5001,34 @@ describe('Declarations', () => { } ), /#\/definitions\/logId\/pattern.*should match pattern.*\^\[a-zA-z0-9._-\]\+\$/)); + it('should not allow privateKeyId when useServiceAccountToken is true', () => assert.isRejected(validateFull( + { + type: 'Google_Cloud_Logging', + logScope: 'organizations', + logScopeId: 'myOrganization', + logId: 'allM yLogs', + privateKeyId: 'privateKeyId', + serviceEmail: 'serviceEmail', + reportInstanceMetadata: true, + useServiceAccountToken: true + } + ), /useServiceAccountToken\/const.*"allowedValue":false/)); + + it('should not allow privateKey when useServiceAccountToken is true', () => assert.isRejected(validateFull( + { + type: 'Google_Cloud_Logging', + logScope: 'organizations', + logScopeId: 'myOrganization', + logId: 'allM yLogs', + privateKey: { + cipherText: 'privateKey' + }, + serviceEmail: 'serviceEmail', + reportInstanceMetadata: true, + useServiceAccountToken: true + } + ), /useServiceAccountToken\/const.*"allowedValue":false/)); + schemaValidationUtil.generateSchemaBasicTests( basicSchemaTestsValidator, { @@ -4977,7 +5081,24 @@ describe('Declarations', () => { cipherText: '$M$privateKey' }, serviceEmail: 'serviceEmail', - reportInstanceMetadata: false + reportInstanceMetadata: false, + useServiceAccountToken: false + } + )); + + it('should pass minimal declaration (useServiceAccountToken = true)', () => validateMinimal( + { + type: 'Google_Cloud_Monitoring', + projectId: 'projectId', + serviceEmail: 'serviceEmail', + useServiceAccountToken: true + }, + { + type: 'Google_Cloud_Monitoring', + projectId: 'projectId', + serviceEmail: 'serviceEmail', + reportInstanceMetadata: false, + useServiceAccountToken: true } )); @@ -5001,7 +5122,8 @@ describe('Declarations', () => { cipherText: '$M$privateKey' }, serviceEmail: 'serviceEmail', - reportInstanceMetadata: false + reportInstanceMetadata: false, + useServiceAccountToken: false } )); @@ -5025,10 +5147,33 @@ describe('Declarations', () => { cipherText: '$M$privateKey' }, serviceEmail: 'serviceEmail', - reportInstanceMetadata: false + reportInstanceMetadata: false, + useServiceAccountToken: false } )); + it('should not allow privateKeyId when useServiceAccountToken is true', () => assert.isRejected(validateFull( + { + type: 'Google_Cloud_Monitoring', + projectId: 'projectId', + privateKeyId: 'privateKeyId', + serviceEmail: 'serviceEmail', + useServiceAccountToken: true + } + ), /useServiceAccountToken\/const.*"allowedValue":false/)); + + it('should not allow privateKey when useServiceAccountToken is true', () => assert.isRejected(validateFull( + { + type: 'Google_Cloud_Monitoring', + projectId: 'projectId', + privateKey: { + cipherText: 'privateKey' + }, + serviceEmail: 'serviceEmail', + useServiceAccountToken: true + } + ), /useServiceAccountToken\/const.*"allowedValue":false/)); + schemaValidationUtil.generateSchemaBasicTests( basicSchemaTestsValidator, { @@ -5389,7 +5534,8 @@ describe('Declarations', () => { type: 'Statsd', host: 'host', protocol: 'udp', - port: 8125 + port: 8125, + convertBooleansToMetrics: false } )); @@ -5398,13 +5544,15 @@ describe('Declarations', () => { type: 'Statsd', host: 'host', protocol: 'tcp', - port: 80 + port: 80, + convertBooleansToMetrics: true }, { type: 'Statsd', host: 'host', protocol: 'tcp', - port: 80 + port: 80, + convertBooleansToMetrics: true } )); @@ -5433,6 +5581,15 @@ describe('Declarations', () => { } ] ); + + it('should fail when invalid \'convertBooleansToMetrics\' value specified', () => assert.isRejected( + validateMinimal({ + type: 'Statsd', + host: 'host', + convertBooleansToMetrics: 'something' + }), + /convertBooleansToMetrics\/type.*should be boolean/ + )); }); describe('Sumo_Logic', () => { @@ -5682,7 +5839,8 @@ describe('Declarations', () => { { type: 'OpenTelemetry_Exporter', host: 'host', - port: 55681 + port: 55681, + convertBooleansToMetrics: false } )); @@ -5697,7 +5855,8 @@ describe('Declarations', () => { name: 'headerName', value: 'headerValue' } - ] + ], + convertBooleansToMetrics: true }, { type: 'OpenTelemetry_Exporter', @@ -5709,7 +5868,8 @@ describe('Declarations', () => { name: 'headerName', value: 'headerValue' } - ] + ], + convertBooleansToMetrics: true } )); @@ -5726,6 +5886,16 @@ describe('Declarations', () => { ], { stringLengthTests: true } ); + + it('should fail when invalid \'convertBooleansToMetrics\' value specified', () => assert.isRejected( + validateMinimal({ + type: 'OpenTelemetry_Exporter', + host: 'host', + port: 55681, + convertBooleansToMetrics: 'something' + }), + /convertBooleansToMetrics\/type.*should be boolean/ + )); }); }); @@ -5999,7 +6169,6 @@ describe('Declarations', () => { }); }); - function declValidator(decl, addtlContext) { let options; decl = testUtil.deepCopy(decl); diff --git a/test/unit/eventListener/baseDataReceiverTests.js b/test/unit/eventListener/baseDataReceiverTests.js index c50cdab6..06510cab 100644 --- a/test/unit/eventListener/baseDataReceiverTests.js +++ b/test/unit/eventListener/baseDataReceiverTests.js @@ -30,7 +30,7 @@ describe('Base Data Receiver', () => { let startHandlerStub; let stopHandlerStub; - const fetchStates = () => stateChangedSpy.args.map(callArgs => callArgs[0].current); + const fetchStates = () => stateChangedSpy.args.map((callArgs) => callArgs[0].current); before(() => { moduleCache.restore(); @@ -274,7 +274,7 @@ describe('Base Data Receiver', () => { it('should not wait till completion of previous operation', () => { const errors = []; return new Promise((resolve, reject) => { - stopHandlerStub.callsFake(() => receiverInst.start(false).catch(err => errors.push(err))); + stopHandlerStub.callsFake(() => receiverInst.start(false).catch((err) => errors.push(err))); receiverInst.stop().then(resolve).catch(reject); }) .then(() => { @@ -323,7 +323,7 @@ describe('Base Data Receiver', () => { it('should not wait till completion of previous operation', () => { const errors = []; return new Promise((resolve, reject) => { - startHandlerStub.callsFake(() => receiverInst.stop(false).catch(err => errors.push(err))); + startHandlerStub.callsFake(() => receiverInst.stop(false).catch((err) => errors.push(err))); receiverInst.start().then(resolve).catch(reject); }) .then(() => { diff --git a/test/unit/eventListener/dataPublisherTests.js b/test/unit/eventListener/dataPublisherTests.js index 120a72f0..58c72812 100644 --- a/test/unit/eventListener/dataPublisherTests.js +++ b/test/unit/eventListener/dataPublisherTests.js @@ -87,7 +87,7 @@ describe('Data Publisher', () => { sentData = data; cb(); }; - sinon.stub(net, 'createConnection').callsFake(opts => new MockNetConnection(opts)); + sinon.stub(net, 'createConnection').callsFake((opts) => new MockNetConnection(opts)); return configWorker.processDeclaration(getDefaultDeclaration()); }); diff --git a/test/unit/eventListener/eventListenerTests.js b/test/unit/eventListener/eventListenerTests.js index 8560568d..511cdcf8 100644 --- a/test/unit/eventListener/eventListenerTests.js +++ b/test/unit/eventListener/eventListenerTests.js @@ -74,13 +74,13 @@ describe('Event Listener', () => { }; const gatherIds = () => { - const ids = EventListener.getAll().map(inst => inst.id); + const ids = EventListener.getAll().map((inst) => inst.id); ids.sort(); return ids; }; const registeredTracerPaths = () => { - const paths = tracer.registered().map(t => t.path); + const paths = tracer.registered().map((t) => t.path); paths.sort(); return paths; }; @@ -435,20 +435,20 @@ describe('Event Listener', () => { EventListener.receiversManager.getMessageStream(6514), EventListener.receiversManager.getMessageStream(6515) ]; - return Promise.all(receivers.map(r => r.start())) + return Promise.all(receivers.map((r) => r.start())) .then(() => { - receivers.forEach(r => assert.isTrue(r.isRunning(), 'should be in running state')); + receivers.forEach((r) => assert.isTrue(r.isRunning(), 'should be in running state')); return EventListener.receiversManager.destroyAll(); }) .then(() => { - receivers.forEach(r => assert.isTrue(r.isDestroyed(), 'should be destroyed')); + receivers.forEach((r) => assert.isTrue(r.isDestroyed(), 'should be destroyed')); assert.deepStrictEqual(EventListener.receiversManager.registered, {}, 'should have no registered receivers'); }); }); it('should enable/disable input tracing', () => { - const getTracerData = tracerPath => coreStub.tracer.data[ - Object.keys(coreStub.tracer.data).find(key => key.indexOf(tracerPath) !== -1) + const getTracerData = (tracerPath) => coreStub.tracer.data[ + Object.keys(coreStub.tracer.data).find((key) => key.indexOf(tracerPath) !== -1) ]; const newDecl = testUtil.deepCopy(origDecl); @@ -518,7 +518,7 @@ describe('Event Listener', () => { 'INPUT.Telemetry_Listener.f5telemetry_default::Listener2', 'INPUT.Telemetry_Listener.f5telemetry_default::Listener3', 'INPUT.Telemetry_Listener.f5telemetry_default::Listener4' - ].forEach(tracerPath => assert.deepStrictEqual( + ].forEach((tracerPath) => assert.deepStrictEqual( getTracerData(tracerPath), [{ data: Buffer.from('12345').toString('hex'), // in hex @@ -561,7 +561,7 @@ describe('Event Listener', () => { [ 'INPUT.Telemetry_Listener.f5telemetry_default::Listener2', 'INPUT.Telemetry_Listener.f5telemetry_default::Listener3' - ].forEach(tracerPath => assert.includeDeepMembers( + ].forEach((tracerPath) => assert.includeDeepMembers( getTracerData(tracerPath), [{ data: Buffer.from('6789').toString('hex'), // in hex @@ -669,7 +669,7 @@ describe('Event Listener', () => { 'INPUT.Telemetry_Listener.f5telemetry_default::Listener2', 'INPUT.Telemetry_Listener.f5telemetry_default::Listener3', 'INPUT.Telemetry_Listener.f5telemetry_default::Listener4' - ].forEach(tracerPath => assert.notDeepInclude( + ].forEach((tracerPath) => assert.notDeepInclude( getTracerData(tracerPath), { data: Buffer.from('final').toString('hex'), // in hex diff --git a/test/unit/eventListener/messageStreamTests.js b/test/unit/eventListener/messageStreamTests.js index 8a658bb2..8e727bda 100644 --- a/test/unit/eventListener/messageStreamTests.js +++ b/test/unit/eventListener/messageStreamTests.js @@ -74,7 +74,7 @@ describe('Message Stream Receiver', () => { destroy() {} } - const getServerMock = (cls, ipv6) => serverMocks.find(mock => mock instanceof cls && (ipv6 === undefined || (ipv6 && mock.opts.type === 'udp6') || (!ipv6 && mock.opts.type === 'udp4'))); + const getServerMock = (cls, ipv6) => serverMocks.find((mock) => mock instanceof cls && (ipv6 === undefined || (ipv6 && mock.opts.type === 'udp6') || (!ipv6 && mock.opts.type === 'udp4'))); const createServerMock = (Cls, opts) => { const mock = new Cls(); mock.setInitArgs(opts); @@ -116,8 +116,8 @@ describe('Message Stream Receiver', () => { sinon.stub(messageStream.MessageStream, 'MAX_BUFFER_TIMEOUT').value(testBufferTimeout); - sinon.stub(udp, 'createSocket').callsFake(opts => createServerMock(MockUdpServer, opts)); - sinon.stub(net, 'createServer').callsFake(opts => createServerMock(MockTcpServer, opts)); + sinon.stub(udp, 'createSocket').callsFake((opts) => createServerMock(MockUdpServer, opts)); + sinon.stub(net, 'createServer').callsFake((opts) => createServerMock(MockTcpServer, opts)); onMockCreatedCallback = (serverMock) => { serverMock.on('listenMock', () => serverMock.emit('listening')); serverMock.on('closeMock', (inst, args) => { @@ -401,7 +401,7 @@ describe('Message Stream Receiver', () => { const fetchEvents = () => { const events = []; dataCallbackSpy.args.forEach((args) => { - args[0].forEach(arg => events.push(arg)); + args[0].forEach((arg) => events.push(arg)); }); return events; }; @@ -417,7 +417,7 @@ describe('Message Stream Receiver', () => { .then(() => { const socketInfo = createSocketInfo(MockUdpServer, false); const server = getServerMock(MockUdpServer, false); - testConf.chunks.forEach(chunk => server.emit('message', chunk.replace(/\{sep\}/g, sep), socketInfo)); + testConf.chunks.forEach((chunk) => server.emit('message', chunk.replace(/\{sep\}/g, sep), socketInfo)); clock.clockForward(100, { promisify: true }); return testUtil.sleep(testBufferTimeout * 4); // sleep to process pending tasks }) @@ -441,9 +441,9 @@ describe('Message Stream Receiver', () => { }) .then(() => { assert.lengthOf(serverMocks, 6, 'should create 3 more sockets'); - assert.lengthOf(serverMocks.filter(mock => mock.opts.type === 'udp4'), 2, 'should have 2 udp4 sockets'); - assert.lengthOf(serverMocks.filter(mock => mock.opts.type === 'udp6'), 2, 'should have 2 udp6 sockets'); - assert.lengthOf(serverMocks.filter(mock => mock.opts.allowHalfOpen === false), 2, 'should have 2 tcp sockets'); + assert.lengthOf(serverMocks.filter((mock) => mock.opts.type === 'udp4'), 2, 'should have 2 udp4 sockets'); + assert.lengthOf(serverMocks.filter((mock) => mock.opts.type === 'udp6'), 2, 'should have 2 udp6 sockets'); + assert.lengthOf(serverMocks.filter((mock) => mock.opts.allowHalfOpen === false), 2, 'should have 2 tcp sockets'); })); }); diff --git a/test/unit/eventListener/tcpUdpDataReceiverTests.js b/test/unit/eventListener/tcpUdpDataReceiverTests.js index d42dfc07..9f6d0529 100644 --- a/test/unit/eventListener/tcpUdpDataReceiverTests.js +++ b/test/unit/eventListener/tcpUdpDataReceiverTests.js @@ -569,7 +569,7 @@ describe('TCP and UDP Receivers', () => { let serverMocks; let onMockCreatedCallback; - const getServerMock = ipv6 => serverMocks.find(mock => (ipv6 && mock.opts.type === 'udp6') || (!ipv6 && mock.opts.type === 'udp4')); + const getServerMock = (ipv6) => serverMocks.find((mock) => (ipv6 && mock.opts.type === 'udp6') || (!ipv6 && mock.opts.type === 'udp4')); beforeEach(() => { serverMocks = []; @@ -655,8 +655,8 @@ describe('TCP and UDP Receivers', () => { }) .then(() => { assert.lengthOf(serverMocks, 4, 'should create 2 more sockets'); - assert.lengthOf(serverMocks.filter(mock => mock.opts.type === 'udp4'), 2, 'should have 2 udp4 sockets'); - assert.lengthOf(serverMocks.filter(mock => mock.opts.type === 'udp6'), 2, 'should have 2 udp6 sockets'); + assert.lengthOf(serverMocks.filter((mock) => mock.opts.type === 'udp4'), 2, 'should have 2 udp4 sockets'); + assert.lengthOf(serverMocks.filter((mock) => mock.opts.type === 'udp6'), 2, 'should have 2 udp6 sockets'); })); }); diff --git a/test/unit/iHealthTests.js b/test/unit/iHealthTests.js index e6cbdb51..6aa27880 100644 --- a/test/unit/iHealthTests.js +++ b/test/unit/iHealthTests.js @@ -45,8 +45,8 @@ describe('iHealth', () => { let processedReports; const disabledAllPollers = () => Promise.all(IHealthPoller.getAll({ includeDemo: true }) - .map(poller => IHealthPoller.disable(poller, true))) - .then(retObjs => Promise.all(retObjs.map(obj => obj.stopPromise))); + .map((poller) => IHealthPoller.disable(poller, true))) + .then((retObjs) => Promise.all(retObjs.map((obj) => obj.stopPromise))); const expectedConfiguration = (includeDisabled) => { const components = [ @@ -109,18 +109,18 @@ describe('iHealth', () => { }; const registeredTracerPaths = () => { - const paths = tracer.registered().map(t => t.path); + const paths = tracer.registered().map((t) => t.path); paths.sort(); return paths; }; const toTracerPaths = (ids) => { - const tracerPaths = ids.map(id => `Telemetry_iHealth_Poller.${id}`); + const tracerPaths = ids.map((id) => `Telemetry_iHealth_Poller.${id}`); tracerPaths.sort(); return tracerPaths; }; - const verifyPollersConfig = (pollers, expectedConfigs) => Promise.all(pollers.map(p => p.getConfig())) - .then(configs => assert.sameDeepMembers(configs, expectedConfigs, 'should match expected configuration')); + const verifyPollersConfig = (pollers, expectedConfigs) => Promise.all(pollers.map((p) => p.getConfig())) + .then((configs) => assert.sameDeepMembers(configs, expectedConfigs, 'should match expected configuration')); const waitForReport = (cb) => { processedReports = []; @@ -239,7 +239,7 @@ describe('iHealth', () => { return configWorker.processDeclaration(testUtil.deepCopy(declaration)) .then(() => { assert.sameMembers( - IHealthPoller.getAll({ includeDemo: true }).map(p => p.id), + IHealthPoller.getAll({ includeDemo: true }).map((p) => p.id), preExistingConfigIDs, 'should create instances with expected IDs' ); @@ -297,7 +297,7 @@ describe('iHealth', () => { .then(() => { const newInstances = IHealthPoller.getAll({ includeDemo: true }); assert.sameDeepMembers( - newInstances.map(p => p.id), + newInstances.map((p) => p.id), preExistingConfigIDs, 'should create instances with expected IDs' ); @@ -353,7 +353,7 @@ describe('iHealth', () => { .then(() => { const newInstances = IHealthPoller.getAll({ includeDemo: true }); assert.sameDeepMembers( - newInstances.map(p => p.id), + newInstances.map((p) => p.id), configIDsBeforeUpdate, 'should create instances with expected IDs' ); @@ -364,7 +364,7 @@ describe('iHealth', () => { 'should create tracers with expected IDs' ); // ignoring Namespace' 'demo' instance - instancesBefore = instancesBefore.filter(inst => configIDsBeforeUpdate.indexOf(inst.id) !== -1); + instancesBefore = instancesBefore.filter((inst) => configIDsBeforeUpdate.indexOf(inst.id) !== -1); instancesBefore.forEach((inst) => { if (inst.id.startsWith('Namespace')) { assert.notDeepInclude(newInstances, inst, 'should create new instance on update'); @@ -419,7 +419,7 @@ describe('iHealth', () => { .then(() => { const newInstances = IHealthPoller.getAll({ includeDemo: true }); assert.deepStrictEqual( - newInstances.map(p => p.id), + newInstances.map((p) => p.id), ['Namespace::System::iHealthPoller_1'], 'should disable disabled only' ); @@ -475,7 +475,7 @@ describe('iHealth', () => { .then(() => { const newInstances = IHealthPoller.getAll({ includeDemo: true }); assert.sameDeepMembers( - newInstances.map(p => p.id), + newInstances.map((p) => p.id), preExistingConfigIDs, 'should create instances with expected IDs' ); @@ -529,7 +529,7 @@ describe('iHealth', () => { .then(() => { const newInstances = IHealthPoller.getAll({ includeDemo: true }); assert.sameDeepMembers( - newInstances.map(p => p.id), + newInstances.map((p) => p.id), preExistingConfigIDs, 'should create instances with expected IDs' ); @@ -549,7 +549,7 @@ describe('iHealth', () => { return configWorker.processDeclaration(testUtil.deepCopy(declaration)) .then(() => { assert.sameMembers( - IHealthPoller.getAll({ includeDemo: true }).map(p => p.id), + IHealthPoller.getAll({ includeDemo: true }).map((p) => p.id), preExistingConfigIDs, 'should create instances with expected IDs' ); @@ -572,7 +572,7 @@ describe('iHealth', () => { declaration.Namespace.System.enable = false; return configWorker.processDeclaration(testUtil.deepCopy(declaration)) .then(() => { - assert.isEmpty(IHealthPoller.getAll({ includeDemo: true }).map(p => p.id), 'should not create instances'); + assert.isEmpty(IHealthPoller.getAll({ includeDemo: true }).map((p) => p.id), 'should not create instances'); assert.isEmpty(registeredTracerPaths(), 'should not create tracers'); }); }); @@ -588,7 +588,7 @@ describe('iHealth', () => { message: 'iHealth Poller for System "System" started' }); assert.sameMembers( - IHealthPoller.getAll({ demoOnly: true }).map(p => p.id), + IHealthPoller.getAll({ demoOnly: true }).map((p) => p.id), ['f5telemetry_default::System::iHealthPoller_1'], 'should create instance with expected ID' ); @@ -616,7 +616,7 @@ describe('iHealth', () => { message: 'iHealth Poller for System "System" (namespace "Namespace") started' }); assert.sameMembers( - IHealthPoller.getAll({ demoOnly: true }).map(p => p.id), + IHealthPoller.getAll({ demoOnly: true }).map((p) => p.id), ['Namespace::System::iHealthPoller_1'], 'should create instance with expected ID' ); @@ -655,7 +655,7 @@ describe('iHealth', () => { message: 'iHealth Poller for System "Disabled_System" (namespace "Namespace") started' }); assert.sameMembers( - IHealthPoller.getAll({ demoOnly: true }).map(p => p.id), + IHealthPoller.getAll({ demoOnly: true }).map((p) => p.id), [ 'f5telemetry_default::Disabled_System::iHealthPoller_1', 'Namespace::Disabled_System::iHealthPoller_1' @@ -720,7 +720,7 @@ describe('iHealth', () => { return configWorker.processDeclaration(testUtil.deepCopy(declaration)) .then(() => { assert.sameMembers( - IHealthPoller.getAll({ includeDemo: true }).map(p => p.id), + IHealthPoller.getAll({ includeDemo: true }).map((p) => p.id), preExistingConfigIDs, 'should create instances with expected IDs' ); @@ -745,7 +745,7 @@ describe('iHealth', () => { it('should return statuses for all pollers', () => { assert.sameDeepMembers( - ihealth.getCurrentState().map(s => s.name), + ihealth.getCurrentState().map((s) => s.name), [ 'f5telemetry_default::System::iHealthPoller_1', 'Namespace::System::iHealthPoller_1' @@ -756,7 +756,7 @@ describe('iHealth', () => { it('should return statuses for pollers in namespace', () => { assert.sameDeepMembers( - ihealth.getCurrentState('Namespace').map(s => s.name), + ihealth.getCurrentState('Namespace').map((s) => s.name), [ 'Namespace::System::iHealthPoller_1' ], @@ -770,7 +770,7 @@ describe('iHealth', () => { ]) .then(() => { assert.sameDeepMembers( - ihealth.getCurrentState().map(s => s.name), + ihealth.getCurrentState().map((s) => s.name), [ 'f5telemetry_default::System::iHealthPoller_1 (DEMO)', 'Namespace::System::iHealthPoller_1 (DEMO)', diff --git a/test/unit/ihealthPollerTests.js b/test/unit/ihealthPollerTests.js index 47e6193a..abc6c87d 100644 --- a/test/unit/ihealthPollerTests.js +++ b/test/unit/ihealthPollerTests.js @@ -55,8 +55,8 @@ describe('IHealthPoller', () => { }); return Promise.all(IHealthPoller.getAll({ includeDemo: true }) - .map(poller => IHealthPoller.disable(poller, true))) - .then(retObjs => Promise.all(retObjs.map(obj => obj.stopPromise))) + .map((poller) => IHealthPoller.disable(poller, true))) + .then((retObjs) => Promise.all(retObjs.map((obj) => obj.stopPromise))) .then(() => { assert.isEmpty(IHealthPoller.getAll({ includeDemo: true }), 'should have no running iHealth Pollers before any test'); return persistentStorage.persistentStorage.load(); @@ -64,8 +64,8 @@ describe('IHealthPoller', () => { }); afterEach(() => Promise.all(IHealthPoller.getAll({ includeDemo: true }) - .map(poller => IHealthPoller.disable(poller, true))) - .then(retObjs => Promise.all(retObjs.map(obj => obj.stopPromise))) + .map((poller) => IHealthPoller.disable(poller, true))) + .then((retObjs) => Promise.all(retObjs.map((obj) => obj.stopPromise))) .then(() => { assert.isEmpty(IHealthPoller.getAll({ includeDemo: true }), 'should have no running iHealth Pollers'); sinon.restore(); @@ -104,22 +104,22 @@ describe('IHealthPoller', () => { const poller = IHealthPoller.create('poller1'); assert.instanceOf(poller, IHealthPoller, 'should be instance of IHealthPoller'); assert.deepStrictEqual( - IHealthPoller.getAll({ includeDemo: true }).map(p => p.id), + IHealthPoller.getAll({ includeDemo: true }).map((p) => p.id), ['poller1'], 'should register iHealth Poller' ); - assert.isEmpty(IHealthPoller.getAll({ demoOnly: true }).map(p => p.id), 'should have no demo instances'); + assert.isEmpty(IHealthPoller.getAll({ demoOnly: true }).map((p) => p.id), 'should have no demo instances'); }); it('should create and register instance (demo instance)', () => { const poller = IHealthPoller.create('demoPoller1', { demo: true }); assert.instanceOf(poller, IHealthPoller, 'should be instance of IHealthPoller'); assert.deepStrictEqual( - IHealthPoller.getAll({ includeDemo: true }).map(p => p.id), + IHealthPoller.getAll({ includeDemo: true }).map((p) => p.id), ['demoPoller1'], 'should register demo iHealth Poller' ); - assert.isEmpty(IHealthPoller.getAll({ includeDemo: false }).map(p => p.id), 'should have no non-demo instances'); + assert.isEmpty(IHealthPoller.getAll({ includeDemo: false }).map((p) => p.id), 'should have no non-demo instances'); }); it('should throw error when instance exists already', () => { @@ -154,12 +154,12 @@ describe('IHealthPoller', () => { const poller = IHealthPoller.createDemo('poller1'); assert.instanceOf(poller, IHealthPoller, 'should be instance of IHealthPoller'); assert.deepStrictEqual( - IHealthPoller.getAll({ includeDemo: true }).map(p => p.id), + IHealthPoller.getAll({ includeDemo: true }).map((p) => p.id), ['poller1'], 'should register iHealth Poller' ); assert.deepStrictEqual( - IHealthPoller.getAll({ demoOnly: true }).map(p => p.id), + IHealthPoller.getAll({ demoOnly: true }).map((p) => p.id), ['poller1'], 'should register as demo instance' ); @@ -232,7 +232,7 @@ describe('IHealthPoller', () => { it('should return null when demo instance with such ID registered too', () => { IHealthPoller.createDemo('id'); assert.deepStrictEqual( - IHealthPoller.get('id').find(p => !p.isDemoModeEnabled()), + IHealthPoller.get('id').find((p) => !p.isDemoModeEnabled()), undefined, 'should have no instance with such ID' ); @@ -247,7 +247,7 @@ describe('IHealthPoller', () => { it('should return null when non-demo instance with such ID registered too', () => { IHealthPoller.create('id'); assert.deepStrictEqual( - IHealthPoller.get('id').find(p => p.isDemoModeEnabled()), + IHealthPoller.get('id').find((p) => p.isDemoModeEnabled()), undefined, 'should have no instance with such ID' ); @@ -299,9 +299,9 @@ describe('IHealthPoller', () => { }); describe('.unregister()', () => { - const getOrCreate = (id, opts) => IHealthPoller.get(id).find(p => !p.isDemoModeEnabled()) + const getOrCreate = (id, opts) => IHealthPoller.get(id).find((p) => !p.isDemoModeEnabled()) || IHealthPoller.create(id, opts); - const getOrCreateDemo = (id, opts) => IHealthPoller.get(id).find(p => p.isDemoModeEnabled()) + const getOrCreateDemo = (id, opts) => IHealthPoller.get(id).find((p) => p.isDemoModeEnabled()) || IHealthPoller.createDemo(id, opts); it('should unregister instance', () => { @@ -561,7 +561,7 @@ describe('IHealthPoller', () => { describe('.start() & .stop() and polling cycle', () => { const startPoller = (poller, options) => new Promise((resolve, reject) => { options = options || {}; - poller.on('died', error => (error ? reject(error) : resolve())); + poller.on('died', (error) => (error ? reject(error) : resolve())); if (!options.waitTillDied) { poller.on('completed', () => { if (!options.waitTillCycleCompleted @@ -609,8 +609,8 @@ describe('IHealthPoller', () => { }); it('should stop on attempt so start and stop and the same time (start and stop)', () => Promise.all([ - instance.start().catch(error => error), - instance.stop().catch(error => error) + instance.start().catch((error) => error), + instance.stop().catch((error) => error) ]) .then((results) => { assert.isFalse(instance.isActive(), 'should be inactive'); @@ -620,8 +620,8 @@ describe('IHealthPoller', () => { })); // try different order of execution it('should stop on attempt so start and stop and the same time (stop and start)', () => Promise.all([ - instance.stop().catch(error => error), - instance.start().catch(error => error) + instance.stop().catch((error) => error), + instance.start().catch((error) => error) ]) .then((results) => { assert.isFalse(instance.isActive(), 'should be inactive'); @@ -716,7 +716,7 @@ describe('IHealthPoller', () => { const reports = []; let storageCopy; - instance.on('report', report => reports.push(report)); + instance.on('report', (report) => reports.push(report)); const clockStub = stubs.clock(); clockStub.clockForward(30 * 60 * 1000, { promisify: true }); return startPoller(instance, { waitTillCycleCompleted: 2 }) // wait till 2 cycles will be completed @@ -778,7 +778,7 @@ describe('IHealthPoller', () => { reportCollect: 0 }); - const removedFiles = ihealthStub.ihealthUtil.DeviceAPI.removeFile.args.map(args => args[0]); + const removedFiles = ihealthStub.ihealthUtil.DeviceAPI.removeFile.args.map((args) => args[0]); assert.includeMembers( removedFiles, [ @@ -899,7 +899,7 @@ describe('IHealthPoller', () => { let timeToRestore; let uploadQkviewCallCount; - const stopOnceDataSaved = state => new Promise((resolve) => { + const stopOnceDataSaved = (state) => new Promise((resolve) => { coreStub.persistentStorage.saveCbAfter = (ctx) => { if (ctx.savedData.ihealth[instance.storageKey].lastKnownState === state) { lastSavedData = testUtil.deepCopy(ctx.savedData); @@ -908,7 +908,7 @@ describe('IHealthPoller', () => { }; }) .then(() => IHealthPoller.disable(instance)) - .then(ret => ret.stopPromise) + .then((ret) => ret.stopPromise) .then(() => { instance = null; }); diff --git a/test/unit/normalizeTests.js b/test/unit/normalizeTests.js index 9ee75926..cf38a1a8 100644 --- a/test/unit/normalizeTests.js +++ b/test/unit/normalizeTests.js @@ -696,7 +696,6 @@ describe('Normalize', () => { }); }); - describe('formatTimestamps', () => { it('should format timestamps', () => { const data = { diff --git a/test/unit/persistentStorageTests.js b/test/unit/persistentStorageTests.js index 0e28f83a..2636e0d7 100644 --- a/test/unit/persistentStorageTests.js +++ b/test/unit/persistentStorageTests.js @@ -55,15 +55,15 @@ describe('Persistent Storage', () => { value.push(6); return persistentStorageInst.get('somekey'); }) - .then(value => assert.deepStrictEqual(value, [1, 2, 3, 4, 5]))); + .then((value) => assert.deepStrictEqual(value, [1, 2, 3, 4, 5]))); it('should return data (one level key)', () => persistentStorageInst.load() .then(() => persistentStorageInst.get('somekey')) - .then(value => assert.deepStrictEqual(value, [1, 2, 3, 4, 5]))); + .then((value) => assert.deepStrictEqual(value, [1, 2, 3, 4, 5]))); it('should return data (multi level key)', () => persistentStorageInst.load() .then(() => persistentStorageInst.get('somekey[1]')) - .then(value => assert.deepStrictEqual(value, 2))); + .then((value) => assert.deepStrictEqual(value, 2))); it('should return data (multi level key as array)', () => { persistentStorageStub.loadData = { @@ -73,7 +73,7 @@ describe('Persistent Storage', () => { }; return persistentStorageInst.load() .then(() => persistentStorageInst.get(['somekey', 'key.with.dot'])) - .then(value => assert.deepStrictEqual(value, 10)); + .then((value) => assert.deepStrictEqual(value, 10)); }); }); @@ -86,22 +86,22 @@ describe('Persistent Storage', () => { return promise; }) .then(() => persistentStorageInst.get('somekey')) - .then(value => assert.deepStrictEqual(value, [1]))); + .then((value) => assert.deepStrictEqual(value, [1]))); it('should set data (one level key)', () => persistentStorageInst.load() .then(() => persistentStorageInst.set('somekey', 1)) .then(() => persistentStorageInst.get('somekey')) - .then(value => assert.deepStrictEqual(value, 1))); + .then((value) => assert.deepStrictEqual(value, 1))); it('should set data (multi level key)', () => persistentStorageInst.load() .then(() => persistentStorageInst.set('somekey.first.second', 10)) .then(() => persistentStorageInst.get('somekey')) - .then(value => assert.deepStrictEqual(value, { first: { second: 10 } }))); + .then((value) => assert.deepStrictEqual(value, { first: { second: 10 } }))); it('should set data (multi level key as array)', () => persistentStorageInst.load() .then(() => persistentStorageInst.set(['somekey', 'first', 'second'], 10)) .then(() => persistentStorageInst.get('somekey')) - .then(value => assert.deepStrictEqual(value, { first: { second: 10 } }))); + .then((value) => assert.deepStrictEqual(value, { first: { second: 10 } }))); it('should set data (multiple requests)', () => { persistentStorageStub.loadData = {}; @@ -114,9 +114,9 @@ describe('Persistent Storage', () => { persistentStorageInst.get('a.a'), persistentStorageInst.get('a.b') ])) - .then(results => assert.deepStrictEqual(results, [10, 20])) + .then((results) => assert.deepStrictEqual(results, [10, 20])) .then(() => persistentStorageInst.get('a')) - .then(value => assert.deepStrictEqual(value, { a: 10, b: 20 })); + .then((value) => assert.deepStrictEqual(value, { a: 10, b: 20 })); }); it('should override data (multiple requests)', () => { @@ -148,7 +148,7 @@ describe('Persistent Storage', () => { return persistentStorageInst.remove('somekey'); }) .then(() => persistentStorageInst.get('somekey')) - .then(value => assert.deepStrictEqual(value, undefined))); + .then((value) => assert.deepStrictEqual(value, undefined))); it('should remove data (multi level key)', () => persistentStorageInst.load() .then(() => persistentStorageInst.set('somekey.first.second', 10)) @@ -158,7 +158,7 @@ describe('Persistent Storage', () => { return persistentStorageInst.remove('somekey.first.second'); }) .then(() => persistentStorageInst.get('somekey')) - .then(value => assert.deepStrictEqual(value, { first: { } }))); + .then((value) => assert.deepStrictEqual(value, { first: { } }))); it('should remove data (multi level key as array)', () => persistentStorageInst.load() .then(() => persistentStorageInst.set('somekey.first.second', 10)) @@ -168,7 +168,7 @@ describe('Persistent Storage', () => { return persistentStorageInst.remove(['somekey', 'first', 'second']); }) .then(() => persistentStorageInst.get('somekey')) - .then(value => assert.deepStrictEqual(value, { first: { } }))); + .then((value) => assert.deepStrictEqual(value, { first: { } }))); }); }); @@ -207,7 +207,6 @@ describe('Persistent Storage', () => { }); }); - it('should load empty state', () => { persistentStorageStub.loadState = null; return persistentStorageInst.load() diff --git a/test/unit/propertiesJsonTests.js b/test/unit/propertiesJsonTests.js index 70f7f483..bb1edf8a 100644 --- a/test/unit/propertiesJsonTests.js +++ b/test/unit/propertiesJsonTests.js @@ -95,7 +95,7 @@ describe('properties.json', () => { }; const getCollectedData = testConf.getCollectedData - ? testConf.getCollectedData : promise => promise; + ? testConf.getCollectedData : (promise) => promise; const stats = new SystemStats(options); diff --git a/test/unit/pullConsumers/data/system_poller_datasets.json b/test/unit/pullConsumers/data/system_poller_datasets.json index 9c3248fc..88b12acb 100644 --- a/test/unit/pullConsumers/data/system_poller_datasets.json +++ b/test/unit/pullConsumers/data/system_poller_datasets.json @@ -430,6 +430,7 @@ "members": { "/Sample_01/192.0.1.10:80": { "addr": "192.0.1.10", + "poolName": "/Sample_01/A1/web_pool", "port": 80, "serverside.bitsIn": 0, "serverside.bitsOut": 0, @@ -439,6 +440,7 @@ }, "/Sample_01/192.0.1.11:80": { "addr": "192.0.1.11", + "poolName": "/Sample_01/A1/web_pool", "port": 80, "serverside.bitsIn": 0, "serverside.bitsOut": 0, @@ -462,6 +464,7 @@ "members": { "/Sample_01/192.0.1.12:80": { "addr": "192.0.1.12", + "poolName": "/Sample_01/A1/web_pool1", "port": 80, "serverside.bitsIn": 0, "serverside.bitsOut": 0, @@ -471,6 +474,7 @@ }, "/Sample_01/192.0.1.13:80": { "addr": "192.0.1.13", + "poolName": "/Sample_01/A1/web_pool1", "port": 80, "serverside.bitsIn": 0, "serverside.bitsOut": 0, @@ -494,6 +498,7 @@ "members": { "/Sample_02/192.0.2.10:80": { "addr": "192.0.2.10", + "poolName": "/Sample_02/A1/web_pool", "port": 80, "serverside.bitsIn": 0, "serverside.bitsOut": 0, @@ -503,6 +508,7 @@ }, "/Sample_02/192.0.2.11:80": { "addr": "192.0.2.11", + "poolName": "/Sample_02/A1/web_pool", "port": 80, "serverside.bitsIn": 0, "serverside.bitsOut": 0, @@ -526,6 +532,7 @@ "members": { "/Sample_02/192.0.2.12:80": { "addr": "192.0.2.12", + "poolName": "/Sample_02/A1/web_pool1", "port": 80, "serverside.bitsIn": 0, "serverside.bitsOut": 0, @@ -535,6 +542,7 @@ }, "/Sample_02/192.0.2.13:80": { "addr": "192.0.2.13", + "poolName": "/Sample_02/A1/web_pool1", "port": 80, "serverside.bitsIn": 0, "serverside.bitsOut": 0, @@ -1604,6 +1612,7 @@ "members": { "/Sample_01/192.0.1.10:80": { "addr": "192.0.1.10", + "poolName": "/Sample_01/A1/web_pool", "port": 80, "serverside.bitsIn": 0, "serverside.bitsOut": 0, @@ -1613,6 +1622,7 @@ }, "/Sample_01/192.0.1.11:80": { "addr": "192.0.1.11", + "poolName": "/Sample_01/A1/web_pool", "port": 80, "serverside.bitsIn": 0, "serverside.bitsOut": 0, @@ -1638,6 +1648,7 @@ "members": { "/Sample_01/192.0.1.12:80": { "addr": "192.0.1.12", + "poolName": "/Sample_01/A1/web_pool1", "port": 80, "serverside.bitsIn": 0, "serverside.bitsOut": 0, @@ -1647,6 +1658,7 @@ }, "/Sample_01/192.0.1.13:80": { "addr": "192.0.1.13", + "poolName": "/Sample_01/A1/web_pool1", "port": 80, "serverside.bitsIn": 0, "serverside.bitsOut": 0, @@ -1672,6 +1684,7 @@ "members": { "/Sample_02/192.0.2.10:80": { "addr": "192.0.2.10", + "poolName": "/Sample_02/A1/web_pool", "port": 80, "serverside.bitsIn": 0, "serverside.bitsOut": 0, @@ -1681,6 +1694,7 @@ }, "/Sample_02/192.0.2.11:80": { "addr": "192.0.2.11", + "poolName": "/Sample_02/A1/web_pool", "port": 80, "serverside.bitsIn": 0, "serverside.bitsOut": 0, @@ -1706,6 +1720,7 @@ "members": { "/Sample_02/192.0.2.12:80": { "addr": "192.0.2.12", + "poolName": "/Sample_02/A1/web_pool1", "port": 80, "serverside.bitsIn": 0, "serverside.bitsOut": 0, @@ -1715,6 +1730,7 @@ }, "/Sample_02/192.0.2.13:80": { "addr": "192.0.2.13", + "poolName": "/Sample_02/A1/web_pool1", "port": 80, "serverside.bitsIn": 0, "serverside.bitsOut": 0, @@ -2773,4 +2789,4 @@ "telemetryEventCategory": "systemInfo" } } -} \ No newline at end of file +} diff --git a/test/unit/pullConsumers/prometheusPullConsumerTests.js b/test/unit/pullConsumers/prometheusPullConsumerTests.js index 271aeeda..f363b6b9 100644 --- a/test/unit/pullConsumers/prometheusPullConsumerTests.js +++ b/test/unit/pullConsumers/prometheusPullConsumerTests.js @@ -27,7 +27,7 @@ const assert = chai.assert; moduleCache.remember(); -const arraysToPromLines = input => lodash.flatten(input).join('\n'); +const arraysToPromLines = (input) => lodash.flatten(input).join('\n'); describe('Prometheus Pull Consumer', () => { let context; diff --git a/test/unit/pullConsumersTests.js b/test/unit/pullConsumersTests.js index 800a3c60..daa7e7d7 100644 --- a/test/unit/pullConsumersTests.js +++ b/test/unit/pullConsumersTests.js @@ -110,7 +110,7 @@ describe('Pull Consumers', () => { // config will not pass schema validation // but this test allows catching if consumer module/dir is not configured properly return configUtil.normalizeDeclaration(exampleConfig) - .then(normalized => configWorker.emitAsync('change', normalized)) + .then((normalized) => configWorker.emitAsync('change', normalized)) .then(() => { const loadedConsumers = pullConsumers.getConsumers(); assert.strictEqual(Object.keys(loadedConsumers).indexOf('unknowntype'), -1, @@ -140,7 +140,7 @@ describe('Pull Consumers', () => { const loadedConsumers = pullConsumers.getConsumers(); assert.isEmpty(loadedConsumers, 'should unload default consumer'); }) - .catch(err => Promise.reject(err)); + .catch((err) => Promise.reject(err)); }); it('should not reload existing pull consumer when processing a new namespace declaration', () => { @@ -171,7 +171,7 @@ describe('Pull Consumers', () => { }) .then(() => configWorker.processNamespaceDeclaration(namespaceConfig, 'NewNamespace')) .then(() => { - const loadedConsumerIds = pullConsumers.getConsumers().map(c => c.id); + const loadedConsumerIds = pullConsumers.getConsumers().map((c) => c.id); assert.lengthOf(loadedConsumerIds, 2, 'should load new consumer'); assert.deepStrictEqual(loadedConsumerIds[0], existingConsumer.id); assert.isTrue(moduleLoaderSpy.calledTwice); @@ -200,7 +200,7 @@ describe('Pull Consumers', () => { }); }); - const runTestCase = testConf => testUtil.getCallableIt(testConf)(testConf.name, () => { + const runTestCase = (testConf) => testUtil.getCallableIt(testConf)(testConf.name, () => { declaration = testConf.declaration; if (typeof testConf.returnCtx !== 'undefined') { returnCtx = testConf.returnCtx; @@ -218,7 +218,7 @@ describe('Pull Consumers', () => { }); }); - describe('default (no namespace)', () => pullConsumersTestsData.getData.forEach(testConf => runTestCase(testConf))); + describe('default (no namespace)', () => pullConsumersTestsData.getData.forEach((testConf) => runTestCase(testConf))); describe('default (no namespace), lookup using f5telemetry_default name', () => pullConsumersTestsData.getData.forEach((testConf) => { diff --git a/test/unit/requestHandlers/declareHandlerTests.js b/test/unit/requestHandlers/declareHandlerTests.js index e71aa35a..79a55970 100644 --- a/test/unit/requestHandlers/declareHandlerTests.js +++ b/test/unit/requestHandlers/declareHandlerTests.js @@ -81,7 +81,7 @@ describe('DeclareHandler', () => { } function assertMultiRequestResults(mockConfig, expectedResponses, params) { - const fetchResponseInfo = handler => ({ + const fetchResponseInfo = (handler) => ({ code: handler.getCode(), body: handler.getBody() }); diff --git a/test/unit/requestHandlers/errorHandlerTests.js b/test/unit/requestHandlers/errorHandlerTests.js index 47544140..478e1f1b 100644 --- a/test/unit/requestHandlers/errorHandlerTests.js +++ b/test/unit/requestHandlers/errorHandlerTests.js @@ -17,7 +17,7 @@ const chaiAsPromised = require('chai-as-promised'); const ErrorHandler = require('../../../src/lib/requestHandlers/errorHandler'); const errors = require('../../../src/lib/errors'); const httpErrors = require('../../../src/lib/requestHandlers/httpErrors'); -const testUtil = require('./../shared/util'); +const testUtil = require('../shared/util'); chai.use(chaiAsPromised); const assert = chai.assert; diff --git a/test/unit/requestHandlers/eventListenerHandlerTests.js b/test/unit/requestHandlers/eventListenerHandlerTests.js index cf7ce02e..c8b8d234 100644 --- a/test/unit/requestHandlers/eventListenerHandlerTests.js +++ b/test/unit/requestHandlers/eventListenerHandlerTests.js @@ -99,7 +99,7 @@ describe('EventListenerHandler', () => { requestRouter.on('register', spy); requestRouter.removeAllHandlers(); requestRouter.registerAllHandlers(true); - const routePaths = spy.firstCall.args[0].router.routes.map(r => r.path); + const routePaths = spy.firstCall.args[0].router.routes.map((r) => r.path); assert.isTrue(routePaths.indexOf('/eventListener/:eventListener') > -1); assert.isTrue(routePaths.indexOf('/namespace/:namespace/eventListener/:eventListener') > -1); @@ -110,7 +110,7 @@ describe('EventListenerHandler', () => { requestRouter.on('register', spy); requestRouter.removeAllHandlers(); requestRouter.registerAllHandlers(); - const routePaths = spy.firstCall.args[0].router.routes.map(r => r.path); + const routePaths = spy.firstCall.args[0].router.routes.map((r) => r.path); assert.strictEqual(routePaths.indexOf('/eventListener/:eventListener'), -1); assert.strictEqual(routePaths.indexOf('/namespace/:namespace/eventListener/:eventListener'), -1); diff --git a/test/unit/requestHandlers/ihealthPollerHandlerTests.js b/test/unit/requestHandlers/ihealthPollerHandlerTests.js index d759dce5..5748b127 100644 --- a/test/unit/requestHandlers/ihealthPollerHandlerTests.js +++ b/test/unit/requestHandlers/ihealthPollerHandlerTests.js @@ -116,7 +116,7 @@ describe('IHealthPollerHandler', () => { assert.strictEqual(restOpMock.getStatusCode(), 200, 'should return expected code'); assert.strictEqual(restOpMock.getBody().code, 200, 'should return expected code'); assert.sameDeepMembers( - restOpMock.getBody().message.map(s => s.name), + restOpMock.getBody().message.map((s) => s.name), [ 'f5telemetry_default::System::iHealthPoller_1', 'Namespace::System::iHealthPoller_1' @@ -202,7 +202,7 @@ describe('IHealthPollerHandler', () => { assert.strictEqual(restOpMock.getStatusCode(), 200, 'should return expected code'); assert.strictEqual(restOpMock.getBody().code, 200, 'should return expected code'); assert.sameDeepMembers( - restOpMock.getBody().message.map(s => s.name), + restOpMock.getBody().message.map((s) => s.name), [ 'Namespace::System::iHealthPoller_1' ], @@ -217,7 +217,7 @@ describe('IHealthPollerHandler', () => { .then(() => { assert.strictEqual(restOpMock.getStatusCode(), 200, 'should return expected code'); assert.strictEqual(restOpMock.getBody().code, 200, 'should return expected code'); - assert.isEmpty(restOpMock.getBody().message.map(s => s.name), 'should return expected body'); + assert.isEmpty(restOpMock.getBody().message.map((s) => s.name), 'should return expected body'); }); }); diff --git a/test/unit/requestHandlers/routerTests.js b/test/unit/requestHandlers/routerTests.js index 4010cbee..6913cc61 100644 --- a/test/unit/requestHandlers/routerTests.js +++ b/test/unit/requestHandlers/routerTests.js @@ -57,7 +57,6 @@ class CustomRequestHandler extends BaseRequestHandler { } } - describe('Requests Router', () => { before(() => { moduleCache.restore(); diff --git a/test/unit/requestHandlers/systemPollerHandlerTests.js b/test/unit/requestHandlers/systemPollerHandlerTests.js index a22342d1..13e6d8ca 100644 --- a/test/unit/requestHandlers/systemPollerHandlerTests.js +++ b/test/unit/requestHandlers/systemPollerHandlerTests.js @@ -68,7 +68,6 @@ describe('SystemPollerHandler', () => { }); }); - it('should return 404 when unable to make config lookup', () => { sinon.stub(systemPoller, 'getPollersConfig').rejects(new errors.ConfigLookupError('expectedError')); return requestHandler.process() diff --git a/test/unit/restWorkerTests.js b/test/unit/restWorkerTests.js index 7b69d649..b6779d24 100644 --- a/test/unit/restWorkerTests.js +++ b/test/unit/restWorkerTests.js @@ -109,7 +109,7 @@ describe('restWorker', () => { }); it('should gather host device info', () => new Promise((resolve, reject) => { - restWorker.onStartCompleted(resolve, msg => reject(new Error(msg || 'no message provided'))); + restWorker.onStartCompleted(resolve, (msg) => reject(new Error(msg || 'no message provided'))); }) .then(() => new Promise((resolve, reject) => { setTimeout(() => { @@ -126,7 +126,7 @@ describe('restWorker', () => { it('should not fail when unable to gather host device info', () => { gatherHostDeviceInfoStub.rejects(new Error('expected error')); return new Promise((resolve, reject) => { - restWorker.onStartCompleted(resolve, msg => reject(new Error(msg || 'no message provided'))); + restWorker.onStartCompleted(resolve, (msg) => reject(new Error(msg || 'no message provided'))); }); }); }); @@ -145,7 +145,7 @@ describe('restWorker', () => { }; Object.keys(httpMethodsMapping).forEach((httpMethod) => { it(`should pass ${httpMethod} request to requests router`, () => new Promise((resolve, reject) => { - restWorker.onStartCompleted(resolve, msg => reject(new Error(msg || 'no message provided'))); + restWorker.onStartCompleted(resolve, (msg) => reject(new Error(msg || 'no message provided'))); }) .then(() => { assert.notOk(requestsProcessStub.called, 'should not be called yet'); diff --git a/test/unit/shared/assert.js b/test/unit/shared/assert.js index 8c5cfda2..06afbc98 100644 --- a/test/unit/shared/assert.js +++ b/test/unit/shared/assert.js @@ -25,8 +25,8 @@ module.exports = { */ includeMatch(haystack, needle, message) { const checkFn = needle instanceof RegExp - ? (elem => needle.test(elem)) - : (elem => elem.indexOf(needle) !== -1); + ? ((elem) => needle.test(elem)) + : ((elem) => elem.indexOf(needle) !== -1); const ok = Array.isArray(haystack) ? haystack.some(checkFn) : checkFn(haystack); @@ -64,8 +64,8 @@ module.exports = { */ notIncludeMatch(haystack, needle, message) { const checkFn = needle instanceof RegExp - ? (elem => needle.test(elem)) - : (elem => elem.indexOf(needle) !== -1); + ? ((elem) => needle.test(elem)) + : ((elem) => elem.indexOf(needle) !== -1); const ok = Array.isArray(haystack) ? haystack.some(checkFn) : checkFn(haystack); diff --git a/test/unit/shared/dummies.js b/test/unit/shared/dummies.js index 153dd831..9d0bf472 100644 --- a/test/unit/shared/dummies.js +++ b/test/unit/shared/dummies.js @@ -28,7 +28,7 @@ function mergeData(object) { if (arguments.length < 2) { return object; } - const setValueToObj = pair => setByKey(object, pair.key, pair.value); + const setValueToObj = (pair) => setByKey(object, pair.key, pair.value); for (let i = 1; i < arguments.length; i += 1) { const source = arguments[i]; if (Array.isArray(source)) { diff --git a/test/unit/shared/schemaValidation/index.js b/test/unit/shared/schemaValidation/index.js index 00d059ab..7fb92295 100644 --- a/test/unit/shared/schemaValidation/index.js +++ b/test/unit/shared/schemaValidation/index.js @@ -24,7 +24,7 @@ const subTests = { propertyTests: {} }; Object.keys(subTests) - .forEach(parentDir => fs.readdirSync(path.join(__dirname, parentDir)) + .forEach((parentDir) => fs.readdirSync(path.join(__dirname, parentDir)) .forEach((subModule) => { // eslint-disable-next-line global-require, import/no-dynamic-require const lib = require(path.join(__dirname, parentDir, subModule)); @@ -71,7 +71,7 @@ module.exports = { if (!propConf.tests) { propConf.tests = [lodash.cloneDeep(propConf)]; } - propConf.tests = propConf.tests.map(testConf => processOptions(testConf, options)); + propConf.tests = propConf.tests.map((testConf) => processOptions(testConf, options)); propConf.tests.forEach((testConf) => { const baseCtx = { declaration: lodash.cloneDeep(validDecl), @@ -83,7 +83,7 @@ module.exports = { * Simply verify that path is string(s) */ if (Array.isArray(propConf.property)) { - propConf.property.forEach(p => assert.isString( + propConf.property.forEach((p) => assert.isString( p, `Property path ${JSON.stringify(propConf.property)} should contain strings only` )); } else { @@ -152,12 +152,11 @@ module.exports = { )); } return validator(rootDeclCopy) - .then(validated => lodash.get(validated, pathToSubDecl)); + .then((validated) => lodash.get(validated, pathToSubDecl)); }; } }; - /** * Set control properties for test config * @@ -178,7 +177,6 @@ function setControlProperties(testConf) { } } - /** * Process and normalize test options * diff --git a/test/unit/shared/schemaValidation/propertyTests/defaultValue.js b/test/unit/shared/schemaValidation/propertyTests/defaultValue.js index 371dfb9a..0bd72f11 100644 --- a/test/unit/shared/schemaValidation/propertyTests/defaultValue.js +++ b/test/unit/shared/schemaValidation/propertyTests/defaultValue.js @@ -40,7 +40,7 @@ module.exports = { const testDecl = lodash.cloneDeep(ctx.declaration); lodash.unset(testDecl, ctx.property); return ctx.validator(testDecl) - .then(validDecl => assert.deepStrictEqual( + .then((validDecl) => assert.deepStrictEqual( lodash.get(validDecl, ctx.property), testConf.defaultValue, `should return default value when property "${ctx.propFullName}" not set` diff --git a/test/unit/shared/schemaValidation/propertyTests/enum.js b/test/unit/shared/schemaValidation/propertyTests/enum.js index 046f703d..93bde883 100644 --- a/test/unit/shared/schemaValidation/propertyTests/enum.js +++ b/test/unit/shared/schemaValidation/propertyTests/enum.js @@ -36,7 +36,7 @@ module.exports = { tests(ctx, testConf) { const subTitle = utils.testControls.fmtSubTitle(testConf); utils.testControls.getSubTestDescribe(testConf)(`"enum" keyword tests${subTitle}`, () => { - testConf.notAllowed.forEach(negVal => it(`should not allow value "${negVal}"`, () => { + testConf.notAllowed.forEach((negVal) => it(`should not allow value "${negVal}"`, () => { const testDecl = lodash.cloneDeep(ctx.declaration); lodash.set(testDecl, ctx.property, negVal); return assert.isRejected( @@ -45,7 +45,7 @@ module.exports = { `should not allow to set value "${negVal}" that not defined in enum` ); })); - testConf.allowed.forEach(posVal => it(`should allow value "${posVal}"`, () => { + testConf.allowed.forEach((posVal) => it(`should allow value "${posVal}"`, () => { const testDecl = lodash.cloneDeep(ctx.declaration); lodash.set(testDecl, ctx.property, posVal); return assert.isFulfilled( diff --git a/test/unit/shared/schemaValidation/propertyTests/optionalProps.js b/test/unit/shared/schemaValidation/propertyTests/optionalProps.js index ce41fd59..e959ca05 100644 --- a/test/unit/shared/schemaValidation/propertyTests/optionalProps.js +++ b/test/unit/shared/schemaValidation/propertyTests/optionalProps.js @@ -63,18 +63,18 @@ module.exports = { testConf.properties.length ); combinations.forEach((propSets) => { - utils.testControls.getSubTestDescribe(testConf)(`test for optional properties - ${propSets.map(idx => testConf.properties[idx].name).join(', ')}`, () => { + utils.testControls.getSubTestDescribe(testConf)(`test for optional properties - ${propSets.map((idx) => testConf.properties[idx].name).join(', ')}`, () => { it('should not fail when optional child properties are not set', () => { const testDecl = lodash.cloneDeep(ctx.declaration); const targetObj = lodash.get(testDecl, ctx.property); - propSets.forEach(idx => lodash.unset(targetObj, testConf.properties[idx].name)); + propSets.forEach((idx) => lodash.unset(targetObj, testConf.properties[idx].name)); lodash.set(testDecl, ctx.property, targetObj); return assert.isFulfilled( ctx.validator(testDecl), `should not fail when optional child properties for "${ctx.propFullName}" are not set` ); }); - if (lodash.some(propSets.map(idx => lodash.has(testConf.properties[idx], 'value')))) { + if (lodash.some(propSets.map((idx) => lodash.has(testConf.properties[idx], 'value')))) { it('should not fail when optional child properties are set', () => { const testDecl = lodash.cloneDeep(ctx.declaration); const targetObj = lodash.get(testDecl, ctx.property); @@ -119,7 +119,7 @@ module.exports = { }); } if (lodash.isObject(opts)) { - return Object.keys(opts).map(prop => ({ name: prop, value: opts[prop] })); + return Object.keys(opts).map((prop) => ({ name: prop, value: opts[prop] })); } return assert.fail(`optionalPropTests expected to be string, array of strings or object, got "${typeof opts}" instead`); }; diff --git a/test/unit/shared/schemaValidation/propertyTests/required.js b/test/unit/shared/schemaValidation/propertyTests/required.js index 902a371f..44d1d9a8 100644 --- a/test/unit/shared/schemaValidation/propertyTests/required.js +++ b/test/unit/shared/schemaValidation/propertyTests/required.js @@ -65,7 +65,7 @@ module.exports = { const ajvProp = ctx.propFullName.split('.').slice(-1)[0]; const testDecl = lodash.cloneDeep(ctx.declaration); const targetObj = lodash.get(testDecl, ctx.property); - propSets.forEach(prop => lodash.unset(targetObj, prop)); + propSets.forEach((prop) => lodash.unset(targetObj, prop)); lodash.set(testDecl, ctx.property, targetObj); return assert.isRejected( ctx.validator(testDecl), diff --git a/test/unit/shared/schemaValidation/utils.js b/test/unit/shared/schemaValidation/utils.js index 22125b51..be6409a5 100644 --- a/test/unit/shared/schemaValidation/utils.js +++ b/test/unit/shared/schemaValidation/utils.js @@ -16,7 +16,6 @@ const mochaDescribe = require('mocha').describe; chai.use(chaiAsPromised); const assert = chai.assert; - module.exports = { /** * All combinations from array diff --git a/test/unit/shared/stubs.js b/test/unit/shared/stubs.js index d6278093..5712f070 100644 --- a/test/unit/shared/stubs.js +++ b/test/unit/shared/stubs.js @@ -197,7 +197,7 @@ const _module = module.exports = { configWorker(configWorker) { const ctx = _module.eventEmitter(configWorker); ctx.configs = []; - configWorker.on('change', config => ctx.configs.push(config)); + configWorker.on('change', (config) => ctx.configs.push(config)); return ctx; }, @@ -214,8 +214,8 @@ const _module = module.exports = { encryptSecret: sinon.stub(deviceUtil, 'encryptSecret'), getDeviceType: sinon.stub(deviceUtil, 'getDeviceType') }; - ctx.decryptSecret.callsFake(data => Promise.resolve(data.slice(3))); - ctx.encryptSecret.callsFake(data => Promise.resolve(`$M$${data}`)); + ctx.decryptSecret.callsFake((data) => Promise.resolve(data.slice(3))); + ctx.encryptSecret.callsFake((data) => Promise.resolve(`$M$${data}`)); ctx.getDeviceType.callsFake(() => Promise.resolve(constants.DEVICE_TYPE.BIG_IP)); return ctx; }, @@ -238,7 +238,7 @@ const _module = module.exports = { addStubRestore(ctx.stub, () => { emitter.removeAllListeners(); Object.keys(ctx.preExistingListeners).forEach((evtName) => { - ctx.preExistingListeners[evtName].forEach(listener => emitter.on(evtName, listener)); + ctx.preExistingListeners[evtName].forEach((listener) => emitter.on(evtName, listener)); }); }); return ctx; diff --git a/test/unit/shared/util.js b/test/unit/shared/util.js index 66d82f0a..0a414c54 100644 --- a/test/unit/shared/util.js +++ b/test/unit/shared/util.js @@ -263,7 +263,7 @@ module.exports = { for (let i = 0; i < arguments.length; i += 1) { validators.push(getValidator(i, this.deepCopy(arguments[i]), arguments[i])); } - return () => validators.every(validator => validator()); + return () => validators.every((validator) => validator()); }, /** @@ -275,7 +275,7 @@ module.exports = { if (process.versions.node.startsWith('4.')) { return urllib.parse; } - return url => new urllib.URL(url); + return (url) => new urllib.URL(url); }()), /** @@ -329,6 +329,6 @@ module.exports = { * @returns {Promise} resolved once N .ms passed */ sleep(sleepTime) { - return new Promise(resolve => setTimeout(resolve, sleepTime)); + return new Promise((resolve) => setTimeout(resolve, sleepTime)); } }; diff --git a/test/unit/systemPollerTests.js b/test/unit/systemPollerTests.js index 9ebb2d96..05642c05 100644 --- a/test/unit/systemPollerTests.js +++ b/test/unit/systemPollerTests.js @@ -42,7 +42,6 @@ describe('System Poller', () => { moduleCache.restore(); }); - beforeEach(() => { coreStub = stubs.coreStub({ configWorker, @@ -140,12 +139,12 @@ describe('System Poller', () => { describe('.getPollersConfig', () => { /* eslint-disable implicit-arrow-linebreak */ - systemPollerConfigTestsData.getPollersConfig.forEach(testConf => + systemPollerConfigTestsData.getPollersConfig.forEach((testConf) => testUtil.getCallableIt(testConf)( testConf.name, () => configWorker.processDeclaration(testUtil.deepCopy(testConf.declaration)) .then(() => systemPoller.getPollersConfig(testConf.sysOrPollerName, testConf.funcOptions)) .then((pollersConfig) => { - pollersConfig = pollersConfig.map(p => ({ name: p.traceName })); + pollersConfig = pollersConfig.map((p) => ({ name: p.traceName })); assert.sameDeepMembers(pollersConfig, testConf.expectedConfig); // assert.isTrue(coreStub.deviceUtil.decryptSecret.called); }, (error) => { @@ -159,7 +158,7 @@ describe('System Poller', () => { describe('.findSystemOrPollerConfigs', () => { /* eslint-disable implicit-arrow-linebreak */ - systemPollerConfigTestsData.findSystemOrPollerConfigs.forEach(testConf => + systemPollerConfigTestsData.findSystemOrPollerConfigs.forEach((testConf) => testUtil.getCallableIt(testConf)( testConf.name, () => configWorker.processDeclaration(testUtil.deepCopy(testConf.rawConfig)) .then(() => { @@ -184,7 +183,7 @@ describe('System Poller', () => { beforeEach(() => { processStub = sinon.stub(systemPoller, 'process'); - processStub.callsFake(config => Promise.resolve({ data: { poller: config.name } })); + processStub.callsFake((config) => Promise.resolve({ data: { poller: config.name } })); }); it('should return empty array when no config passed', () => { @@ -209,7 +208,6 @@ describe('System Poller', () => { return assert.becomes(systemPoller.fetchPollersData(pollerConfigs), expected); }); - it('should fetch data using multiple poller configs', () => { const pollerConfigs = [ { @@ -308,7 +306,7 @@ describe('System Poller', () => { let pollerTimersBefore; const registeredTracerPaths = () => { - const paths = tracer.registered().map(t => t.path); + const paths = tracer.registered().map((t) => t.path); paths.sort(); return paths; }; @@ -522,7 +520,7 @@ describe('System Poller', () => { path: '/var/tmp/telemetry/Telemetry_System_Poller.f5telemetry_default::My_System_New::My_Poller', type: 'output' }, - tracer: tracer.registered().find(t => /Telemetry_System_Poller.f5telemetry_default::My_System_New::My_Poller/.test(t.path)), + tracer: tracer.registered().find((t) => /Telemetry_System_Poller.f5telemetry_default::My_System_New::My_Poller/.test(t.path)), credentials: { username: undefined, passphrase: undefined @@ -569,7 +567,7 @@ describe('System Poller', () => { type: 'output' }, systemName: 'My_System_New', - tracer: tracer.registered().find(t => /Telemetry_System_Poller.f5telemetry_default::My_System_New::SystemPoller_1/.test(t.path)), + tracer: tracer.registered().find((t) => /Telemetry_System_Poller.f5telemetry_default::My_System_New::SystemPoller_1/.test(t.path)), traceName: 'f5telemetry_default::My_System_New::SystemPoller_1', credentials: { username: undefined, diff --git a/test/unit/systemStatsTests.js b/test/unit/systemStatsTests.js index d21ab268..7b8b02d3 100644 --- a/test/unit/systemStatsTests.js +++ b/test/unit/systemStatsTests.js @@ -199,7 +199,7 @@ describe('System Stats', () => { // not strict, just verifies that properties are not in skip list // so, if property in skip list -> it is an error const shouldKeep = (testConf.shouldKeep || testConf.shouldKeepOnly || []).filter( - statKey => activeStats.indexOf(statKey) === -1 + (statKey) => activeStats.indexOf(statKey) === -1 ); assert.isEmpty(shouldKeep, `[shouldKeep] should keep following properties - '${JSON.stringify(shouldKeep)}'`); @@ -207,7 +207,7 @@ describe('System Stats', () => { // so, if property not in skip list -> it is an error const shouldRemove = (testConf.shouldRemove || testConf.shouldRemoveOnly || []).filter( // stats key SHOULD be in skip list - statKey => activeStats.indexOf(statKey) !== -1 + (statKey) => activeStats.indexOf(statKey) !== -1 ); assert.isEmpty(shouldRemove, `[shouldRemove] should remove following properties - '${JSON.stringify(shouldRemove)}'`); @@ -216,7 +216,7 @@ describe('System Stats', () => { let notRemoved = []; if (testConf.shouldKeepOnly) { notRemoved = Object.keys(stats).filter( - statKey => activeStats.indexOf(statKey) !== -1 + (statKey) => activeStats.indexOf(statKey) !== -1 && testConf.shouldKeepOnly.indexOf(statKey) === -1 ); } @@ -227,7 +227,7 @@ describe('System Stats', () => { let notKept = []; if (testConf.shouldRemoveOnly) { notKept = Object.keys(stats).filter( - statKey => activeStats.indexOf(statKey) === -1 + (statKey) => activeStats.indexOf(statKey) === -1 && testConf.shouldRemoveOnly.indexOf(statKey) === -1 ); } diff --git a/test/unit/teemReporterTests.js b/test/unit/teemReporterTests.js index 3eb014e6..0d888310 100644 --- a/test/unit/teemReporterTests.js +++ b/test/unit/teemReporterTests.js @@ -77,7 +77,7 @@ describe('TeemReporter', () => { 'addJsonObject', 'calculateAssetId' ]; - const teemSpies = methods.map(m => ({ name: m, instance: sinon.spy(TeemRecord.prototype, m) })); + const teemSpies = methods.map((m) => ({ name: m, instance: sinon.spy(TeemRecord.prototype, m) })); const reportRecordStub = sinon.stub(teemDevice, 'reportRecord'); reportRecordStub.callsFake((record) => { @@ -107,7 +107,6 @@ describe('TeemReporter', () => { }); }); - describe('.fetchExtraData()', () => { const teemReporter = new TeemReporter(); const validate = (decl, expectedExtraData) => configWorker.processDeclaration(decl) diff --git a/test/unit/utils/configTests.js b/test/unit/utils/configTests.js index 72e0075a..fd4557e5 100644 --- a/test/unit/utils/configTests.js +++ b/test/unit/utils/configTests.js @@ -82,7 +82,7 @@ describe('Config Util', () => { listener: { class: 'Telemetry_Listener' }, - My_Namespace: { + My_Namespace_1: { class: 'Telemetry_Namespace', listener: { class: 'Telemetry_Listener' @@ -91,6 +91,51 @@ describe('Config Util', () => { class: 'Telemetry_Consumer', type: 'default' } + }, + My_Namespace_2: { + class: 'Telemetry_Namespace', + listener: { + class: 'Telemetry_Listener' + }, + consumer_1: { + class: 'Telemetry_Consumer', + type: 'default' + }, + consumer_2: { + class: 'Telemetry_Consumer', + type: 'default' + } + }, + My_Namespace_3: { + class: 'Telemetry_Namespace', + poller: { + class: 'Telemetry_System_Poller' + }, + consumer: { + class: 'Telemetry_Consumer', + type: 'default' + }, + pullConsumer: { + class: 'Telemetry_Pull_Consumer', + type: 'default', + systemPoller: ['poller'] + } + }, + My_Namespace_4: { + class: 'Telemetry_Namespace', + poller: { + class: 'Telemetry_System_Poller', + interval: 0 + }, + consumer: { + class: 'Telemetry_Consumer', + type: 'default' + }, + pullConsumer: { + class: 'Telemetry_Pull_Consumer', + type: 'default', + systemPoller: ['poller'] + } } }; return parseDeclaration(rawDecl) @@ -98,10 +143,141 @@ describe('Config Util', () => { let listener = configUtil.getTelemetryListeners(configWorker.currentConfig, 'f5telemetry_default')[0]; assert.isEmpty(configUtil.getReceivers(configWorker.currentConfig, listener)); - listener = configUtil.getTelemetryListeners(configWorker.currentConfig, 'My_Namespace')[0]; + listener = configUtil.getTelemetryListeners(configWorker.currentConfig, 'My_Namespace_1')[0]; + assert.deepStrictEqual( + configUtil.getReceivers(configWorker.currentConfig, listener).map((c) => c.id), + ['My_Namespace_1::consumer'] + ); + + listener = configUtil.getTelemetryListeners(configWorker.currentConfig, 'My_Namespace_2')[0]; assert.deepStrictEqual( - configUtil.getReceivers(configWorker.currentConfig, listener).map(c => c.id), - ['My_Namespace::consumer'] + configUtil.getReceivers(configWorker.currentConfig, listener).map((c) => c.id), + ['My_Namespace_2::consumer_1', 'My_Namespace_2::consumer_2'] + ); + + let poller = configUtil.getTelemetrySystemPollers(configWorker.currentConfig, 'My_Namespace_3')[0]; + assert.deepStrictEqual( + configUtil.getReceivers(configWorker.currentConfig, poller).map((c) => c.id), + ['My_Namespace_3::consumer'] + ); + + poller = configUtil.getTelemetrySystemPollers(configWorker.currentConfig, 'My_Namespace_4')[0]; + assert.isEmpty(configUtil.getReceivers(configWorker.currentConfig, poller)); + + let pullConsumerGroup = configUtil.getTelemetryPullConsumerSystemPollerGroups(configWorker.currentConfig, 'My_Namespace_3')[0]; + assert.deepStrictEqual( + configUtil.getReceivers(configWorker.currentConfig, pullConsumerGroup).map((c) => c.id), + ['My_Namespace_3::pullConsumer'] + ); + + pullConsumerGroup = configUtil.getTelemetryPullConsumerSystemPollerGroups(configWorker.currentConfig, 'My_Namespace_4')[0]; + assert.deepStrictEqual( + configUtil.getReceivers(configWorker.currentConfig, pullConsumerGroup).map((c) => c.id), + ['My_Namespace_4::pullConsumer'] + ); + }); + }); + }); + + describe('.getSources()', () => { + it('should return data sources when defined', () => { + const rawDecl = { + class: 'Telemetry', + consumer: { + class: 'Telemetry_Consumer', + type: 'default' + }, + My_Namespace_1: { + class: 'Telemetry_Namespace', + listener: { + class: 'Telemetry_Listener' + }, + consumer: { + class: 'Telemetry_Consumer', + type: 'default' + } + }, + My_Namespace_2: { + class: 'Telemetry_Namespace', + listener_1: { + class: 'Telemetry_Listener' + }, + listener_2: { + class: 'Telemetry_Listener' + }, + consumer: { + class: 'Telemetry_Consumer', + type: 'default' + } + }, + My_Namespace_3: { + class: 'Telemetry_Namespace', + poller: { + class: 'Telemetry_System_Poller' + }, + consumer: { + class: 'Telemetry_Consumer', + type: 'default' + }, + pullConsumer: { + class: 'Telemetry_Pull_Consumer', + type: 'default', + systemPoller: ['poller'] + } + }, + My_Namespace_4: { + class: 'Telemetry_Namespace', + poller: { + class: 'Telemetry_System_Poller', + interval: 0 + }, + consumer: { + class: 'Telemetry_Consumer', + type: 'default' + }, + pullConsumer: { + class: 'Telemetry_Pull_Consumer', + type: 'default', + systemPoller: ['poller'] + } + } + }; + return parseDeclaration(rawDecl) + .then(() => { + let consumer = configUtil.getTelemetryConsumers(configWorker.currentConfig, 'f5telemetry_default')[0]; + assert.isEmpty(configUtil.getSources(configWorker.currentConfig, consumer)); + + consumer = configUtil.getTelemetryConsumers(configWorker.currentConfig, 'My_Namespace_1')[0]; + assert.deepStrictEqual( + configUtil.getSources(configWorker.currentConfig, consumer).map((c) => c.id), + ['My_Namespace_1::listener'] + ); + + consumer = configUtil.getTelemetryConsumers(configWorker.currentConfig, 'My_Namespace_2')[0]; + assert.deepStrictEqual( + configUtil.getSources(configWorker.currentConfig, consumer).map((c) => c.id), + ['My_Namespace_2::listener_1', 'My_Namespace_2::listener_2'] + ); + + consumer = configUtil.getTelemetryConsumers(configWorker.currentConfig, 'My_Namespace_3')[0]; + assert.deepStrictEqual( + configUtil.getSources(configWorker.currentConfig, consumer).map((c) => c.id), + ['My_Namespace_3::poller::poller'] + ); + + let pullConsumer = configUtil.getTelemetryPullConsumers(configWorker.currentConfig, 'My_Namespace_3')[0]; + assert.deepStrictEqual( + configUtil.getSources(configWorker.currentConfig, pullConsumer).map((c) => c.id), + ['My_Namespace_3::Telemetry_Pull_Consumer_System_Poller_Group_pullConsumer'] + ); + + consumer = configUtil.getTelemetryConsumers(configWorker.currentConfig, 'My_Namespace_4')[0]; + assert.isEmpty(configUtil.getSources(configWorker.currentConfig, consumer)); + + pullConsumer = configUtil.getTelemetryPullConsumers(configWorker.currentConfig, 'My_Namespace_4')[0]; + assert.deepStrictEqual( + configUtil.getSources(configWorker.currentConfig, pullConsumer).map((c) => c.id), + ['My_Namespace_4::Telemetry_Pull_Consumer_System_Poller_Group_pullConsumer'] ); }); }); @@ -251,7 +427,7 @@ describe('Config Util', () => { parseDeclaration(testConf.baseDeclaration, { expanded: true }).then(configUtil.normalizeDeclaration), parseDeclaration(testConf.newDeclaration, { expanded: true }) ]) - .then(configs => configUtil.mergeDeclaration(configs[1], configs[0])) + .then((configs) => configUtil.mergeDeclaration(configs[1], configs[0])) .then((normalized) => { assert.sameDeepMembers([normalized.mappings], [testConf.expected.mappings]); assert.sameDeepMembers(normalized.components, testConf.expected.components); @@ -329,24 +505,24 @@ describe('Config Util', () => { 'My_Namespace::My_Listener': ['My_Namespace::My_Consumer'] }); - configUtil.removeComponents(parsedConf, { filter: c => c.name === 'My_Consumer' }); + configUtil.removeComponents(parsedConf, { filter: (c) => c.name === 'My_Consumer' }); assert.deepStrictEqual(parsedConf.mappings, { 'f5telemetry_default::My_Listener': ['f5telemetry_default::My_Consumer_2', 'f5telemetry_default::My_Consumer_3'] }); assert.lengthOf(configUtil.getTelemetryListeners(parsedConf), 2); assert.lengthOf(configUtil.getTelemetryConsumers(parsedConf), 2); - configUtil.removeComponents(parsedConf, { filter: c => c.name === 'My_Listener', namespace: 'f5telemetry_default' }); + configUtil.removeComponents(parsedConf, { filter: (c) => c.name === 'My_Listener', namespace: 'f5telemetry_default' }); assert.deepStrictEqual(parsedConf.mappings, {}); assert.lengthOf(configUtil.getTelemetryListeners(parsedConf), 1); assert.lengthOf(configUtil.getTelemetryConsumers(parsedConf), 2); - configUtil.removeComponents(parsedConf, { class: 'Telemetry_Listener', namespace: c => c.namespace === 'My_Namespace' }); + configUtil.removeComponents(parsedConf, { class: 'Telemetry_Listener', namespace: (c) => c.namespace === 'My_Namespace' }); assert.deepStrictEqual(parsedConf.mappings, {}); assert.isEmpty(configUtil.getTelemetryListeners(parsedConf)); assert.lengthOf(configUtil.getTelemetryConsumers(parsedConf), 2); - configUtil.removeComponents(parsedConf, { filter: c => c.name === 'My_Consumer_2' }); + configUtil.removeComponents(parsedConf, { filter: (c) => c.name === 'My_Consumer_2' }); assert.deepStrictEqual(parsedConf.mappings, {}); assert.lengthOf(configUtil.getTelemetryConsumers(parsedConf), 1); @@ -511,8 +687,16 @@ describe('Config Util', () => { it('should return Telemetry_Pull_Consumer_System_Poller_Group for each consumer', () => parseDeclaration(declaration) .then(() => { + const pullPoller = configUtil.getTelemetrySystemPollers(configWorker.currentConfig, 'f5telemetry_default') + .find((pc) => pc.name === 'Pull_Poller_1'); + assert.isNotEmpty(pullPoller); + assert.isUndefined(configUtil.getTelemetryPullConsumerSystemPollerGroupForPullConsumer( + configWorker.currentConfig, + pullPoller + ), 'should return "undefined" when unable to find component'); + const pullConsumer1 = configUtil.getTelemetryPullConsumers(configWorker.currentConfig, 'f5telemetry_default') - .find(pc => pc.name === 'My_Pull_Consumer_1'); + .find((pc) => pc.name === 'My_Pull_Consumer_1'); assert.isNotEmpty(pullConsumer1); assert.deepStrictEqual( configUtil.getTelemetryPullConsumerSystemPollerGroupForPullConsumer( @@ -524,20 +708,20 @@ describe('Config Util', () => { id: 'f5telemetry_default::Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_1', name: 'Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_1', namespace: 'f5telemetry_default', - pullConsumer: 'My_Pull_Consumer_1', + pullConsumer: 'f5telemetry_default::My_Pull_Consumer_1', traceName: 'f5telemetry_default::Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_1', trace: { enable: false }, systemPollers: [ - 'Pull_Poller_1', - 'Pull_Poller_2' + 'f5telemetry_default::Pull_Poller_1::Pull_Poller_1', + 'f5telemetry_default::Pull_Poller_2::Pull_Poller_2' ] }, 'should return Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_1' ); const pullConsumer2 = configUtil.getTelemetryPullConsumers(configWorker.currentConfig, 'f5telemetry_default') - .find(pc => pc.name === 'My_Pull_Consumer_2'); + .find((pc) => pc.name === 'My_Pull_Consumer_2'); assert.isNotEmpty(pullConsumer1); assert.deepStrictEqual( configUtil.getTelemetryPullConsumerSystemPollerGroupForPullConsumer( @@ -549,20 +733,20 @@ describe('Config Util', () => { id: 'f5telemetry_default::Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_2', name: 'Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_2', namespace: 'f5telemetry_default', - pullConsumer: 'My_Pull_Consumer_2', + pullConsumer: 'f5telemetry_default::My_Pull_Consumer_2', traceName: 'f5telemetry_default::Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_2', trace: { enable: false }, systemPollers: [ - 'Pull_Poller_2', - 'Pull_Poller_3' + 'f5telemetry_default::Pull_Poller_2::Pull_Poller_2', + 'f5telemetry_default::Pull_Poller_3::Pull_Poller_3' ] }, 'should return Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_2' ); const pullConsumer3 = configUtil.getTelemetryPullConsumers(configWorker.currentConfig, 'My_Namespace') - .find(pc => pc.name === 'My_Pull_Consumer_1'); + .find((pc) => pc.name === 'My_Pull_Consumer_1'); assert.isNotEmpty(pullConsumer1); assert.deepStrictEqual( configUtil.getTelemetryPullConsumerSystemPollerGroupForPullConsumer( @@ -574,20 +758,20 @@ describe('Config Util', () => { id: 'My_Namespace::Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_1', name: 'Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_1', namespace: 'My_Namespace', - pullConsumer: 'My_Pull_Consumer_1', + pullConsumer: 'My_Namespace::My_Pull_Consumer_1', traceName: 'My_Namespace::Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_1', trace: { enable: false }, systemPollers: [ - 'Pull_Poller_1', - 'Pull_Poller_2' + 'My_Namespace::Pull_Poller_1::Pull_Poller_1', + 'My_Namespace::Pull_Poller_2::Pull_Poller_2' ] }, 'should return Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_1' ); const pullConsumer4 = configUtil.getTelemetryPullConsumers(configWorker.currentConfig, 'My_Namespace') - .find(pc => pc.name === 'My_Pull_Consumer_2'); + .find((pc) => pc.name === 'My_Pull_Consumer_2'); assert.isNotEmpty(pullConsumer1); assert.deepStrictEqual( configUtil.getTelemetryPullConsumerSystemPollerGroupForPullConsumer( @@ -599,14 +783,14 @@ describe('Config Util', () => { id: 'My_Namespace::Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_2', name: 'Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_2', namespace: 'My_Namespace', - pullConsumer: 'My_Pull_Consumer_2', + pullConsumer: 'My_Namespace::My_Pull_Consumer_2', traceName: 'My_Namespace::Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_2', trace: { enable: false }, systemPollers: [ - 'Pull_Poller_2', - 'Pull_Poller_3' + 'My_Namespace::Pull_Poller_2::Pull_Poller_2', + 'My_Namespace::Pull_Poller_3::Pull_Poller_3' ] }, 'should return Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_2' @@ -617,6 +801,16 @@ describe('Config Util', () => { describe('.getTelemetrySystemPollersForGroup()', () => { const declaration = { class: 'Telemetry', + Disabled_Pull_Poller_Standalone: { + class: 'Telemetry_System_Poller', + enable: false, + interval: 0 + }, + Disabled_Pull_Poller_1: { + class: 'Telemetry_System_Poller', + enable: false, + interval: 0 + }, Pull_Poller_1: { class: 'Telemetry_System_Poller', interval: 0 @@ -631,8 +825,7 @@ describe('Config Util', () => { interval: 0 }, Pull_Poller_4: { - class: 'Telemetry_System_Poller', - interval: 0 + class: 'Telemetry_System_Poller' }, Pull_System_1: { class: 'Telemetry_System', @@ -642,7 +835,10 @@ describe('Config Util', () => { Pull_System_2: { class: 'Telemetry_System', host: 'host2', - systemPoller: 'Pull_Poller_4' + systemPoller: [ + 'Pull_Poller_4', + 'Disabled_Pull_Poller_1' + ] }, Pull_System_3: { class: 'Telemetry_System', @@ -662,7 +858,8 @@ describe('Config Util', () => { systemPoller: [ 'Pull_Poller_1', 'Pull_Poller_2', - 'Pull_Poller_4' + 'Pull_Poller_4', + 'Disabled_Pull_Poller_Standalone' ] }, My_Pull_Consumer_2: { @@ -671,11 +868,22 @@ describe('Config Util', () => { systemPoller: [ 'Pull_Poller_2', 'Pull_Poller_3', - 'Pull_Poller_4' + 'Pull_Poller_4', + 'Disabled_Pull_Poller_Standalone' ] }, My_Namespace: { class: 'Telemetry_Namespace', + Disabled_Pull_Poller_Standalone: { + class: 'Telemetry_System_Poller', + enable: false, + interval: 0 + }, + Disabled_Pull_Poller_1: { + class: 'Telemetry_System_Poller', + enable: false, + interval: 0 + }, Pull_Poller_1: { class: 'Telemetry_System_Poller', interval: 0 @@ -701,7 +909,10 @@ describe('Config Util', () => { Pull_System_2: { class: 'Telemetry_System', host: 'host2', - systemPoller: 'Pull_Poller_4' + systemPoller: [ + 'Pull_Poller_4', + 'Disabled_Pull_Poller_1' + ] }, Pull_System_3: { class: 'Telemetry_System', @@ -721,7 +932,8 @@ describe('Config Util', () => { systemPoller: [ 'Pull_Poller_1', 'Pull_Poller_2', - 'Pull_Poller_4' + 'Pull_Poller_4', + 'Disabled_Pull_Poller_Standalone' ] }, My_Pull_Consumer_2: { @@ -730,7 +942,8 @@ describe('Config Util', () => { systemPoller: [ 'Pull_Poller_2', 'Pull_Poller_3', - 'Pull_Poller_4' + 'Pull_Poller_4', + 'Disabled_Pull_Poller_Standalone' ] } } @@ -739,89 +952,77 @@ describe('Config Util', () => { it('should return Telemetry_System_Poller for each group', () => parseDeclaration(declaration) .then(() => { const pullConsumer1 = configUtil.getTelemetryPullConsumers(configWorker.currentConfig, 'f5telemetry_default') - .find(pc => pc.name === 'My_Pull_Consumer_1'); + .find((pc) => pc.name === 'My_Pull_Consumer_1'); assert.isNotEmpty(pullConsumer1); const pollerGroup1 = configUtil.getTelemetryPullConsumerSystemPollerGroupForPullConsumer( configWorker.currentConfig, pullConsumer1 ); assert.isNotEmpty(pollerGroup1); - assert.deepStrictEqual(pollerGroup1.traceName, 'f5telemetry_default::Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_1'); + assert.deepStrictEqual(pollerGroup1.id, 'f5telemetry_default::Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_1'); assert.sameDeepMembers( configUtil.getTelemetrySystemPollersForGroup(configWorker.currentConfig, pollerGroup1) - .map(sp => sp.traceName), + .map((sp) => sp.id), [ - 'f5telemetry_default::Pull_System_1::Pull_Poller_2', - 'f5telemetry_default::Pull_System_2::Pull_Poller_4', - 'f5telemetry_default::Pull_System_3::Pull_Poller_2', - 'f5telemetry_default::Pull_System_4::Pull_Poller_4', - 'f5telemetry_default::Pull_Poller_1::Pull_Poller_1' + 'f5telemetry_default::Pull_Poller_1::Pull_Poller_1', + 'f5telemetry_default::Pull_System_2::Pull_Poller_4' ], 'should return Telemetry_System_Poller objects for f5telemetry_default::Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_1 group' ); const pullConsumer2 = configUtil.getTelemetryPullConsumers(configWorker.currentConfig, 'f5telemetry_default') - .find(pc => pc.name === 'My_Pull_Consumer_2'); + .find((pc) => pc.name === 'My_Pull_Consumer_2'); assert.isNotEmpty(pullConsumer2); const pollerGroup2 = configUtil.getTelemetryPullConsumerSystemPollerGroupForPullConsumer( configWorker.currentConfig, pullConsumer2 ); assert.isNotEmpty(pollerGroup2); - assert.deepStrictEqual(pollerGroup2.traceName, 'f5telemetry_default::Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_2'); + assert.deepStrictEqual(pollerGroup2.id, 'f5telemetry_default::Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_2'); assert.sameDeepMembers( configUtil.getTelemetrySystemPollersForGroup(configWorker.currentConfig, pollerGroup2) - .map(sp => sp.traceName), + .map((sp) => sp.id), [ - 'f5telemetry_default::Pull_System_1::Pull_Poller_2', - 'f5telemetry_default::Pull_System_2::Pull_Poller_4', - 'f5telemetry_default::Pull_System_3::Pull_Poller_2', - 'f5telemetry_default::Pull_System_4::Pull_Poller_4', - 'f5telemetry_default::Pull_Poller_3::Pull_Poller_3' + 'f5telemetry_default::Pull_Poller_3::Pull_Poller_3', + 'f5telemetry_default::Pull_System_2::Pull_Poller_4' ], 'should return Telemetry_System_Poller objects for f5telemetry_default::Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_2 group' ); const pullConsumer3 = configUtil.getTelemetryPullConsumers(configWorker.currentConfig, 'My_Namespace') - .find(pc => pc.name === 'My_Pull_Consumer_1'); + .find((pc) => pc.name === 'My_Pull_Consumer_1'); assert.isNotEmpty(pullConsumer3); const pollerGroup3 = configUtil.getTelemetryPullConsumerSystemPollerGroupForPullConsumer( configWorker.currentConfig, pullConsumer3 ); assert.isNotEmpty(pollerGroup3); - assert.deepStrictEqual(pollerGroup3.traceName, 'My_Namespace::Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_1'); + assert.deepStrictEqual(pollerGroup3.id, 'My_Namespace::Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_1'); assert.sameDeepMembers( configUtil.getTelemetrySystemPollersForGroup(configWorker.currentConfig, pollerGroup3) - .map(sp => sp.traceName), + .map((sp) => sp.id), [ - 'My_Namespace::Pull_System_1::Pull_Poller_2', - 'My_Namespace::Pull_System_2::Pull_Poller_4', - 'My_Namespace::Pull_System_3::Pull_Poller_2', - 'My_Namespace::Pull_System_4::Pull_Poller_4', - 'My_Namespace::Pull_Poller_1::Pull_Poller_1' + 'My_Namespace::Pull_Poller_1::Pull_Poller_1', + 'My_Namespace::Pull_System_2::Pull_Poller_4' ], 'should return Telemetry_System_Poller objects for My_Namespace::Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_1 group' ); const pullConsumer4 = configUtil.getTelemetryPullConsumers(configWorker.currentConfig, 'My_Namespace') - .find(pc => pc.name === 'My_Pull_Consumer_2'); + .find((pc) => pc.name === 'My_Pull_Consumer_2'); assert.isNotEmpty(pullConsumer4); const pollerGroup4 = configUtil.getTelemetryPullConsumerSystemPollerGroupForPullConsumer( configWorker.currentConfig, pullConsumer4 ); assert.isNotEmpty(pollerGroup4); - assert.deepStrictEqual(pollerGroup4.traceName, 'My_Namespace::Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_2'); + assert.deepStrictEqual(pollerGroup4.id, 'My_Namespace::Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_2'); assert.sameDeepMembers( configUtil.getTelemetrySystemPollersForGroup(configWorker.currentConfig, pollerGroup4) - .map(sp => sp.traceName), + .map((sp) => sp.id), [ - 'My_Namespace::Pull_System_1::Pull_Poller_2', - 'My_Namespace::Pull_System_2::Pull_Poller_4', - 'My_Namespace::Pull_System_3::Pull_Poller_2', - 'My_Namespace::Pull_System_4::Pull_Poller_4', - 'My_Namespace::Pull_Poller_3::Pull_Poller_3' + 'My_Namespace::Pull_Poller_3::Pull_Poller_3', + 'My_Namespace::Pull_System_2::Pull_Poller_4' ], 'should return Telemetry_System_Poller objects for My_Namespace::Telemetry_Pull_Consumer_System_Poller_Group_My_Pull_Consumer_2 group' ); diff --git a/test/unit/utils/deviceTests.js b/test/unit/utils/deviceTests.js index 90d0c08e..edb48b53 100644 --- a/test/unit/utils/deviceTests.js +++ b/test/unit/utils/deviceTests.js @@ -950,7 +950,7 @@ describe('Device Util (DeviceAsyncCLI)', () => { if (process.versions.node.startsWith('4.')) { parseURL = urllib.parse; } else { - parseURL = url => new urllib.URL(url); + parseURL = (url) => new urllib.URL(url); } const testScriptName = 'testScriptName'; diff --git a/test/unit/utils/ihealthTests.js b/test/unit/utils/ihealthTests.js index c74b0cfe..2f9c6e90 100644 --- a/test/unit/utils/ihealthTests.js +++ b/test/unit/utils/ihealthTests.js @@ -22,7 +22,6 @@ const deviceUtil = require('../../../src/lib/utils/device'); const ihealthUtil = require('../../../src/lib/utils/ihealth'); const testUtil = require('../shared/util'); - chai.use(chaiAsPromised); const assert = chai.assert; @@ -1490,7 +1489,7 @@ describe('iHealth Utils', () => { it('should fetch diagnostics', () => { ihealthMgr.qkviewURI = 'qkviewURI'; sinon.stub(ihealthUtil.IHealthAPI.prototype, 'fetchQkviewDiagnostics') - .callsFake(qkURI => Promise.resolve() + .callsFake((qkURI) => Promise.resolve() .then(() => { assert.strictEqual(qkURI, 'qkviewURI'); return { diagnostics: 'JSON' }; @@ -1511,7 +1510,7 @@ describe('iHealth Utils', () => { it('should fetch diagnostics from URI passed as arg', () => { ihealthMgr.qkviewURI = 'qkviewURI'; sinon.stub(ihealthUtil.IHealthAPI.prototype, 'fetchQkviewDiagnostics') - .callsFake(qkURI => Promise.resolve() + .callsFake((qkURI) => Promise.resolve() .then(() => { assert.strictEqual(qkURI, 'qkviewURI_arg'); return { diagnostics: 'JSON' }; @@ -1568,7 +1567,7 @@ describe('iHealth Utils', () => { it('should be able to check report status', () => { ihealthMgr.qkviewURI = 'qkviewURI'; sinon.stub(ihealthUtil.IHealthAPI.prototype, 'isQkviewReportReady') - .callsFake(qkURI => Promise.resolve() + .callsFake((qkURI) => Promise.resolve() .then(() => { assert.strictEqual(qkURI, 'qkviewURI'); return true; @@ -1586,7 +1585,7 @@ describe('iHealth Utils', () => { it('should be able to check report status using URI passed as arg', () => { ihealthMgr.qkviewURI = 'qkviewURI'; sinon.stub(ihealthUtil.IHealthAPI.prototype, 'isQkviewReportReady') - .callsFake(qkURI => Promise.resolve() + .callsFake((qkURI) => Promise.resolve() .then(() => { assert.strictEqual(qkURI, 'qkviewURI_arg'); return true; @@ -1604,7 +1603,7 @@ describe('iHealth Utils', () => { it('should be able to upload Qkview', () => { ihealthMgr.qkviewFile = 'qkviewFile'; sinon.stub(ihealthUtil.IHealthAPI.prototype, 'uploadQkview') - .callsFake(qkFile => Promise.resolve() + .callsFake((qkFile) => Promise.resolve() .then(() => { assert.strictEqual(qkFile, 'qkviewFile'); return 'qkviewURIFromIHealth'; @@ -1623,7 +1622,7 @@ describe('iHealth Utils', () => { it('should be able to upload Qkview file passed as arg', () => { ihealthMgr.qkviewFile = 'qkviewFile'; sinon.stub(ihealthUtil.IHealthAPI.prototype, 'uploadQkview') - .callsFake(qkFile => Promise.resolve() + .callsFake((qkFile) => Promise.resolve() .then(() => { assert.strictEqual(qkFile, 'qkviewFile_arg'); return 'qkviewURIFromIHealth'; diff --git a/test/unit/utils/miscTests.js b/test/unit/utils/miscTests.js index a9a56c78..55c2db39 100644 --- a/test/unit/utils/miscTests.js +++ b/test/unit/utils/miscTests.js @@ -727,7 +727,6 @@ describe('Misc Util', () => { }); }); - describe('.generateUuid', () => { it('should return valid and unique values with multiple/consecutive calls', () => { const v4Regex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; @@ -763,7 +762,6 @@ describe('Misc Util', () => { assert.strictEqual(util.getProperty(obj, 'child1.prop1'), true); }); - it('should return correct property value (object)', () => { assert.deepStrictEqual( util.getProperty(obj, 'child1.prop2'), @@ -827,7 +825,7 @@ describe('Misc Util', () => { it('should not be able to cancel once canceled', () => { const promise = util.sleep(50); assert.isTrue(promise.cancel()); - return promise.catch(err => err) + return promise.catch((err) => err) .then((err) => { assert.isTrue(/canceled/.test(err)); assert.isFalse(promise.cancel(new Error('expected error'))); diff --git a/test/unit/utils/monitorTests.js b/test/unit/utils/monitorTests.js index 70bff583..dbbc19f4 100644 --- a/test/unit/utils/monitorTests.js +++ b/test/unit/utils/monitorTests.js @@ -266,7 +266,6 @@ describe('Monitor Util', () => { memUsageStub = sinon.stub(monitor, 'getProcessMemUsage'); }); - const usageIntervals = [ { sec: 30, memUsagePercent: 24, name: 'should configure <25%' }, { sec: 15, memUsagePercent: 49, name: 'should configure >=25% and < 50%' }, diff --git a/test/unit/utils/normalizeTests.js b/test/unit/utils/normalizeTests.js index 623deac9..2b0d1f18 100644 --- a/test/unit/utils/normalizeTests.js +++ b/test/unit/utils/normalizeTests.js @@ -329,7 +329,6 @@ describe('Normalize Util', () => { assert.deepStrictEqual(result, expectedResult); }); - it('should format as json and filter/rename', () => { const data = 'named_key,key1,key2,key3\nname,value,value,value'; const expectedResult = { diff --git a/test/unit/utils/promiseTests.js b/test/unit/utils/promiseTests.js index cf8d0303..b5fa07db 100644 --- a/test/unit/utils/promiseTests.js +++ b/test/unit/utils/promiseTests.js @@ -98,7 +98,7 @@ describe('Promise Util', () => { Promise.resolve(1), Promise.reject(err2) ]) - .then(statuses => promiseUtil.getValues(statuses, true)), + .then((statuses) => promiseUtil.getValues(statuses, true)), [1, undefined] ); }); diff --git a/test/unit/utils/timersTests.js b/test/unit/utils/timersTests.js index f371619a..a6aa888d 100644 --- a/test/unit/utils/timersTests.js +++ b/test/unit/utils/timersTests.js @@ -79,7 +79,7 @@ describe('Timer Tests', () => { name: 'SlidingTimer', TimerClass: timers.SlidingTimer } - ].forEach(testConf => testUtil.getCallableDescribe(testConf)(testConf.name, () => { + ].forEach((testConf) => testUtil.getCallableDescribe(testConf)(testConf.name, () => { let timerInst; beforeEach(() => { @@ -1063,7 +1063,7 @@ describe('Timer Tests', () => { timestamps.push(Date.now()); if (callCount < 4) { callCount += 1; - return new Promise(innerResolve => setTimeout(innerResolve, 1100 + callCount * 100)); + return new Promise((innerResolve) => setTimeout(innerResolve, 1100 + callCount * 100)); } return timerInst.stop().then(resolve, reject); }, 1); @@ -1207,7 +1207,7 @@ describe('Timer Tests', () => { it('should catch error (function=promise)', () => assert.isFulfilled( new Promise((resolve, reject) => { intervalID = timers.setSlidingInterval( - arg => Promise.reject(new Error(arg)), + (arg) => Promise.reject(new Error(arg)), { interval: 1000, onError: (error) => { @@ -1258,7 +1258,7 @@ describe('Timer Tests', () => { timestamps.push(Date.now()); if (callCount < 4) { callCount += 1; - return new Promise(innerResolve => setTimeout(innerResolve, 1100 + callCount * 100)); + return new Promise((innerResolve) => setTimeout(innerResolve, 1100 + callCount * 100)); } return timers.clearSlidingInterval(intervalID, resolve); }, @@ -1281,13 +1281,13 @@ describe('Timer Tests', () => { 3500 // third starts immediately after second - 2200 + 1300 delay ], 200, 'should adjust interval'); assertApproximatelyArray( - durations.slice(0, 3).map(p => p[0]), + durations.slice(0, 3).map((p) => p[0]), [1200, 1300, 1400], 200, 'should match expected duration' ); assert.deepStrictEqual( - durations.slice(0, 3).map(p => p[1]), + durations.slice(0, 3).map((p) => p[1]), [1000, 1000, 1000], 'should pass configured interval value' ); @@ -1312,7 +1312,7 @@ describe('Timer Tests', () => { }, { interval: 1000, - onError: () => new Promise(innerResolve => setTimeout( + onError: () => new Promise((innerResolve) => setTimeout( innerResolve, 1100 + callCount * 100 )), onIntervalSlide: (duration, interval) => { @@ -1331,13 +1331,13 @@ describe('Timer Tests', () => { 3500 // third starts immediately after second - 2200 + 1300 delay ], 200, 'should adjust interval'); assertApproximatelyArray( - durations.slice(0, 3).map(p => p[0]), + durations.slice(0, 3).map((p) => p[0]), [1200, 1300, 1400], 200, 'should match expected duration' ); assert.deepStrictEqual( - durations.slice(0, 3).map(p => p[1]), + durations.slice(0, 3).map((p) => p[1]), [1000, 1000, 1000], 'should pass configured interval value' ); @@ -1356,7 +1356,7 @@ describe('Timer Tests', () => { .then(() => { assert.strictEqual(spy.callCount, 0, 'should not call function once canceled'); assert.deepStrictEqual( - cancelSpy.args.map(args => args[0]), + cancelSpy.args.map((args) => args[0]), ['sync', 'async'], 'should run callback in async way' ); @@ -1368,7 +1368,7 @@ describe('Timer Tests', () => { intervalID = timers.setSlidingInterval(spy, 30); fakeClock.clockForward(1, { promisify: true }); return testUtil.sleep(5) - .then(() => new Promise(resolve => timers.clearSlidingInterval(intervalID, resolve))) + .then(() => new Promise((resolve) => timers.clearSlidingInterval(intervalID, resolve))) .then(() => testUtil.sleep(30)) .then(() => { assert.strictEqual(spy.callCount, 0, 'should not call function once canceled'); diff --git a/test/unit/utils/tracerTests.js b/test/unit/utils/tracerTests.js index ff76fdad..3bf965fa 100644 --- a/test/unit/utils/tracerTests.js +++ b/test/unit/utils/tracerTests.js @@ -71,7 +71,7 @@ describe('Tracer', () => { fs.rmdirSync(dirPath); } }; - const addTimestamps = data => data.map(item => ({ data: item, timestamp: new Date().toISOString() })); + const addTimestamps = (data) => data.map((item) => ({ data: item, timestamp: new Date().toISOString() })); beforeEach(() => { coreStub = stubs.coreStub({ @@ -402,7 +402,7 @@ describe('Tracer', () => { 1, 10, 100 - ].forEach(maxRecords => it(`should write max ${maxRecords} records`, () => { + ].forEach((maxRecords) => it(`should write max ${maxRecords} records`, () => { let totalRecords = 0; let allWrittenData = []; @@ -416,7 +416,7 @@ describe('Tracer', () => { } return Promise.all(promises).then(() => writtenData); }; - const getExpectedData = data => addTimestamps(data.slice(data.length - maxRecords)); + const getExpectedData = (data) => addTimestamps(data.slice(data.length - maxRecords)); const validateTracerData = (writtenData) => { allWrittenData = allWrittenData.concat(writtenData); const data = readTraceFile(tracerFile); @@ -717,7 +717,7 @@ describe('Tracer', () => { .then(() => { const registered = tracer.registered(); assert.sameDeepMembers( - registered.map(t => t.path), + registered.map((t) => t.path), [ '/var/tmp/telemetry/Telemetry_Consumer.f5telemetry_default::My_Consumer', '/var/tmp/telemetry/Telemetry_Listener.f5telemetry_default::My_Listener_With_Dual_Tracing', @@ -766,7 +766,7 @@ describe('Tracer', () => { .then(() => { const registered = tracer.registered(); assert.sameDeepMembers( - registered.map(t => t.path), + registered.map((t) => t.path), [ '/var/tmp/telemetry/Telemetry_Consumer.f5telemetry_default::My_Consumer', '/var/tmp/telemetry/Telemetry_Listener.f5telemetry_default::My_Listener_With_Dual_Tracing', @@ -800,7 +800,7 @@ describe('Tracer', () => { assert.isTrue(tracerInst.disabled, 'should be disabled once unregistered'); }); assert.sameDeepMembers( - tracer.registered().map(t => t.path), + tracer.registered().map((t) => t.path), [ '/var/tmp/telemetry/Telemetry_System_Poller.f5telemetry_default::My_Enabled_Poller_With_Disabled_Trace::My_Enabled_Poller_With_Disabled_Trace', 'listener2' @@ -841,7 +841,7 @@ describe('Tracer', () => { .then(() => { const registered = tracer.registered(); // remember those instances - should survive all updates assert.sameDeepMembers( - registered.map(t => t.path), + registered.map((t) => t.path), [ '/var/tmp/telemetry/Telemetry_Consumer.f5telemetry_default::My_Consumer', '/var/tmp/telemetry/Telemetry_Listener.f5telemetry_default::My_Listener_With_Dual_Tracing', @@ -882,7 +882,7 @@ describe('Tracer', () => { assert.isFalse(tracerInst.disabled, 'should not disable pre-existing tracers'); }); assert.sameDeepMembers( - tracer.registered().map(t => t.path), + tracer.registered().map((t) => t.path), [ '/var/tmp/telemetry/Telemetry_Consumer.f5telemetry_default::My_Consumer', '/var/tmp/telemetry/Telemetry_Listener.f5telemetry_default::My_Listener_With_Dual_Tracing', @@ -911,7 +911,7 @@ describe('Tracer', () => { assert.isFalse(tracerInst.disabled, 'should not disable pre-existing tracers'); }); assert.sameDeepMembers( - tracer.registered().map(t => t.path), + tracer.registered().map((t) => t.path), [ '/var/tmp/telemetry/Telemetry_Consumer.f5telemetry_default::My_Consumer', '/var/tmp/telemetry/Telemetry_Listener.f5telemetry_default::My_Listener_With_Dual_Tracing', @@ -931,7 +931,7 @@ describe('Tracer', () => { assert.isFalse(tracerInst.disabled, 'should not disable pre-existing tracers'); }); assert.sameDeepMembers( - tracer.registered().map(t => t.path), + tracer.registered().map((t) => t.path), [ '/var/tmp/telemetry/Telemetry_Consumer.f5telemetry_default::My_Consumer', '/var/tmp/telemetry/Telemetry_Listener.f5telemetry_default::My_Listener_With_Dual_Tracing', @@ -975,7 +975,7 @@ describe('Tracer', () => { .then(() => { const registered = tracer.registered(); assert.sameDeepMembers( - registered.map(t => t.path), + registered.map((t) => t.path), [ '/var/tmp/telemetry/Telemetry_Consumer.f5telemetry_default::Default_Push_Consumer', '/var/tmp/telemetry/Telemetry_Pull_Consumer.f5telemetry_default::Default_Pull_Consumer', diff --git a/test/winstonLogger.js b/test/winstonLogger.js index 6e62818a..08ce142b 100644 --- a/test/winstonLogger.js +++ b/test/winstonLogger.js @@ -15,8 +15,8 @@ const path = require('path'); const winston = require('winston'); const LOG_FILE = `${__dirname}/artifacts/testoutput.log`; -const LOG_DST = ['console', 'file'].find(item => item === (process.env.LOG_DST || 'file').trim()); -const LOG_SECRETS_ENABLED = !!['1', 'true'].find(item => item === process.env.LOG_SECRETS); +const LOG_DST = ['console', 'file'].find((item) => item === (process.env.LOG_DST || 'file').trim()); +const LOG_SECRETS_ENABLED = !!['1', 'true'].find((item) => item === process.env.LOG_SECRETS); const SECRETS_MASK = '*********'; const KEYWORDS_TO_MASK = [ @@ -94,10 +94,9 @@ function maskSecrets(msg) { return ret; } - const timestamp = () => (new Date()).toISOString(); /* eslint-disable prefer-template */ -const formatter = options => `[${options.timestamp()}][${options.level.toUpperCase()}] ` +const formatter = (options) => `[${options.timestamp()}][${options.level.toUpperCase()}] ` + `${maskSecrets(options.message ? options.message : '')}` + `${maskSecrets(options.meta && Object.keys(options.meta).length ? ('\n' + JSON.stringify(options.meta, null, 4)) : '')}`; diff --git a/versions.json b/versions.json index 96149b4b..7b59d4ef 100644 --- a/versions.json +++ b/versions.json @@ -1,7 +1,7 @@ { "versionMetaTimestamp": 1540928503, "latestVersion": { - "name": "1.23 (non-LTS)", + "name": "1.25 (non-LTS)", "url": "/products/extensions/f5-telemetry-streaming/latest/" }, "otherVersions": [