From 71b162a457be0a2bab9c5462d4ef91aaf4bbf3d3 Mon Sep 17 00:00:00 2001 From: Matt Apperson Date: Tue, 26 Jun 2018 10:48:27 -0400 Subject: [PATCH] [Beats Management] Move to Ingest UI arch and initial TS effort (#20039) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Beats Management] Initial scaffolding for plugin (#18977) * Initial scaffolding for Beats plugin * Removing bits not (yet) necessary in initial scaffolding * [Beats Management] Install Beats index template on plugin init (#19072) * Install Beats index template on plugin init * Adding missing files * [Beats Management] APIs: Create enrollment tokens (#19018) * WIP checkin * Register API routes * Fixing typo in index name * Adding TODOs * Removing commented out license checking code that isn't yet implemented * Remove unnecessary async/await * Don't return until indices have been refreshed * Add API integration test * Converting to Jest test * Fixing API for default case + adding test for it * Fixing copy pasta typos * Adding TODO * Fixing variable name * Using a single index * Adding expiration date field * Adding test for expiration date field * Ignore non-existent index * Fixing logic in test * Creating constant for default enrollment tokens TTL value * Updating test * Fixing name of test file (#19100) * [Beats Management] APIs: Enroll beat (#19056) * WIP checkin * Add API integration test * Converting to Jest test * Create API for enrolling a beat * Handle invalid or expired enrollment tokens * Use create instead of index to prevent same beat from being enrolled twice * Adding unit test for duplicate beat enrollment * Do not persist enrollment token with beat once token has been checked and used * Fix datatype of host_ip field * Make Kibana API guess host IP instead of requiring it in payload * Fixing error introduced in rebase conflict resolution * [Beats Management] APIs: List beats (#19086) * WIP checkin * Add API integration test * Converting to Jest test * WIP checkin * Fixing API for default case + adding test for it * Fixing copy pasta typos * Fixing variable name * Using a single index * Implementing GET /api/beats/agents API * Updating mapping * [Beats Management] APIs: Verify beats (#19103) * WIP checkin * WIP checkin * Add API integration test * Converting to Jest test * Fixing API for default case + adding test for it * Fixing copy pasta typos * Fixing variable name * Using a single index * Implementing GET /api/beats/agents API * Creating POST /api/beats/agents/verify API * Refactoring: extracting out helper functions * Fleshing out remaining tests * Expanding TODO note so I won't forget :) * Fixing file name * Updating mapping * Moving TODO comment to right file * Rename determine* helper functions to find* * Fixing assertions (#19194) * [Beats Management] APIs: Update beat (#19148) * WIP checkin * WIP checkin * Add API integration test * Converting to Jest test * Fixing API for default case + adding test for it * Fixing copy pasta typos * Fixing variable name * Using a single index * Implementing GET /api/beats/agents API * Creating POST /api/beats/agents/verify API * Refactoring: extracting out helper functions * Expanding TODO note so I won't forget :) * Fixing file name * Updating mapping * Add API tests * Update template to allow version field for beat * Implement PUT /api/beats/agent/{beat ID} API * Make enroll beat code consistent with update beat code * Fixing minor typo in TODO comment * Allow version in request payload * Make sure beat is not updated in ES in error scenarios * Adding version as required field in Enroll Beat API payload * Using destructuring * Fixing rename that was accidentally reversed in conflict fixing * [Beats Management] APIs: take auth tokens via headers (#19210) * WIP checkin * WIP checkin * Add API integration test * Converting to Jest test * Fixing API for default case + adding test for it * Fixing copy pasta typos * Fixing variable name * Using a single index * Implementing GET /api/beats/agents API * Creating POST /api/beats/agents/verify API * Refactoring: extracting out helper functions * Expanding TODO note so I won't forget :) * Fixing file name * Updating mapping * Fixing minor typo in TODO comment * Make "Enroll Beat" API take enrollment token via header instead of request body * Make "Update Beat" API take access token via header instead of request body * [Beats Management] APIs: Create configuration block (#19270) * WIP checkin * WIP checkin * Add API integration test * Converting to Jest test * Fixing API for default case + adding test for it * Fixing copy pasta typos * Fixing variable name * Using a single index * Implementing GET /api/beats/agents API * Creating POST /api/beats/agents/verify API * Refactoring: extracting out helper functions * Expanding TODO note so I won't forget :) * Fixing file name * Updating mapping * Fixing minor typo in TODO comment * Implementing POST /api/beats/configuration_blocks API * Removing unnecessary escaping * Fleshing out types + adding validation for them * Making output singular (was outputs) * Removing metricbeat.inputs * Revert implementation of `POST /api/beats/configuration_blocks` API (#19340) This API allowed the user to operate at a level of abstraction that is unnecessarily and dangerously too low. A better API would be at one level higher, where users can create, update, and delete tags (where a tag can contain multiple configuration blocks). * [Beats Management] APIs: Create or update tag (#19342) * Updating mappings * Implementing PUT /api/beats/tag/{tag} API * [Beats Management] Prevent timing attacks when checking auth tokens (#19363) * Using crypto.timingSafeEqual() for comparing auth tokens * Prevent subtler timing attack in token comparison function * Introduce random delay after we try to find token in ES to mitigate timing attack * Remove random delay * [Beats Management] APIs: Assign tag(s) to beat(s) (#19431) * Using crypto.timingSafeEqual() for comparing auth tokens * Introduce random delay after we try to find token in ES to mitigate timing attack * Rename "determine" to "find" * Remove random delay * Starting to implement POST /api/beats/beats_tags API * Changing API * Updating tests for changes to API * Updating ES archive * Renaming * Use destructuring * Moving start of script to own line to increase readability * Using destructuring * [Beats Management] APIs: Remove tag(s) from beat(s) (#19440) * Using crypto.timingSafeEqual() for comparing auth tokens * Introduce random delay after we try to find token in ES to mitigate timing attack * Remove random delay * Starting to implement POST /api/beats/beats_tags API * Changing API * Updating tests for changes to API * Renaming * Use destructuring * Using crypto.timingSafeEqual() for comparing auth tokens * Introduce random delay after we try to find token in ES to mitigate timing attack * Implementing `POST /api/beats/agents_tags/removals` API * Updating ES archive * Use destructuring * Moving start of script to own line to increase readability * Nothing to remove if there are no existing tags! * Updating tests to match changes in bulk update painless script * Use destructuring * Ported over base types and arch structure * move management of installIndexTemplate into the framework adapter * ts-lint fix * tslint fixes * more ts tweaks * fix paths * added several working endpoints * add more routes and bug fixes * fix linting * fix type remove CRUFT * remove more cruft * remove more CRUFT * added comments, change plurality * add tsconfig file * add extends path * fixed typo * serveral PR review fixes * fixed lodash type version * “fix” types by applying a lot of any --- .../common/constants/index_names.ts | 9 - .../call_with_request_factory.js | 18 - .../common/constants/index.js | 8 - x-pack/plugins/monitoring/public/index.css | 439 ++++++++++++++++++ 4 files changed, 439 insertions(+), 35 deletions(-) delete mode 100644 x-pack/plugins/beats_management/common/constants/index_names.ts delete mode 100644 x-pack/plugins/grokdebugger/server/lib/call_with_request_factory/call_with_request_factory.js delete mode 100644 x-pack/plugins/license_management/common/constants/index.js create mode 100644 x-pack/plugins/monitoring/public/index.css diff --git a/x-pack/plugins/beats_management/common/constants/index_names.ts b/x-pack/plugins/beats_management/common/constants/index_names.ts deleted file mode 100644 index f8d20fb79c360..0000000000000 --- a/x-pack/plugins/beats_management/common/constants/index_names.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export const INDEX_NAMES = { - BEATS: '.management-beats', -}; diff --git a/x-pack/plugins/grokdebugger/server/lib/call_with_request_factory/call_with_request_factory.js b/x-pack/plugins/grokdebugger/server/lib/call_with_request_factory/call_with_request_factory.js deleted file mode 100644 index b9a77a1a0362b..0000000000000 --- a/x-pack/plugins/grokdebugger/server/lib/call_with_request_factory/call_with_request_factory.js +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { once } from 'lodash'; - -const callWithRequest = once((server) => { - const cluster = server.plugins.elasticsearch.getCluster('data'); - return cluster.callWithRequest; -}); - -export const callWithRequestFactory = (server, request) => { - return (...args) => { - return callWithRequest(server)(request, ...args); - }; -}; diff --git a/x-pack/plugins/license_management/common/constants/index.js b/x-pack/plugins/license_management/common/constants/index.js deleted file mode 100644 index 59b61f7b99f98..0000000000000 --- a/x-pack/plugins/license_management/common/constants/index.js +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { PLUGIN } from './plugin'; -export { BASE_PATH } from './base_path'; diff --git a/x-pack/plugins/monitoring/public/index.css b/x-pack/plugins/monitoring/public/index.css new file mode 100644 index 0000000000000..03375c604fdb1 --- /dev/null +++ b/x-pack/plugins/monitoring/public/index.css @@ -0,0 +1,439 @@ +@keyframes euiAnimFadeIn { + 0% { + opacity: 0; } + 100% { + opacity: 1; } } + +@keyframes euiGrow { + 0% { + opacity: 0; } + 1% { + opacity: 0; + transform: scale(0); } + 100% { + opacity: 1; + transform: scale(1); } } + +/** + * Set scroll bar appearance on Chrome. + */ +@keyframes focusRingAnimate { + 0% { + box-shadow: 0 0 0 6px rgba(0, 121, 165, 0); } + 100% { + box-shadow: 0 0 0 2px rgba(0, 121, 165, 0.3); } } + +@keyframes focusRingAnimateLarge { + 0% { + box-shadow: 0 0 0 10px rgba(0, 121, 165, 0); } + 100% { + box-shadow: 0 0 0 4px rgba(0, 121, 165, 0.3); } } + +.tab-no-data, .tab-overview, .tab-license { + background: #F5F5F5; } + +.pui-tooltip-inner { + font-size: 12.0px; } + +.monitoring-tooltip__trigger, +.monitoring-tooltip__trigger:hover { + color: #2D2D2D; } + +.betaIcon { + color: #666; } + +.xpack-breadcrumbs { + min-height: 37px; + padding: 8px 10px; + margin: 0; } + +.monRhythmChart { + position: relative; + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; + -ms-flex: 1 0 auto; + flex: 1 0 auto; } + +.monRhythmChart__title { + color: #2D2D2D; + margin: 0 0 8px; } + +.monRhythmChart__content { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + display: -ms-flexbox; + display: flex; + -ms-flex: 1 0 auto; + flex: 1 0 auto; } + +.monRhythmChart__visualization { + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; + -ms-flex: 1 0 auto; + flex: 1 0 auto; + position: relative; } + .monRhythmChart__visualization > div { + min-width: 1px; + width: 100%; + height: 100%; } + .monRhythmChart__visualization div { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-touch-callout: none; + -webkit-tap-highlight-color: transparent; } + +.monRhythmChart__legendItem { + font-size: 12.0px; + cursor: pointer; + color: #2D2D2D; } + .monRhythmChart__legendItem-isDisabled { + opacity: 0.5; } + +.monRhythmChart__legendHorizontal { + margin-top: 4px; } + +.monRhythmChart__legendLabel { + overflow: hidden; + white-space: nowrap; } + +.monRhythmChart__legendValue { + overflow: hidden; + white-space: nowrap; + margin-left: 4px; } + +.noData__content { + max-width: 600px; + text-align: center; + position: relative; } + +.monSparkline { + height: 2em; } + +.monSparklineTooltip { + font-weight: normal; + background: rgba(63, 63, 63, 0.7); + font-size: 12.0px; + padding: 4px; + border-radius: 4px; + pointer-events: none; } + +.monSparklineTooltip__xValue { + color: rgba(255, 255, 255, 0.7); } + +.monSparklineTooltip__yValue { + color: #FFF; } + +.monSparklineTooltip__caret { + font-size: 18.0px; + color: rgba(63, 63, 63, 0.7); + display: none; } + +.monSparklineTooltip__container { + position: fixed; + z-index: 2000; + display: -ms-flexbox; + display: flex; + -ms-flex-direction: row; + flex-direction: row; + -ms-flex-pack: center; + justify-content: center; + -ms-flex-align: center; + align-items: center; } + +.monSummaryStatus { + background-color: #F5F5F5; + border-bottom: 1px solid #D9D9D9; + padding: 16px; } + +/* + * A table that stretches the full window width and columns size appropriately to content. + * The .monitoringTable class is on the KuiControlledTable instance. + * The table within it requires the shrinkToContent flag as well as width set to 100% + */ +.monTable .kuiTable { + width: 100%; } + +.monTableCell__clusterCellExpired, +.monTableCell__offline { + color: #2D2D2D; } + +.monTableCell__clusterCellLiscense { + font-size: 16px; } + +.monTableCell__clusterCellExpiration { + color: #666; + font-size: 14px; + font-size: 1rem; + line-height: 1.5; } + +.monTableCell__name, +.monTableCell__status, +.monTableCell__version { + font-size: 16.0px; + font-size: 1.14286rem; + line-height: 1.5; } + +.monTableCell__transportAddress { + color: #666; + font-size: 14px; + font-size: 1rem; + line-height: 1.5; } + +.monTableCell__number { + font-size: 24.0px; + font-size: 1.71429rem; + line-height: 1.25; + font-weight: 600; } + +.monTableCell__splitNumber { + font-size: 16.0px; + font-size: 1.14286rem; + line-height: 1.5; } + +.monTableCell__metricCellMetric { + display: inline-block; + font-size: 24.0px; + font-size: 1.71429rem; + line-height: 1.25; + font-weight: 600; } + +.monTableCell__metricCellSlopeArrow { + display: inline-block; + margin-left: 4px; + font-size: 24.0px; + font-size: 1.71429rem; + line-height: 1.25; + font-weight: 600; } + +.monTableCell__metricCellMixMax { + display: inline-block; + text-align: right; + margin-left: 4px; + color: #666; + font-size: 14px; + font-size: 1rem; + line-height: 1.5; } + +monitoring-main[page="pipeline"] { + background: #F5F5F5; + min-height: 100vh; } + +.monPipelineViewer { + max-width: 1000px; } + +.monPipelineViewer__statement { + padding-left: 12px; } + +.monPipelineViewer__plugin { + margin-left: 4px; } + +.monPipelineViewer__spaceContainer { + background-color: #FFF; + -ms-flex-item-align: stretch; + align-self: stretch; + display: -ms-flexbox; + display: flex; + border-bottom: solid 2px #FFF; } + +.monPipelineViewer__spacer { + width: 12px; + -ms-flex-item-align: stretch; + -ms-grid-row-align: stretch; + align-self: stretch; + margin-left: 12px; + border-left: 1px #D9D9D9 dashed; } + .monPipelineViewer__spacer:last-child { + width: 0px; } + .monPipelineViewer__spacer:first-child { + margin-left: 23px; } + +.monPipelineViewer__metric { + text-align: right; } + .monPipelineViewer__metric--cpuTime { + width: 40px; } + .monPipelineViewer__metric--events, .monPipelineViewer__metric--eventsEmitted { + width: 160px; } + .monPipelineViewer__metric--eventMillis { + width: 80px; } + +.monPipelineViewer__queueMessage { + margin-left: 24px; + color: #666; } + +.monPipelineViewer__list .monPipelineViewer__listItem { + display: -ms-flexbox; + display: flex; + min-height: 32px; + -ms-flex-align: center; + align-items: center; + padding-right: 12px; } + .monPipelineViewer__list .monPipelineViewer__listItem:nth-child(2n+1) { + background: whitesmoke; } + +.monPipelineViewer__conditional { + font-weight: bold; } + +img.lspvDetailDrawerIcon { + display: inline; + margin: 0 4px 0 0; + width: auto; + vertical-align: middle; } + +.lspvDetailDrawerSparklineContainer { + width: 7vw; } + +@media only screen and (min-width: 769px) and (max-width: 991px) { + .monPipelineViewer .monPipelineViewer__spacer { + border: none; } + .monPipelineViewer .monPipelineViewer__metricFlexItem { + margin-bottom: 4px !important; } + .monPipelineViewer .monPipelineViewer__metric { + text-align: left; + padding-left: 32px; } } + +.monChart__container { + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; + -ms-flex: 1 0 auto; + flex: 1 0 auto; + height: 200px; + margin-bottom: 16px; } + +.monChart__tooltipTrigger { + float: right; + position: relative; + top: 4px; + right: 16px; } + +.monChart__tooltipLabel, +.monChart__tooltipValue { + text-align: left; + font-size: 12.0px; + padding: 4px; + word-wrap: break-word; + white-space: normal; } + +.monChart__tooltipLabel { + font-weight: 700; } + +monitoring-shard-allocation { + display: block; + border-top: 8px solid #F5F5F5; } + +.monClusterTitle { + font-size: 18.0px; + margin: 0; } + +.monCluster cluster-view { + display: block; } + +.monCluster .parent { + padding-top: 14px; + border-left: 3px solid #017F75 !important; } + .monCluster .parent.red { + border-left: 3px solid #A30000 !important; } + .monCluster .parent.yellow { + border-left: 3px solid #E5830E !important; } + +.monCluster td.unassigned { + vertical-align: middle; + width: 150px; } + +.monCluster .children { + display: -ms-flexbox; + display: flex; + -ms-flex-flow: row wrap; + flex-flow: row wrap; } + +.monCluster .child { + display: -ms-flexbox; + display: flex; + -ms-flex-item-align: center; + align-self: center; + background-color: #666; + margin: 5px; } + .monCluster .child.index { + border-left: 4px solid #017F75; } + .monCluster .child.index.red { + border-left: 4px solid #A30000; } + .monCluster .child.index.yellow { + border-left: 4px solid #E5830E; } + .monCluster .child .title { + padding: 5px 7px; + float: left; + text-align: center; + font-size: 12px; + font: 10px sans-serif; + color: #FFF; } + .monCluster .child .title a { + color: #FFF; + text-decoration: none; } + .monCluster .child .title i { + margin-left: 5px; } + .monCluster .child.unassigned .title { + color: #999; + display: none; } + +.monCluster th { + text-align: left; } + +.monCluster td:first-child { + width: 200px; } + +.monCluster .shard { + -ms-flex-item-align: center; + -ms-grid-row-align: center; + align-self: center; + padding: 5px 7px; + background-color: #0079a5; + font: 10px sans-serif; + border-left: 1px solid #FFF; + position: relative; + color: #FFF; } + .monCluster .shard .shard-tooltip { + padding: 5px; + bottom: 25px; + left: 0; + background-color: #D9D9D9; + position: absolute; + color: #666; + border: 1px solid #D9D9D9; + white-space: nowrap; } + .monCluster .shard.replica { + background-color: #268db3; } + .monCluster .shard.unassigned { + background-color: #999 !important; + color: #000; } + .monCluster .shard.emergency { + background-color: #A30000 !important; + color: #000; } + .monCluster .shard.relocating { + background-color: #490092; } + .monCluster .shard.initializing { + background-color: #6426a2; } + +.monCluster .legend { + font-size: 12px; + background-color: #FFF; + color: #3F3F3F; + padding: 5px; } + .monCluster .legend .title { + margin-left: 5px; + font-weight: bold; } + .monCluster .legend span.shard { + float: none; + display: inline-block; + margin: 0 5px 0 10px; + padding: 0 4px; } +/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL25vZGVfbW9kdWxlcy9AZWxhc3RpYy9ldWkvc3JjL2dsb2JhbF9zdHlsaW5nL3ZhcmlhYmxlcy9fYW5pbWF0aW9ucy5zY3NzIiwiLi4vLi4vLi4vLi4vbm9kZV9tb2R1bGVzL0BlbGFzdGljL2V1aS9zcmMvZ2xvYmFsX3N0eWxpbmcvbWl4aW5zL19oZWxwZXJzLnNjc3MiLCIuLi8uLi8uLi8uLi9ub2RlX21vZHVsZXMvQGVsYXN0aWMvZXVpL3NyYy9nbG9iYWxfc3R5bGluZy9taXhpbnMvX3N0YXRlcy5zY3NzIiwiLi4vLi4vLi4vLi4vbm9kZV9tb2R1bGVzL0BlbGFzdGljL2V1aS9zcmMvZ2xvYmFsX3N0eWxpbmcvdmFyaWFibGVzL19jb2xvcnMuc2NzcyIsIl9oYWNrcy5zY3NzIiwiLi4vLi4vLi4vLi4vbm9kZV9tb2R1bGVzL0BlbGFzdGljL2V1aS9zcmMvdGhlbWVzL2s2L2s2X2dsb2JhbHMuc2NzcyIsIi4uLy4uLy4uLy4uL25vZGVfbW9kdWxlcy9AZWxhc3RpYy9ldWkvc3JjL3RoZW1lcy9rNi9rNl9jb2xvcnNfbGlnaHQuc2NzcyIsImNvbXBvbmVudHMvY2hhcnQvX2NoYXJ0LnNjc3MiLCIuLi8uLi8uLi8uLi9ub2RlX21vZHVsZXMvQGVsYXN0aWMvZXVpL3NyYy9nbG9iYWxfc3R5bGluZy92YXJpYWJsZXMvX3NpemUuc2NzcyIsImNvbXBvbmVudHMvbm9fZGF0YS9fbm9fZGF0YS5zY3NzIiwiY29tcG9uZW50cy9zcGFya2xpbmUvX3NwYXJrbGluZS5zY3NzIiwiLi4vLi4vLi4vLi4vbm9kZV9tb2R1bGVzL0BlbGFzdGljL2V1aS9zcmMvZ2xvYmFsX3N0eWxpbmcvdmFyaWFibGVzL19ib3JkZXJzLnNjc3MiLCIuLi8uLi8uLi8uLi9ub2RlX21vZHVsZXMvQGVsYXN0aWMvZXVpL3NyYy9nbG9iYWxfc3R5bGluZy92YXJpYWJsZXMvX3pfaW5kZXguc2NzcyIsImNvbXBvbmVudHMvc3VtbWFyeV9zdGF0dXMvX3N1bW1hcnlfc3RhdHVzLnNjc3MiLCJjb21wb25lbnRzL3RhYmxlL190YWJsZS5zY3NzIiwiLi4vLi4vLi4vLi4vbm9kZV9tb2R1bGVzL0BlbGFzdGljL2V1aS9zcmMvZ2xvYmFsX3N0eWxpbmcvdmFyaWFibGVzL190eXBvZ3JhcGh5LnNjc3MiLCIuLi8uLi8uLi8uLi9ub2RlX21vZHVsZXMvQGVsYXN0aWMvZXVpL3NyYy9nbG9iYWxfc3R5bGluZy9taXhpbnMvX3R5cG9ncmFwaHkuc2NzcyIsImNvbXBvbmVudHMvbG9nc3Rhc2gvcGlwZWxpbmVfdmlld2VyL3ZpZXdzL19waXBlbGluZV92aWV3ZXIuc2NzcyIsIi4uLy4uLy4uLy4uL25vZGVfbW9kdWxlcy9AZWxhc3RpYy9ldWkvc3JjL2dsb2JhbF9zdHlsaW5nL2Z1bmN0aW9ucy9fY29sb3JzLnNjc3MiLCIuLi8uLi8uLi8uLi9ub2RlX21vZHVsZXMvQGVsYXN0aWMvZXVpL3NyYy9nbG9iYWxfc3R5bGluZy9taXhpbnMvX3Jlc3BvbnNpdmUuc2NzcyIsImRpcmVjdGl2ZXMvY2hhcnQvX2NoYXJ0LnNjc3MiLCJkaXJlY3RpdmVzL2VsYXN0aWNzZWFyY2gvc2hhcmRfYWxsb2NhdGlvbi9fc2hhcmRfYWxsb2NhdGlvbi5zY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQVdBO0VBQ0U7SUFDRSxXQUFVLEVBQUE7RUFFWjtJQUNFLFdBQVUsRUFBQSxFQUFBOztBQUlkO0VBQ0U7SUFDRSxXQUFVLEVBQUE7RUFFWjtJQUNFLFdBQVU7SUFDVixvQkFBbUIsRUFBQTtFQUVyQjtJQUNFLFdBQVU7SUFDVixvQkFBbUIsRUFBQSxFQUFBOztBQ0N2Qjs7R0FFRztBQ2pCSDtFQUNFO0lBQ0UsMkNDaEJxQixFQUFBO0VEa0J2QjtJQUNFLDZDQ25CcUIsRUFBQSxFQUFBOztBRHVCekI7RUFDRTtJQUNFLDRDQ3pCcUIsRUFBQTtFRDJCdkI7SUFDRSw2Q0M1QnFCLEVBQUEsRUFBQTs7QUNEekI7RUFDRSxvQkRhNkIsRUNaOUI7O0FBR0Q7RUFDRSxrQkNBc0QsRURDdkQ7O0FBRUQ7O0VB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 */ \ No newline at end of file