Merge branch '8.7' into risk_score_api_8.7
* 8.7: (93 commits) [8.7] [Controls] Use EUI Selectable for Field search (#151231) (#155454) [8.7] [Synthetics] Fix performance breakdown link from error details page (#155393) (#155427) [8.7] [DOCS] Remove or move book-scoped attributes (#155210) (#155426) [8.7] [Synthetics] add default email recovery message (#154862) (#155418) [8.7] [Uptime] Add both both ip filters for view host in uptime location for host and monitor (#155382) (#155399) [8.7] Setup Node.js environment before instrumenting Kibana with APM. (#155063) (#155300) [8.7] [Discover] Address react warnings for legacy table (#154579) (#155345) [8.7] [Fleet] Fix logs useless rerender (#155305) (#155310) [8.7] [kbn-failed-test-reporter-cli] truncate report message to fix github api call failure (#155141) (#155286) [8.7][APM] Fleet migration support for bundled APM package (#153159) (#155281) [8.7] [Enterprise Search] Fix Connector scheduling show week information correctly (#155191) (#155227) [8.7] [Synthetics] Fix pending count in case of location filtering (#155200) (#155225) [8.7] [Controls] Add Expensive Queries Fallback (#155082) (#155189) [8.7] [data view field editor] Runtime field code editor - move state out of controller (#155107) (#155150) [8.7] [FullStory] Update snippet (#153570) (#155138) [8.7] [Security Solution][Exceptions] - Fix exception operator logic when mapping conflict (#155071) (#155094) [DOCS] Adds 8.7.1 release notes (#154844) [8.7] Sync bundled packages with Package Storage (#155042) [APM] plugin description (#154811) Update api.asciidoc (#155021) ...
|
@ -176,6 +176,7 @@ enabled:
|
|||
- x-pack/test/api_integration/apis/uptime/config.ts
|
||||
- x-pack/test/api_integration/apis/watcher/config.ts
|
||||
- x-pack/test/apm_api_integration/basic/config.ts
|
||||
- x-pack/test/apm_api_integration/cloud/config.ts
|
||||
- x-pack/test/apm_api_integration/rules/config.ts
|
||||
- x-pack/test/apm_api_integration/trial/config.ts
|
||||
- x-pack/test/banners_functional/config.ts
|
||||
|
|
6
.buildkite/pipelines/artifacts_trigger.yml
Normal file
|
@ -0,0 +1,6 @@
|
|||
steps:
|
||||
- command: .buildkite/scripts/steps/artifacts/trigger.sh
|
||||
label: Trigger artifacts build
|
||||
agents:
|
||||
queue: kibana-default
|
||||
timeout_in_minutes: 10
|
|
@ -23,9 +23,16 @@ else
|
|||
WORKFLOW="snapshot"
|
||||
fi
|
||||
|
||||
ARTIFACTS_SUBDOMAIN="artifacts-$WORKFLOW"
|
||||
ARTIFACTS_MANIFEST_FQDN="https://$ARTIFACTS_SUBDOMAIN.elastic.co"
|
||||
KIBANA_MANIFEST_LATEST="$ARTIFACTS_MANIFEST_FQDN/kibana/latest/$FULL_VERSION.json"
|
||||
BEATS_MANIFEST_LATEST="$ARTIFACTS_MANIFEST_FQDN/beats/latest/$FULL_VERSION.json"
|
||||
|
||||
export VERSION_QUALIFIER
|
||||
export BASE_VERSION
|
||||
export QUALIFIER_VERSION
|
||||
export FULL_VERSION
|
||||
export BUILD_ARGS
|
||||
export WORKFLOW
|
||||
export KIBANA_MANIFEST_LATEST
|
||||
export BEATS_MANIFEST_LATEST
|
||||
|
|
|
@ -79,13 +79,12 @@ if [[ "$BUILDKITE_BRANCH" == "$KIBANA_BASE_BRANCH" ]]; then
|
|||
--dependency "beats:$BEATS_MANIFEST_URL" \
|
||||
--artifact-set main
|
||||
|
||||
ARTIFACTS_SUBDOMAIN="artifacts-$WORKFLOW"
|
||||
ARTIFACTS_SUMMARY=$(curl -s "https://$ARTIFACTS_SUBDOMAIN.elastic.co/kibana/latest/$FULL_VERSION.json" | jq -re '.summary_url')
|
||||
KIBANA_SUMMARY=$(curl -s "$KIBANA_MANIFEST_LATEST" | jq -re '.summary_url')
|
||||
|
||||
cat << EOF | buildkite-agent annotate --style "info" --context artifacts-summary
|
||||
### Artifacts Summary
|
||||
|
||||
$ARTIFACTS_SUMMARY
|
||||
$KIBANA_SUMMARY
|
||||
EOF
|
||||
|
||||
else
|
||||
|
|
20
.buildkite/scripts/steps/artifacts/trigger.sh
Executable file
|
@ -0,0 +1,20 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
source .buildkite/scripts/steps/artifacts/env.sh
|
||||
|
||||
BEATS_MANIFEST_LATEST_URL=$(curl "$BEATS_MANIFEST_LATEST" | jq -r '.manifest_url')
|
||||
KIBANA_MANIFEST_URL=$(curl "$KIBANA_MANIFEST_LATEST" | jq -r '.manifest_url')
|
||||
KIBANA_BEATS_MANIFEST_URL=$(curl $KIBANA_MANIFEST_URL | jq -r '.projects.kibana.dependencies[] | select(.prefix == "beats") | .build_uri')
|
||||
|
||||
echo "--- Trigger artifact builds"
|
||||
if [ "$BEATS_MANIFEST_LATEST_URL" = "$KIBANA_BEATS_MANIFEST_URL" ]; then
|
||||
echo "Kibana has the latest version of beats, skipping trigger"
|
||||
else
|
||||
# Staging builds are not necessary on main
|
||||
if [[ "$BUILDKITE_BRANCH" != "main" ]]; then
|
||||
ts-node .buildkite/scripts/steps/trigger_pipeline.ts kibana-artifacts-staging "$BUILDKITE_BRANCH"
|
||||
fi
|
||||
ts-node .buildkite/scripts/steps/trigger_pipeline.ts kibana-artifacts-snapshot "$BUILDKITE_BRANCH"
|
||||
fi
|
|
@ -257,8 +257,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|||
THE SOFTWARE.
|
||||
|
||||
---
|
||||
This code is part of the Services provided by FullStory, Inc. For license information, please refer to https://www.fullstory.com/legal/terms-and-conditions/.
|
||||
Portions of this code are licensed separately and can be found in https://edge.fullstory.com/s/fs.js.LICENSE.txt
|
||||
This code is part of the Services provided by FullStory, Inc. For license information, please refer to https://www.fullstory.com/legal/terms-and-conditions/
|
||||
Portions of this code are licensed under the following license:
|
||||
For license information please see fs.js.LICENSE.txt
|
||||
|
||||
---
|
||||
This product bundles bootstrap@3.3.6 which is available under a
|
||||
|
|
|
@ -8,8 +8,9 @@
|
|||
:issue: https://github.com/elastic/kibana/issues/
|
||||
:pull: https://github.com/elastic/kibana/pull/
|
||||
|
||||
Review important information about the {kib} 8.7.0 release.
|
||||
Review important information about the {kib} 8.7.x releases.
|
||||
|
||||
* <<release-notes-8.7.1>>
|
||||
* <<release-notes-8.7.0>>
|
||||
//* <<release-notes-8.6.1>>
|
||||
//* <<release-notes-8.6.0>>
|
||||
|
@ -40,14 +41,101 @@ Review important information about the {kib} 8.7.0 release.
|
|||
//* <<release-notes-8.0.0-alpha2>>
|
||||
//* <<release-notes-8.0.0-alpha1>>
|
||||
--
|
||||
[[release-notes-8.7.1]]
|
||||
== {kib} 8.7.1
|
||||
|
||||
coming::[8.7.1]
|
||||
|
||||
Review the following information about the {kib} 8.7.1 release.
|
||||
|
||||
[float]
|
||||
[[breaking-changes-8.7.1]]
|
||||
=== Breaking changes
|
||||
|
||||
Breaking changes can prevent your application from optimal operation and performance.
|
||||
Before you upgrade to 8.7.1, review the breaking changes, then mitigate the impact to your application.
|
||||
|
||||
// tag::notable-breaking-changes[]
|
||||
There are no breaking changes in the {kib} 8.7.1 release.
|
||||
// end::notable-breaking-changes[]
|
||||
|
||||
To review the breaking changes in the previous release, check {kibana-ref-all}/8.7/release-notes-8.7.0.html#breaking-changes-8.7.0[8.7.0].
|
||||
|
||||
[float]
|
||||
[[enhancement-v8.7.1]]
|
||||
=== Enhancement
|
||||
Fleet::
|
||||
The agent policy "Host name format" selector is now enabled by default {kibana-pull}154563[#154563]
|
||||
|
||||
[float]
|
||||
[[fixes-v8.7.1]]
|
||||
=== Bug fixes
|
||||
APM::
|
||||
* Scoring is now applied by ES {kibana-pull}154627[#154627]
|
||||
* Fixes the APM Java Agent download link {kibana-pull}154023[#154023]
|
||||
* Improves the overflow message text {kibana-pull}153676[#153676]
|
||||
|
||||
Canvas::
|
||||
* Disables the Edit in Lens action for the legacy savedVisualization function {kibana-pull}154656[#154656]
|
||||
* Fixes the home page redirect loop {kibana-pull}154568[#154568]
|
||||
* Fixes an issue where the image upload component was unable to load for image elements {kibana-pull}154385[#154385]
|
||||
|
||||
Dashboard::
|
||||
Improves controls flyout performance for data views with a large number of fields {kibana-pull}154004[#154004]
|
||||
|
||||
Discover::
|
||||
Fixes aborted request handling in the saved search embeddable {kibana-pull}153822[#153822]
|
||||
|
||||
Elastic Security::
|
||||
For the Elastic Security 8.7.1 release information, refer to {security-guide}/release-notes.html[_Elastic Security Solution Release Notes_].
|
||||
|
||||
Fleet::
|
||||
* Fixes an issue where the Advanced options toggle in the policy editor was always showing {kibana-pull}154612[#154612]
|
||||
* Fixes an issue where the warning icon was unable to display in 8.7 {kibana-pull}154119[#154119]
|
||||
* Adds updates to output logic {kibana-pull}153226[#153226]
|
||||
|
||||
Infrastructure::
|
||||
Fixes the inventory table pagination navigation {kibana-pull}153849[#153849]
|
||||
|
||||
Lens & Visualizations::
|
||||
Fixes the timezone that *Lens* uses in normalize by unit {kibana-pull}154472[#154472]
|
||||
|
||||
Machine Learning::
|
||||
* Change point detection: Fixes applied filters and queries to the charts {kibana-pull}154707[#154707]
|
||||
* Change point detection: Fixes support for running over relative time range {kibana-pull}154313[#154313]
|
||||
* Reinstates cold and frozen tier filters for Linux and Windows security modules {kibana-pull}153222[#153222]
|
||||
|
||||
Maps::
|
||||
Fixes an issue where geographic filters were unable to work when courier:ignoreFilterIfFieldNotInIndex was enabled {kibana-pull}153816[#153816]
|
||||
|
||||
Monitoring::
|
||||
Fixes the CCR read_exceptions alert {kibana-pull}153888[#153888]
|
||||
|
||||
Querying & Filtering::
|
||||
Fixes the ability to copy and paste the comma delimeter for multifields {kibana-pull}153772[#153772]
|
||||
|
||||
[[release-notes-8.7.0]]
|
||||
== {kib} 8.7.0
|
||||
|
||||
coming::[8.7.0]
|
||||
|
||||
Review the following information about the {kib} 8.7.0 release.
|
||||
|
||||
[float]
|
||||
[[known-issues-8.7.0]]
|
||||
=== Known issues
|
||||
|
||||
// tag::known-issue-151698[]
|
||||
[discrete]
|
||||
.Observability Overview shows empty User Experience panel
|
||||
[%collapsible]
|
||||
====
|
||||
*Details* +
|
||||
Release 8.7.0 has a bug causing the Observability Overview page to show an empty User Experience panel, even when there is RUM data (fixed in {kibana-pull}154419[#154419]).
|
||||
|
||||
*Impact* +
|
||||
While the User Experience panel on the Observability Overview page is empty, any RUM data will still be available from the User Experience Dashboard.
|
||||
====
|
||||
// end::known-issue-151698[]
|
||||
|
||||
[float]
|
||||
[[breaking-changes-8.7.0]]
|
||||
=== Breaking changes
|
||||
|
@ -187,7 +275,8 @@ Machine Learning::
|
|||
* Anomaly Detection wizards: adds geo job wizard {kibana-pull}147043[#147043]
|
||||
|
||||
Management::
|
||||
Adds field statistics popovers for Data Frame Analytics & Transform creation wizards {kibana-pull}149879[#149879]
|
||||
* Adds field statistics popovers for Data Frame Analytics & Transform creation wizards {kibana-pull}149879[#149879]
|
||||
* Transforms: Shows health status of transform in UI {kibana-pull}150359[#150359]
|
||||
|
||||
Monitoring::
|
||||
* Adds duration configuration to Stack Monitoring Cluster Health rule {kibana-pull}147565[#147565]
|
||||
|
@ -319,7 +408,6 @@ Machine Learning::
|
|||
* Remove beta badge for Field statistics table in Discover {kibana-pull}140991[#140991]
|
||||
|
||||
Management::
|
||||
* Transforms: Health status information in transform list {kibana-pull}150359[#150359]
|
||||
* Transforms: Adds "Use full data" button to transform creation wizard {kibana-pull}150030[#150030]
|
||||
* Adds override field to Dot expander processor form {kibana-pull}149599[#149599]
|
||||
* Adds fields to Append Ingest Pipeline processor form {kibana-pull}149520[#149520]
|
||||
|
@ -4836,4 +4924,4 @@ The 8.0.0-alpha1 release includes the following bug fix.
|
|||
|
||||
Operations::
|
||||
* Moves systemd service to /usr/lib/systemd/system {kibana-pull}83571[#83571]
|
||||
//////////////////
|
||||
//////////////////
|
||||
|
|
|
@ -98,7 +98,7 @@ Any modifications made to this file will be overwritten.
|
|||
<a href="#connector_response_properties">connector_response_properties</a>
|
||||
<h4 class="field-label">401</h4>
|
||||
Authorization information is missing or invalid.
|
||||
<a href="#401_response">401_response</a>
|
||||
<a href="#Unauthorized_response">Unauthorized_response</a>
|
||||
</div> <!-- method -->
|
||||
<hr/>
|
||||
<div class="method"><a name="deleteConnector"/>
|
||||
|
@ -146,7 +146,7 @@ Any modifications made to this file will be overwritten.
|
|||
<a href="#"></a>
|
||||
<h4 class="field-label">401</h4>
|
||||
Authorization information is missing or invalid.
|
||||
<a href="#401_response">401_response</a>
|
||||
<a href="#Unauthorized_response">Unauthorized_response</a>
|
||||
<h4 class="field-label">404</h4>
|
||||
Object is not found.
|
||||
<a href="#getConnector_404_response">getConnector_404_response</a>
|
||||
|
@ -198,7 +198,7 @@ Any modifications made to this file will be overwritten.
|
|||
<a href="#connector_response_properties">connector_response_properties</a>
|
||||
<h4 class="field-label">401</h4>
|
||||
Authorization information is missing or invalid.
|
||||
<a href="#401_response">401_response</a>
|
||||
<a href="#Unauthorized_response">Unauthorized_response</a>
|
||||
<h4 class="field-label">404</h4>
|
||||
Object is not found.
|
||||
<a href="#getConnector_404_response">getConnector_404_response</a>
|
||||
|
@ -262,7 +262,7 @@ Any modifications made to this file will be overwritten.
|
|||
|
||||
<h4 class="field-label">401</h4>
|
||||
Authorization information is missing or invalid.
|
||||
<a href="#401_response">401_response</a>
|
||||
<a href="#Unauthorized_response">Unauthorized_response</a>
|
||||
</div> <!-- method -->
|
||||
<hr/>
|
||||
<div class="method"><a name="getConnectors"/>
|
||||
|
@ -320,7 +320,7 @@ Any modifications made to this file will be overwritten.
|
|||
|
||||
<h4 class="field-label">401</h4>
|
||||
Authorization information is missing or invalid.
|
||||
<a href="#401_response">401_response</a>
|
||||
<a href="#Unauthorized_response">Unauthorized_response</a>
|
||||
</div> <!-- method -->
|
||||
<hr/>
|
||||
<div class="method"><a name="legacyCreateConnector"/>
|
||||
|
@ -394,7 +394,7 @@ Any modifications made to this file will be overwritten.
|
|||
<a href="#action_response_properties">action_response_properties</a>
|
||||
<h4 class="field-label">401</h4>
|
||||
Authorization information is missing or invalid.
|
||||
<a href="#401_response">401_response</a>
|
||||
<a href="#Unauthorized_response">Unauthorized_response</a>
|
||||
</div> <!-- method -->
|
||||
<hr/>
|
||||
<div class="method"><a name="legacyDeleteConnector"/>
|
||||
|
@ -442,7 +442,7 @@ Any modifications made to this file will be overwritten.
|
|||
<a href="#"></a>
|
||||
<h4 class="field-label">401</h4>
|
||||
Authorization information is missing or invalid.
|
||||
<a href="#401_response">401_response</a>
|
||||
<a href="#Unauthorized_response">Unauthorized_response</a>
|
||||
</div> <!-- method -->
|
||||
<hr/>
|
||||
<div class="method"><a name="legacyGetConnector"/>
|
||||
|
@ -499,7 +499,7 @@ Any modifications made to this file will be overwritten.
|
|||
<a href="#action_response_properties">action_response_properties</a>
|
||||
<h4 class="field-label">401</h4>
|
||||
Authorization information is missing or invalid.
|
||||
<a href="#401_response">401_response</a>
|
||||
<a href="#Unauthorized_response">Unauthorized_response</a>
|
||||
</div> <!-- method -->
|
||||
<hr/>
|
||||
<div class="method"><a name="legacyGetConnectorTypes"/>
|
||||
|
@ -553,7 +553,7 @@ Any modifications made to this file will be overwritten.
|
|||
|
||||
<h4 class="field-label">401</h4>
|
||||
Authorization information is missing or invalid.
|
||||
<a href="#401_response">401_response</a>
|
||||
<a href="#Unauthorized_response">Unauthorized_response</a>
|
||||
</div> <!-- method -->
|
||||
<hr/>
|
||||
<div class="method"><a name="legacyGetConnectors"/>
|
||||
|
@ -608,7 +608,7 @@ Any modifications made to this file will be overwritten.
|
|||
|
||||
<h4 class="field-label">401</h4>
|
||||
Authorization information is missing or invalid.
|
||||
<a href="#401_response">401_response</a>
|
||||
<a href="#Unauthorized_response">Unauthorized_response</a>
|
||||
</div> <!-- method -->
|
||||
<hr/>
|
||||
<div class="method"><a name="legacyRunConnector"/>
|
||||
|
@ -679,7 +679,7 @@ Any modifications made to this file will be overwritten.
|
|||
<a href="#legacyRunConnector_200_response">legacyRunConnector_200_response</a>
|
||||
<h4 class="field-label">401</h4>
|
||||
Authorization information is missing or invalid.
|
||||
<a href="#401_response">401_response</a>
|
||||
<a href="#Unauthorized_response">Unauthorized_response</a>
|
||||
</div> <!-- method -->
|
||||
<hr/>
|
||||
<div class="method"><a name="legacyUpdateConnector"/>
|
||||
|
@ -755,7 +755,7 @@ Any modifications made to this file will be overwritten.
|
|||
<a href="#action_response_properties">action_response_properties</a>
|
||||
<h4 class="field-label">404</h4>
|
||||
Object is not found.
|
||||
<a href="#404_response">404_response</a>
|
||||
<a href="#Not_found_response">Not_found_response</a>
|
||||
</div> <!-- method -->
|
||||
<hr/>
|
||||
<div class="method"><a name="runConnector"/>
|
||||
|
@ -826,7 +826,7 @@ Any modifications made to this file will be overwritten.
|
|||
<a href="#runConnector_200_response">runConnector_200_response</a>
|
||||
<h4 class="field-label">401</h4>
|
||||
Authorization information is missing or invalid.
|
||||
<a href="#401_response">401_response</a>
|
||||
<a href="#Unauthorized_response">Unauthorized_response</a>
|
||||
</div> <!-- method -->
|
||||
<hr/>
|
||||
<div class="method"><a name="updateConnector"/>
|
||||
|
@ -897,10 +897,10 @@ Any modifications made to this file will be overwritten.
|
|||
<a href="#updateConnector_400_response">updateConnector_400_response</a>
|
||||
<h4 class="field-label">401</h4>
|
||||
Authorization information is missing or invalid.
|
||||
<a href="#401_response">401_response</a>
|
||||
<a href="#Unauthorized_response">Unauthorized_response</a>
|
||||
<h4 class="field-label">404</h4>
|
||||
Object is not found.
|
||||
<a href="#404_response">404_response</a>
|
||||
<a href="#Not_found_response">Not_found_response</a>
|
||||
</div> <!-- method -->
|
||||
<hr/>
|
||||
|
||||
|
@ -909,8 +909,6 @@ Any modifications made to this file will be overwritten.
|
|||
|
||||
<h3>Table of Contents</h3>
|
||||
<ol>
|
||||
<li><a href="#401_response"><code>401_response</code> - Unsuccessful rule API response</a></li>
|
||||
<li><a href="#404_response"><code>404_response</code> - </a></li>
|
||||
<li><a href="#Alert_identifier_mapping"><code>Alert_identifier_mapping</code> - Alert identifier mapping</a></li>
|
||||
<li><a href="#Case_comment_mapping"><code>Case_comment_mapping</code> - Case comment mapping</a></li>
|
||||
<li><a href="#Case_description_mapping"><code>Case_description_mapping</code> - Case description mapping</a></li>
|
||||
|
@ -924,11 +922,13 @@ Any modifications made to this file will be overwritten.
|
|||
<li><a href="#Legacy_get_connector_types_response_body_properties_inner"><code>Legacy_get_connector_types_response_body_properties_inner</code> - </a></li>
|
||||
<li><a href="#Legacy_run_connector_request_body_properties"><code>Legacy_run_connector_request_body_properties</code> - Legacy run connector request body properties</a></li>
|
||||
<li><a href="#Legacy_update_connector_request_body_properties"><code>Legacy_update_connector_request_body_properties</code> - Legacy update connector request body properties</a></li>
|
||||
<li><a href="#Not_found_response"><code>Not_found_response</code> - Not found response</a></li>
|
||||
<li><a href="#Rule_name_mapping"><code>Rule_name_mapping</code> - Rule name mapping</a></li>
|
||||
<li><a href="#Run_connector_request_body_properties"><code>Run_connector_request_body_properties</code> - Run connector request body properties</a></li>
|
||||
<li><a href="#Run_connector_request_body_properties_params"><code>Run_connector_request_body_properties_params</code> - </a></li>
|
||||
<li><a href="#Severity_mapping"><code>Severity_mapping</code> - Severity mapping</a></li>
|
||||
<li><a href="#Subaction_parameters"><code>Subaction_parameters</code> - Subaction parameters</a></li>
|
||||
<li><a href="#Unauthorized_response"><code>Unauthorized_response</code> - Unauthorized response</a></li>
|
||||
<li><a href="#Update_connector_request_body_properties"><code>Update_connector_request_body_properties</code> - Update connector request body properties</a></li>
|
||||
<li><a href="#action_response_properties"><code>action_response_properties</code> - Action response properties</a></li>
|
||||
<li><a href="#config_properties_cases_webhook"><code>config_properties_cases_webhook</code> - Connector request properties for Webhook - Case Management connector</a></li>
|
||||
|
@ -1028,32 +1028,6 @@ Any modifications made to this file will be overwritten.
|
|||
<li><a href="#update_connector_request_swimlane"><code>update_connector_request_swimlane</code> - Update Swimlane connector request</a></li>
|
||||
</ol>
|
||||
|
||||
<div class="model">
|
||||
<h3><a name="401_response"><code>401_response</code> - Unsuccessful rule API response</a> <a class="up" href="#__Models">Up</a></h3>
|
||||
<div class='model-description'></div>
|
||||
<div class="field-items">
|
||||
<div class="param">error (optional)</div><div class="param-desc"><span class="param-type"><a href="#string">String</a></span> </div>
|
||||
<div class="param-enum-header">Enum:</div>
|
||||
<div class="param-enum">Unauthorized</div>
|
||||
<div class="param">message (optional)</div><div class="param-desc"><span class="param-type"><a href="#string">String</a></span> </div>
|
||||
<div class="param">statusCode (optional)</div><div class="param-desc"><span class="param-type"><a href="#integer">Integer</a></span> </div>
|
||||
<div class="param-enum-header">Enum:</div>
|
||||
<div class="param-enum">401</div>
|
||||
</div> <!-- field-items -->
|
||||
</div>
|
||||
<div class="model">
|
||||
<h3><a name="404_response"><code>404_response</code> - </a> <a class="up" href="#__Models">Up</a></h3>
|
||||
<div class='model-description'></div>
|
||||
<div class="field-items">
|
||||
<div class="param">error (optional)</div><div class="param-desc"><span class="param-type"><a href="#string">String</a></span> </div>
|
||||
<div class="param-enum-header">Enum:</div>
|
||||
<div class="param-enum">Not Found</div>
|
||||
<div class="param">message (optional)</div><div class="param-desc"><span class="param-type"><a href="#string">String</a></span> </div>
|
||||
<div class="param">statusCode (optional)</div><div class="param-desc"><span class="param-type"><a href="#integer">Integer</a></span> </div>
|
||||
<div class="param-enum-header">Enum:</div>
|
||||
<div class="param-enum">404</div>
|
||||
</div> <!-- field-items -->
|
||||
</div>
|
||||
<div class="model">
|
||||
<h3><a name="Alert_identifier_mapping"><code>Alert_identifier_mapping</code> - Alert identifier mapping</a> <a class="up" href="#__Models">Up</a></h3>
|
||||
<div class='model-description'>Mapping for the alert ID.</div>
|
||||
|
@ -1194,6 +1168,19 @@ Any modifications made to this file will be overwritten.
|
|||
<div class="param">secrets (optional)</div><div class="param-desc"><span class="param-type"><a href="#">Object</a></span> The updated secrets configuration for the connector. Secrets properties vary depending on the connector type. </div>
|
||||
</div> <!-- field-items -->
|
||||
</div>
|
||||
<div class="model">
|
||||
<h3><a name="Not_found_response"><code>Not_found_response</code> - Not found response</a> <a class="up" href="#__Models">Up</a></h3>
|
||||
<div class='model-description'></div>
|
||||
<div class="field-items">
|
||||
<div class="param">error (optional)</div><div class="param-desc"><span class="param-type"><a href="#string">String</a></span> </div>
|
||||
<div class="param-enum-header">Enum:</div>
|
||||
<div class="param-enum">Not Found</div>
|
||||
<div class="param">message (optional)</div><div class="param-desc"><span class="param-type"><a href="#string">String</a></span> </div>
|
||||
<div class="param">statusCode (optional)</div><div class="param-desc"><span class="param-type"><a href="#integer">Integer</a></span> </div>
|
||||
<div class="param-enum-header">Enum:</div>
|
||||
<div class="param-enum">404</div>
|
||||
</div> <!-- field-items -->
|
||||
</div>
|
||||
<div class="model">
|
||||
<h3><a name="Rule_name_mapping"><code>Rule_name_mapping</code> - Rule name mapping</a> <a class="up" href="#__Models">Up</a></h3>
|
||||
<div class='model-description'>Mapping for the name of the alert's rule.</div>
|
||||
|
@ -1246,6 +1233,19 @@ Any modifications made to this file will be overwritten.
|
|||
<div class="param">subActionParams </div><div class="param-desc"><span class="param-type"><a href="#run_connector_subaction_pushtoservice_subActionParams">run_connector_subaction_pushtoservice_subActionParams</a></span> </div>
|
||||
</div> <!-- field-items -->
|
||||
</div>
|
||||
<div class="model">
|
||||
<h3><a name="Unauthorized_response"><code>Unauthorized_response</code> - Unauthorized response</a> <a class="up" href="#__Models">Up</a></h3>
|
||||
<div class='model-description'></div>
|
||||
<div class="field-items">
|
||||
<div class="param">error (optional)</div><div class="param-desc"><span class="param-type"><a href="#string">String</a></span> </div>
|
||||
<div class="param-enum-header">Enum:</div>
|
||||
<div class="param-enum">Unauthorized</div>
|
||||
<div class="param">message (optional)</div><div class="param-desc"><span class="param-type"><a href="#string">String</a></span> </div>
|
||||
<div class="param">statusCode (optional)</div><div class="param-desc"><span class="param-type"><a href="#integer">Integer</a></span> </div>
|
||||
<div class="param-enum-header">Enum:</div>
|
||||
<div class="param-enum">401</div>
|
||||
</div> <!-- field-items -->
|
||||
</div>
|
||||
<div class="model">
|
||||
<h3><a name="Update_connector_request_body_properties"><code>Update_connector_request_body_properties</code> - Update connector request body properties</a> <a class="up" href="#__Models">Up</a></h3>
|
||||
<div class='model-description'>The properties vary depending on the connector type.</div>
|
||||
|
|
|
@ -62,7 +62,7 @@ Update a title of the `<my-view>` data view:
|
|||
|
||||
[source,sh]
|
||||
--------------------------------------------------
|
||||
$ curl -X POST api/data_views/data-view/my-view
|
||||
$ curl -X POST api/data_views/data_view/my-view
|
||||
{
|
||||
"data_view": {
|
||||
"title": "some-other-view-*"
|
||||
|
@ -75,7 +75,7 @@ Customize the update behavior:
|
|||
|
||||
[source,sh]
|
||||
--------------------------------------------------
|
||||
$ curl -X POST api/data_views/data-view/my-view
|
||||
$ curl -X POST api/data_views/data_view/my-view
|
||||
{
|
||||
"refresh_fields": true,
|
||||
"data_view": {
|
||||
|
@ -90,7 +90,7 @@ All update fields are optional, but you can specify the following fields:
|
|||
|
||||
[source,sh]
|
||||
--------------------------------------------------
|
||||
$ curl -X POST api/data_views/data-view/my-view
|
||||
$ curl -X POST api/data_views/data_view/my-view
|
||||
{
|
||||
"data_view": {
|
||||
"title": "...",
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
<titleabbrev>Configure APM agents with central config</titleabbrev>
|
||||
++++
|
||||
|
||||
APM Agent configuration allows you to fine-tune your agent configuration from within the APM app.
|
||||
APM Agent configuration allows you to fine-tune your APM agent configuration from within the APM app.
|
||||
Changes are automatically propagated to your APM agents, so there's no need to redeploy.
|
||||
|
||||
To get started, choose the services and environments you wish to configure.
|
||||
The APM app will let you know when your agents have applied your configurations.
|
||||
The APM app will let you know when your APM agents have applied your configurations.
|
||||
|
||||
[role="screenshot"]
|
||||
image::apm/images/apm-agent-configuration.png[APM Agent configuration in Kibana]
|
||||
|
@ -18,28 +18,39 @@ image::apm/images/apm-agent-configuration.png[APM Agent configuration in Kibana]
|
|||
[float]
|
||||
==== Precedence
|
||||
|
||||
Configurations set from the APM app take precedence over configurations set locally in each Agent.
|
||||
Configurations set from the APM app take precedence over configurations set locally in each APM agent.
|
||||
However, if APM Server is slow to respond, is offline, reports an error, etc.,
|
||||
APM agents will use local defaults until they're able to update the configuration.
|
||||
For this reason, it is still essential to set custom default configurations locally in each of your agents.
|
||||
For this reason, it is still essential to set custom default configurations locally in each of your APM agents.
|
||||
|
||||
[float]
|
||||
==== Supported configurations
|
||||
|
||||
Each Agent has a list of supported configurations.
|
||||
Each APM agent has a list of supported configurations.
|
||||
After selecting a Service name and environment in the APM app,
|
||||
a list of all supported configuration options,
|
||||
including descriptions and default values, will be displayed.
|
||||
|
||||
Supported configurations are also tagged with the image:./images/dynamic-config.svg[] badge in each Agent's configuration reference:
|
||||
Supported configurations are also tagged with the image:./images/dynamic-config.svg[] badge in each APM agent's configuration reference:
|
||||
|
||||
[horizontal]
|
||||
Go Agent:: {apm-go-ref}/configuration.html[Configuration reference]
|
||||
Android agent:: {apm-android-ref}/configuration.html[Configuration reference]
|
||||
Go agent:: {apm-go-ref}/configuration.html[Configuration reference]
|
||||
iOS agent:: _Not yet supported_
|
||||
Java Agent:: {apm-java-ref}/configuration.html[Configuration reference]
|
||||
.NET Agent:: {apm-dotnet-ref}/configuration.html[Configuration reference]
|
||||
Node.js Agent:: {apm-node-ref}/configuration.html[Configuration reference]
|
||||
PHP Agent:: {apm-php-ref}/configuration.html[Configuration reference]
|
||||
Python Agent:: {apm-py-ref}/configuration.html[Configuration reference]
|
||||
Ruby Agent:: {apm-ruby-ref}/configuration.html[Configuration reference]
|
||||
Real User Monitoring (RUM) Agent:: {apm-rum-ref}/configuration.html[Configuration reference]
|
||||
Java agent:: {apm-java-ref}/configuration.html[Configuration reference]
|
||||
.NET agent:: {apm-dotnet-ref}/configuration.html[Configuration reference]
|
||||
Node.js agent:: {apm-node-ref}/configuration.html[Configuration reference]
|
||||
PHP agent:: {apm-php-ref}/configuration.html[Configuration reference]
|
||||
Python agent:: {apm-py-ref}/configuration.html[Configuration reference]
|
||||
Ruby agent:: {apm-ruby-ref}/configuration.html[Configuration reference]
|
||||
Real User Monitoring (RUM) agent:: {apm-rum-ref}/configuration.html[Configuration reference]
|
||||
|
||||
[float]
|
||||
==== APM Server configuration
|
||||
|
||||
For most users, APM agent configuration should work out-of-the-box.
|
||||
If you run into trouble, it may be because you're not using the {es} output,
|
||||
or because your {es} credentials don't have sufficient privileges.
|
||||
|
||||
See {apm-guide-ref}/configure-agent-config.html[configure APM agent configuration]
|
||||
to learn how to configure APM Server to avoid these problems.
|
||||
|
|
|
@ -87,15 +87,15 @@ which you can use to automate certain aspects of configuring and deploying Kiban
|
|||
[[agent-config-api]]
|
||||
=== Agent Configuration API
|
||||
|
||||
The Agent configuration API allows you to fine-tune your APM agent configuration,
|
||||
The APM agent configuration API allows you to fine-tune your APM agent configuration,
|
||||
without needing to redeploy your application.
|
||||
|
||||
The following Agent configuration APIs are available:
|
||||
The following APM agent configuration APIs are available:
|
||||
|
||||
* <<apm-update-config>> to create or update an Agent configuration
|
||||
* <<apm-delete-config>> to delete an Agent configuration.
|
||||
* <<apm-list-config>> to list all Agent configurations.
|
||||
* <<apm-search-config>> to search for an Agent configuration.
|
||||
* <<apm-update-config>> to create or update an APM agent configuration
|
||||
* <<apm-delete-config>> to delete an APM agent configuration.
|
||||
* <<apm-list-config>> to list all APM agent configurations.
|
||||
* <<apm-search-config>> to search for an APM agent configuration.
|
||||
|
||||
[float]
|
||||
[[use-agent-config-api]]
|
||||
|
@ -307,7 +307,7 @@ GET /api/apm/settings/agent-configuration
|
|||
======
|
||||
|
||||
`etag`::
|
||||
(required) etag is sent by the agent to indicate the etag of the last successfully applied configuration. If the etag matches an existing configuration its `applied_by_agent` property will be set to `true`. Every time a configuration is edited `applied_by_agent` is reset to `false`.
|
||||
(required) etag is sent by the APM agent to indicate the etag of the last successfully applied configuration. If the etag matches an existing configuration its `applied_by_agent` property will be set to `true`. Every time a configuration is edited `applied_by_agent` is reset to `false`.
|
||||
|
||||
[[apm-search-config-body]]
|
||||
===== Response body
|
||||
|
@ -719,11 +719,11 @@ curl -X DELETE "http://localhost:5601/api/apm/sourcemaps/apm:foo-1.0.0-644fd5a9"
|
|||
[[agent-key-api]]
|
||||
=== APM agent Key API
|
||||
|
||||
The Agent Key API allows you to configure agent keys to authorize requests from APM agents to the APM Server.
|
||||
The APM agent Key API allows you to configure APM agent keys to authorize requests from APM agents to the APM Server.
|
||||
|
||||
The following Agent key APIs are available:
|
||||
The following APM agent key APIs are available:
|
||||
|
||||
* <<apm-create-agent-key>> to create an agent key
|
||||
* <<apm-create-agent-key>> to create an APM agent key
|
||||
|
||||
[float]
|
||||
[[use-agent-key-api]]
|
||||
|
@ -778,13 +778,13 @@ POST /_security/role/apm_agent_key_user
|
|||
===== Request body
|
||||
|
||||
`name`::
|
||||
(required, string) Name of the agent key.
|
||||
(required, string) Name of the APM agent key.
|
||||
|
||||
`privileges`::
|
||||
(required, array) APM agent key privileges. It can take one or more of the following values:
|
||||
|
||||
- `event:write`. Required for ingesting agent events.
|
||||
- `config_agent:read`. Required for agents to read agent configuration remotely.
|
||||
- `event:write`. Required for ingesting APM agent events.
|
||||
- `config_agent:read`. Required for APM agents to read agent configuration remotely.
|
||||
|
||||
[[apm-agent-key-create-example]]
|
||||
===== Example
|
||||
|
|
|
@ -175,7 +175,7 @@ See <<apm-app-api-user>>.
|
|||
[[apm-app-central-config-manager]]
|
||||
==== Central configuration manager
|
||||
|
||||
Central configuration users need to be able to view, create, update, and delete Agent configurations.
|
||||
Central configuration users need to be able to view, create, update, and delete APM agent configurations.
|
||||
|
||||
. Create a new role, named something like `central-config-manager`, and assign the following privileges:
|
||||
+
|
||||
|
|
|
@ -20,14 +20,16 @@ Selecting an error group ID or error message brings you to the *Error group*.
|
|||
[role="screenshot"]
|
||||
image::apm/images/apm-error-group.png[APM Error group]
|
||||
|
||||
Here, you'll see the error message, culprit, and the number of occurrences over time.
|
||||
The error group details page visualizes the number of error occurrences over time and compared to a recent time range.
|
||||
This allows you to quickly determine if the error rate is changing or remaining constant.
|
||||
You'll also see the top 5 affected transactions--enabling you to quickly narrow down which transactions are most impacted
|
||||
by the selected error.
|
||||
|
||||
Further down, you'll see the Error occurrence table.
|
||||
This table shows the details of a sampled error within this group.
|
||||
Further down, you'll see an Error sample.
|
||||
The error shown is always the most recent to occur.
|
||||
The sample includes the exception message, culprit, stack trace where the error occurred,
|
||||
and additional contextual information to help debug the issue--all of which can be copied with the click of a button.
|
||||
|
||||
Each error occurrence features a breakdown of the exception, including the stack trace from when the error occurred,
|
||||
and additional contextual information to help debug the issue.
|
||||
In some cases, you might also see a Transaction sample ID.
|
||||
This feature allows you to make a connection between the errors and transactions,
|
||||
by linking you to the specific transaction where the error occurred.
|
||||
|
|
|
@ -32,8 +32,8 @@ It allows you to view only relevant data and is especially useful for separating
|
|||
By default, all environments are displayed. If there are no environment options, you'll see "not defined".
|
||||
|
||||
Service environments are defined when configuring your APM agents.
|
||||
It's vital to be consistent when naming environments in your agents.
|
||||
To learn how to configure service environments, see the specific agent documentation:
|
||||
It's vital to be consistent when naming environments in your APM agents.
|
||||
To learn how to configure service environments, see the specific APM agent documentation:
|
||||
|
||||
* *Go:* {apm-go-ref}/configuration.html#config-environment[`ELASTIC_APM_ENVIRONMENT`]
|
||||
* *iOS agent:* _Not yet supported_
|
||||
|
|
|
@ -35,6 +35,7 @@ start with:
|
|||
Notice something awry? Select a service or trace and dive deeper with:
|
||||
|
||||
* <<service-overview>>
|
||||
* <<mobile-service-overview>>
|
||||
* <<transactions>>
|
||||
* <<spans>>
|
||||
* <<errors>>
|
||||
|
@ -53,6 +54,8 @@ include::service-maps.asciidoc[]
|
|||
|
||||
include::service-overview.asciidoc[]
|
||||
|
||||
include::mobile-service.asciidoc[]
|
||||
|
||||
include::transactions.asciidoc[]
|
||||
|
||||
include::spans.asciidoc[]
|
||||
|
|
Before Width: | Height: | Size: 327 KiB After Width: | Height: | Size: 383 KiB |
Before Width: | Height: | Size: 296 KiB After Width: | Height: | Size: 460 KiB |
Before Width: | Height: | Size: 974 KiB After Width: | Height: | Size: 174 KiB |
Before Width: | Height: | Size: 456 KiB After Width: | Height: | Size: 580 KiB |
BIN
docs/apm/images/mobile-location.png
Normal file
After Width: | Height: | Size: 619 KiB |
BIN
docs/apm/images/mobile-most-used.png
Normal file
After Width: | Height: | Size: 240 KiB |
BIN
docs/apm/images/mobile-tp.png
Normal file
After Width: | Height: | Size: 130 KiB |
Before Width: | Height: | Size: 407 KiB After Width: | Height: | Size: 549 KiB |
Before Width: | Height: | Size: 307 KiB After Width: | Height: | Size: 259 KiB |
|
@ -7,7 +7,7 @@
|
|||
The APM app in {kib} allows you to monitor your software services and applications in real-time;
|
||||
visualize detailed performance information on your services,
|
||||
identify and analyze errors,
|
||||
and monitor host-level and agent-specific metrics like JVM and Go runtime metrics.
|
||||
and monitor host-level and APM agent-specific metrics like JVM and Go runtime metrics.
|
||||
|
||||
[float]
|
||||
[[apm-bottlenecks]]
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
[[metrics]]
|
||||
=== Metrics
|
||||
|
||||
The *Metrics* overview provides agent-specific metrics,
|
||||
The *Metrics* overview provides APM agent-specific metrics,
|
||||
which lets you perform more in-depth root cause analysis investigations within the APM app.
|
||||
|
||||
If you're experiencing a problem with your service, you can use this page to attempt to find the underlying cause.
|
||||
|
@ -11,7 +11,7 @@ For example, you might be able to correlate a high number of errors with a long
|
|||
[role="screenshot"]
|
||||
image::apm/images/apm-metrics.png[Example view of the Metrics overview in APM app in Kibana]
|
||||
|
||||
If you're using the Java Agent, you can view metrics for each JVM.
|
||||
If you're using the Java APM agent, you can view metrics for each JVM.
|
||||
|
||||
[role="screenshot"]
|
||||
image::apm/images/jvm-metrics-overview.png[Example view of the Metrics overview for the Java Agent]
|
||||
|
|
59
docs/apm/mobile-service.asciidoc
Normal file
|
@ -0,0 +1,59 @@
|
|||
[role="xpack"]
|
||||
[[mobile-service-overview]]
|
||||
=== Mobile service overview
|
||||
|
||||
Selecting a mobile service brings you to the *Mobile service overview*.
|
||||
The *Mobile service overview* contains a wide variety of charts and tables that provide
|
||||
high-level visibility into how a mobile service is performing for your users--enabling you
|
||||
to make data-driven decisions about how to improve your user experience.
|
||||
|
||||
For example, see:
|
||||
|
||||
* Crash Rate (Crashes per minute) -- coming soon
|
||||
* Slowest App load time -- coming soon
|
||||
* Number of sessions
|
||||
* Number of HTTP requests
|
||||
* Map showing the total number of HTTP requests based on country and region
|
||||
* Most used devices, network connection type, OS version, and app version
|
||||
* Latency, throughput, and errors over time
|
||||
* Service dependencies
|
||||
|
||||
All of these metrics & insights can help SREs and developers better understand the health
|
||||
of their mobile application environment and the impact of backend errors and bottlenecks on end-user experience.
|
||||
|
||||
[discrete]
|
||||
[[mobile-service-stats]]
|
||||
=== Quick stats
|
||||
|
||||
Understand the impact of slow application load times and variations in application crash rate on user traffic (coming soon).
|
||||
Visualize session and HTTP trends, and see where your users are located--enabling you to optimize your infrastructure deployment and routing topology.
|
||||
|
||||
[role="screenshot"]
|
||||
image::apm/images/mobile-location.png[mobile service overview centered on location map]
|
||||
|
||||
[discrete]
|
||||
[[mobile-service-most-used]]
|
||||
=== Most used
|
||||
|
||||
Optimize your end-user experience and your application QA strategy based on your most used device models and operating system versions.
|
||||
|
||||
[role="screenshot"]
|
||||
image::apm/images/mobile-most-used.png[mobile service overview showing most used devices, network, OS, and app version]
|
||||
|
||||
|
||||
[discrete]
|
||||
[[mobile-throughput-transactions]]
|
||||
=== Throughput and transactions
|
||||
|
||||
include::./service-overview.asciidoc[tag=throughput-transactions]
|
||||
|
||||
[discrete]
|
||||
[[mobile-error-and-dependencies]]
|
||||
=== Failed transaction rate and dependencies
|
||||
|
||||
include::./service-overview.asciidoc[tag=ftr]
|
||||
|
||||
include::./service-overview.asciidoc[tag=dependencies]
|
||||
|
||||
[role="screenshot"]
|
||||
image::apm/images/mobile-tp.png[mobile service overview showing latency, throughput, and errors]
|
|
@ -55,19 +55,20 @@ By default, all instrumented services and connections are shown.
|
|||
Whether you're onboarding a new engineer, or just trying to grasp the big picture,
|
||||
drag things around, zoom in and out, and begin to visualize how your services are connected.
|
||||
|
||||
Customize what the service map displays using either the query bar or the environment selector.
|
||||
The query bar enables you to use <<advanced-queries,advanced queries>> to customize the service map based on your needs.
|
||||
The environment selector allows you to narrow displayed results to a specific environment.
|
||||
This can be useful if you have two or more services, in separate environments, but with the same name.
|
||||
Use the environment drop-down to only see the data you're interested in, like `dev` or `production`.
|
||||
|
||||
If there's a specific service that interests you, select that service to highlight its connections.
|
||||
Clicking **Focus map** will refocus the map on that specific service and lock the connection highlighting.
|
||||
From here, select **Service Details**, or click on the **Transaction** tab to jump to the Transaction overview
|
||||
for the selected service.
|
||||
You can also use the tabs at the top of the page to easily jump to the **Errors** or **Metrics** overview.
|
||||
|
||||
Filter out your maps by picking the environment from the environment drop-down filter.
|
||||
This can be useful if you have two or more services, in separate environments, but with the same name.
|
||||
Use the environment drop-down to only see the data you're interested in, like `dev` or `production`.
|
||||
Additional filters are not currently available for service maps.
|
||||
|
||||
[role="screenshot"]
|
||||
image::apm/images/service-maps-java.png[Example view of service maps with Java highlighted in the APM app in Kibana]
|
||||
image::apm/images/service-maps-java.png[Example view of service maps in the APM app in Kibana]
|
||||
|
||||
[float]
|
||||
[[service-map-anomaly-detection]]
|
||||
|
@ -95,16 +96,16 @@ To learn how to create a machine learning job, see <<machine-learning-integratio
|
|||
|
||||
Nodes appear on the map in one of two shapes:
|
||||
|
||||
* **Circle**: Instrumented services. Interior icons are based on the language of the agent used.
|
||||
* **Circle**: Instrumented services. Interior icons are based on the language of the APM agent used.
|
||||
* **Diamond**: Databases, external, and messaging. Interior icons represent the generic type,
|
||||
with specific icons for known entities, like Elasticsearch.
|
||||
Type and subtype are based on `span.type`, and `span.subtype`.
|
||||
|
||||
[float]
|
||||
[[service-maps-supported]]
|
||||
=== Supported APM Agents
|
||||
=== Supported APM agents
|
||||
|
||||
Service maps are supported for the following Agent versions:
|
||||
Service maps are supported for the following APM agent versions:
|
||||
|
||||
[horizontal]
|
||||
Go agent:: ≥ v1.7.0
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
[[service-overview]]
|
||||
=== Service overview
|
||||
|
||||
Selecting a <<services,*service*>> brings you to the *Service overview*.
|
||||
Selecting a non-mobile <<services,*service*>> brings you to the *Service overview*.
|
||||
The *Service overview* contains a wide variety of charts and tables that provide
|
||||
high-level visibility into how a service is performing across your infrastructure:
|
||||
|
||||
* Service details like service version, runtime version, framework, and agent name and version
|
||||
* Service details like service version, runtime version, framework, and APM agent name and version
|
||||
* Container and orchestration information
|
||||
* Cloud provider, machine type, service name, region, and availability zone
|
||||
* Serverless function names and event trigger type
|
||||
|
@ -61,6 +61,7 @@ image::apm/images/latency.png[Service latency]
|
|||
[[service-throughput-transactions]]
|
||||
=== Throughput and transactions
|
||||
|
||||
// tag::throughput-transactions[]
|
||||
The *Throughput* chart visualizes the average number of transactions per minute for the selected service.
|
||||
|
||||
The *Transactions* table displays a list of _transaction groups_ for the
|
||||
|
@ -73,11 +74,13 @@ list of similar transactions on the <<transactions, transactions overview>> page
|
|||
|
||||
[role="screenshot"]
|
||||
image::apm/images/traffic-transactions.png[Traffic and transactions]
|
||||
// end::throughput-transactions[]
|
||||
|
||||
[discrete]
|
||||
[[service-error-rates]]
|
||||
=== Failed transaction rate and errors
|
||||
|
||||
// tag::ftr[]
|
||||
The failed transaction rate represents the percentage of failed transactions from the perspective of the selected service.
|
||||
It's useful for visualizing unexpected increases, decreases, or irregular patterns in a service's transactions.
|
||||
|
||||
|
@ -91,6 +94,7 @@ These spans will set `event.outcome=failure` and increase the failed transaction
|
|||
|
||||
If there is no HTTP status, both transactions and spans are considered successful unless an error is reported.
|
||||
====
|
||||
// end::ftr[]
|
||||
|
||||
The *Errors* table provides a high-level view of each error message when it first and last occurred,
|
||||
along with the total number of occurrences. This makes it very easy to quickly see which errors affect
|
||||
|
@ -105,10 +109,11 @@ image::apm/images/error-rate.png[failed transaction rate and errors]
|
|||
|
||||
The *Time spent by span type* chart visualizes each span type's average duration and helps you determine
|
||||
which spans could be slowing down transactions. The "app" label displayed under the
|
||||
chart indicates that something was happening within the application. This could signal that the
|
||||
chart indicates that something was happening within the application. This could signal that the APM
|
||||
agent does not have auto-instrumentation for whatever was happening during that time or that the time was spent in the
|
||||
application code and not in database or external requests.
|
||||
|
||||
// tag::dependencies[]
|
||||
The *Dependencies* table displays a list of downstream services or external connections relevant
|
||||
to the service at the selected time range. The table displays latency, throughput, failed transaction rate, and the impact of
|
||||
each dependency. By default, dependencies are sorted by _Impact_ to show the most used and the slowest dependency.
|
||||
|
@ -119,14 +124,20 @@ requires an agent version ≥ v5.6.3.
|
|||
|
||||
[role="screenshot"]
|
||||
image::apm/images/spans-dependencies.png[Span type duration and dependencies]
|
||||
// end::dependencies[]
|
||||
|
||||
[discrete]
|
||||
[[service-cold-start]]
|
||||
=== Cold start rate
|
||||
|
||||
The cold start rate chart is specific to serverless services.
|
||||
It displays the percentage of requests that trigger a cold start of a serverless function.
|
||||
See <<apm-lambda-cold-start-info>> for more information.
|
||||
The cold start rate chart is specific to serverless services, and displays the
|
||||
percentage of requests that trigger a cold start of a serverless function.
|
||||
A cold start occurs when a serverless function has not been used for a certain period of time.
|
||||
Analyzing the cold start rate can be useful for deciding how much memory to allocate to a function,
|
||||
or when to remove a large dependency.
|
||||
|
||||
The cold start rate chart is currently supported for <<apm-lambda-cold-start-info,AWS Lambda>>
|
||||
functions and Azure functions.
|
||||
|
||||
[role="screenshot"]
|
||||
image::apm/images/lambda-cold-start.png[lambda cold start graph]
|
||||
|
@ -157,7 +168,7 @@ image::apm/images/metadata-icons.png[Service metadata]
|
|||
* Service version
|
||||
* Runtime name and version
|
||||
* Framework name
|
||||
* Agent name and version
|
||||
* APM agent name and version
|
||||
|
||||
*Container information*
|
||||
|
||||
|
|
|
@ -8,11 +8,7 @@
|
|||
|
||||
APM is available via the navigation sidebar in {Kib}.
|
||||
If you have not already installed and configured Elastic APM,
|
||||
follow the three steps on the *Add data* page to get started:
|
||||
|
||||
. Start APM Server
|
||||
. Add APM agents
|
||||
. Load Kibana objects
|
||||
follow the steps on the *Add data* page to get started.
|
||||
|
||||
[role="screenshot"]
|
||||
image::apm/images/apm-setup.png[Installation instructions on the APM page in Kibana]
|
||||
|
|
|
@ -23,6 +23,22 @@ Each span has a type and is defined by a different color in the timeline/waterfa
|
|||
[role="screenshot"]
|
||||
image::apm/images/apm-span-detail.png[Example view of a span detail in the APM app in Kibana]
|
||||
|
||||
[float]
|
||||
[[trace-sample-investigate]]
|
||||
==== Investigate
|
||||
|
||||
The trace sample timeline features an **Investigate** button which provides a quick way to jump
|
||||
to other areas of the Elastic Observability UI while maintaining the context of the currently selected trace sample.
|
||||
For example, quickly view:
|
||||
|
||||
* logs and metrics for the selected pod
|
||||
* logs and metrics for the selected host
|
||||
* trace logs for the selected `trace.id`
|
||||
* uptime status of the selected domain
|
||||
* the <<service-maps,service map>> filtered by the selected trace
|
||||
* the selected transaction in <<discover,Discover>>
|
||||
* your <<custom-links,custom links>>
|
||||
|
||||
[float]
|
||||
[[distributed-tracing]]
|
||||
==== Distributed tracing
|
||||
|
|
|
@ -74,7 +74,7 @@ tune the APM agent or agents that are collecting the data.
|
|||
You can disable the collection of specific metrics with the **disable metrics** configuration.
|
||||
Or, you can set the **metrics interval** to zero seconds to deactivate metrics entirely.
|
||||
Most APM agents support both options.
|
||||
See the relevant {apm-agents-ref}[agent configuration options] for more details.
|
||||
See the relevant {apm-agents-ref}[APM agent configuration options] for more details.
|
||||
|
||||
[float]
|
||||
===== Reduce the number of errors
|
||||
|
|
|
@ -44,7 +44,7 @@ For example, is your app spending time in external calls, database processing, o
|
|||
+
|
||||
The time a transaction took to complete is also recorded and displayed on the chart under the "app" label.
|
||||
"app" indicates that something was happening within the application, but we're not sure exactly what.
|
||||
This could be a sign that the agent does not have auto-instrumentation for whatever was happening during that time.
|
||||
This could be a sign that the APM agent does not have auto-instrumentation for whatever was happening during that time.
|
||||
+
|
||||
It's important to note that if you have asynchronous spans, the sum of all span times may exceed the duration of the transaction.
|
||||
|
||||
|
@ -71,7 +71,7 @@ If there's a particular endpoint you're worried about, you can click on it to vi
|
|||
[IMPORTANT]
|
||||
====
|
||||
If you only see one route in the Transactions table, or if you have transactions named "unknown route",
|
||||
it could be a symptom that the agent either wasn't installed correctly or doesn't support your framework.
|
||||
it could be a symptom that the APM agent either wasn't installed correctly or doesn't support your framework.
|
||||
|
||||
For further details, including troubleshooting and custom implementation instructions,
|
||||
refer to the documentation for each {apm-agents-ref}[APM Agent] you've implemented.
|
||||
|
@ -81,7 +81,7 @@ refer to the documentation for each {apm-agents-ref}[APM Agent] you've implement
|
|||
[[rum-transaction-overview]]
|
||||
=== RUM Transaction overview
|
||||
|
||||
The transaction overview page is customized for the JavaScript RUM Agent.
|
||||
The transaction overview page is customized for the JavaScript RUM agent.
|
||||
Specifically, the page highlights *page load times* for your service:
|
||||
|
||||
[role="screenshot"]
|
||||
|
@ -144,13 +144,13 @@ NOTE: More information on timeline waterfalls is available in <<spans, spans>>.
|
|||
|
||||
Learn more about a trace sample in the *Metadata* tab:
|
||||
|
||||
* Labels - Custom labels added by agents
|
||||
* Labels - Custom labels added by APM agents
|
||||
* HTTP request/response information
|
||||
* Host information
|
||||
* Container information
|
||||
* Service - The service/application runtime, agent, name, etc..
|
||||
* Service - The service/application runtime, APM agent, name, etc..
|
||||
* Process - The process id that served up the request.
|
||||
* Agent information
|
||||
* APM agent information
|
||||
* URL
|
||||
* User - Requires additional configuration, but allows you to see which user experienced the current transaction.
|
||||
* FaaS information, like cold start, AWS request ID, trigger type, and trigger request ID
|
||||
|
|
|
@ -58,20 +58,20 @@ To force a rollover, use the {ref}/indices-rollover-index.html[rollover API] to
|
|||
[[troubleshooting-too-many-transactions]]
|
||||
=== Too many unique transaction names
|
||||
|
||||
Transaction names are defined in each APM Agent; when an Agent supports a framework,
|
||||
Transaction names are defined in each APM agent; when an APM agent supports a framework,
|
||||
it includes logic for naming the transactions that the framework creates.
|
||||
In some cases though, like when using an Agent's API to create custom transactions,
|
||||
In some cases though, like when using an APM agent's API to create custom transactions,
|
||||
it is up to the user to define a pattern for transaction naming.
|
||||
When transactions are named incorrectly, each unique URL can be associated with a unique transaction group—causing
|
||||
an explosion in the number of transaction groups per service, and leading to inaccuracies in the APM app.
|
||||
|
||||
To fix a large number of unique transaction names,
|
||||
you need to change how you are using the Agent API to name your transactions.
|
||||
you need to change how you are using the APM agent API to name your transactions.
|
||||
To do this, ensure you are **not** naming based on parameters that can change.
|
||||
For example, user ids, product ids, order numbers, query parameters, etc.,
|
||||
should be stripped away, and commonality should be found between your unique URLs.
|
||||
|
||||
Let's look at an example from the RUM Agent documentation. Here are a few URLs you might find on Elastic.co:
|
||||
Let's look at an example from the RUM agent documentation. Here are a few URLs you might find on Elastic.co:
|
||||
|
||||
[source,yml]
|
||||
----
|
||||
|
@ -109,14 +109,14 @@ You will see this warning if your results have more than `1000` unique transacti
|
|||
|
||||
**More information**
|
||||
|
||||
While this can happen with any APM Agent, it typically occurs with the RUM Agent.
|
||||
For more information on how to correctly set `transaction.name` in the RUM Agent,
|
||||
While this can happen with any APM agent, it typically occurs with the RUM agent.
|
||||
For more information on how to correctly set `transaction.name` in the RUM agent,
|
||||
see {apm-rum-ref}/custom-transaction-name.html[custom initial page load transaction names].
|
||||
|
||||
The RUM Agent can also set the `transaction.name` when observing for transaction events.
|
||||
The RUM agent can also set the `transaction.name` when observing for transaction events.
|
||||
See {apm-rum-ref}/agent-api.html#observe[`apm.observe()`] for more information.
|
||||
|
||||
If your problem is occurring in a different Agent, the tips above still apply.
|
||||
If your problem is occurring in a different APM agent, the tips above still apply.
|
||||
See the relevant {apm-agents-ref}[Agent API documentation] to adjust how you're naming your transactions.
|
||||
|
||||
[float]
|
||||
|
@ -128,17 +128,17 @@ when the transactions in your services are named correctly.
|
|||
If you're seeing "GET unknown route" or "unknown route" in the APM app,
|
||||
it could be a sign that something isn't working as it should.
|
||||
|
||||
Elastic APM Agents come with built-in support for popular frameworks out-of-the-box.
|
||||
This means, among other things, that the Agent will try to automatically name HTTP requests.
|
||||
As an example, the Node.js Agent uses the route that handled the request, while the Java Agent uses the Servlet name.
|
||||
Elastic APM agents come with built-in support for popular frameworks out-of-the-box.
|
||||
This means, among other things, that the APM agent will try to automatically name HTTP requests.
|
||||
As an example, the Node.js agent uses the route that handled the request, while the Java agent uses the Servlet name.
|
||||
|
||||
"Unknown route" indicates that the Agent can't determine what to name the request,
|
||||
perhaps because the technology you're using isn't supported, the Agent has been installed incorrectly,
|
||||
or because something is happening to the request that the Agent doesn't understand.
|
||||
"Unknown route" indicates that the APM agent can't determine what to name the request,
|
||||
perhaps because the technology you're using isn't supported, the agent has been installed incorrectly,
|
||||
or because something is happening to the request that the agent doesn't understand.
|
||||
|
||||
To resolve this, you'll need to head over to the relevant {apm-agents-ref}[Agent documentation].
|
||||
Specifically, view the Agent's supported technologies page.
|
||||
You can also use the Agent's public API to manually set a name for the transaction.
|
||||
To resolve this, you'll need to head over to the relevant {apm-agents-ref}[APM agent documentation].
|
||||
Specifically, view the agent's supported technologies page.
|
||||
You can also use the agent's public API to manually set a name for the transaction.
|
||||
|
||||
[float]
|
||||
[[troubleshooting-fields-unsearchable]]
|
||||
|
@ -148,7 +148,7 @@ In Elasticsearch, index templates are used to define settings and mappings that
|
|||
The recommended index templates for APM are installed by {fleet} when the Elastic APM integration is installed.
|
||||
These templates, by default, enable and disable indexing on certain fields.
|
||||
|
||||
As an example, some agents store cookie values in `http.request.cookies`.
|
||||
As an example, some APM agents store cookie values in `http.request.cookies`.
|
||||
Since `http.request` has disabled dynamic indexing, and `http.request.cookies` is not declared in a custom mapping,
|
||||
the values in `http.request.cookies` are not indexed and thus not searchable.
|
||||
|
||||
|
@ -187,5 +187,5 @@ This setting is necessary, for example, for cross-origin requests.
|
|||
If you have a basic web application that provides data via an API on `localhost:4000`,
|
||||
and serves HTML from `localhost:4001`, you'd need to set `distributedTracingOrigins: ['https://localhost:4000']`
|
||||
to ensure the origin is monitored as a part of distributed tracing.
|
||||
In other words, `distributedTracingOrigins` is consulted prior to the agent adding the
|
||||
In other words, `distributedTracingOrigins` is consulted prior to the APM agent adding the
|
||||
distributed tracing `traceparent` header to each request.
|
||||
|
|
|
@ -81,7 +81,7 @@ export TEST_BROWSER_HEADLESS=1
|
|||
export TEST_THROTTLE_NETWORK=1
|
||||
----------
|
||||
|
||||
** When running against a Cloud deployment, some tests are not applicable. To skip tests that do not apply, use --exclude-tag. An example shell file can be found at: {blob}test/scripts/jenkins_cloud.sh[test/scripts/jenkins_cloud.sh]
|
||||
** When running against a Cloud deployment, some tests are not applicable. To skip tests that do not apply, use --exclude-tag. An example shell file can be found at: {kibana-blob}test/scripts/jenkins_cloud.sh[test/scripts/jenkins_cloud.sh]
|
||||
+
|
||||
["source", "shell"]
|
||||
----------
|
||||
|
@ -118,7 +118,7 @@ The tests are written in https://mochajs.org[mocha] using https://github.com/ela
|
|||
|
||||
We use https://www.w3.org/TR/webdriver1/[WebDriver Protocol] to run tests in both Chrome and Firefox with the help of https://sites.google.com/a/chromium.org/chromedriver/[chromedriver] and https://firefox-source-docs.mozilla.org/testing/geckodriver/[geckodriver]. When the `FunctionalTestRunner` launches, remote service creates a new webdriver session, which starts the driver and a stripped-down browser instance. We use `browser` service and `webElementWrapper` class to wrap up https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/[Webdriver API].
|
||||
|
||||
The `FunctionalTestRunner` automatically transpiles functional tests using babel, so that tests can use the same ECMAScript features that {kib} source code uses. See {blob}style_guides/js_style_guide.md[style_guides/js_style_guide.md].
|
||||
The `FunctionalTestRunner` automatically transpiles functional tests using babel, so that tests can use the same ECMAScript features that {kib} source code uses. See {kibana-blob}style_guides/js_style_guide.md[style_guides/js_style_guide.md].
|
||||
|
||||
[discrete]
|
||||
==== Definitions
|
||||
|
@ -270,7 +270,7 @@ The first and only argument to all providers is a Provider API Object. This obje
|
|||
Within config files the API has the following properties
|
||||
|
||||
[horizontal]
|
||||
`log`::: An instance of the {blob}packages/kbn-dev-utils/src/tooling_log/tooling_log.js[`ToolingLog`] that is ready for use
|
||||
`log`::: An instance of the {kibana-blob}packages/kbn-dev-utils/src/tooling_log/tooling_log.js[`ToolingLog`] that is ready for use
|
||||
`readConfigFile(path)`::: Returns a promise that will resolve to a Config instance that provides the values from the config file at `path`
|
||||
|
||||
Within service and PageObject Providers the API is:
|
||||
|
@ -293,17 +293,17 @@ Within a test Provider the API is exactly the same as the service providers API
|
|||
The `FunctionalTestRunner` comes with three built-in services:
|
||||
|
||||
**config:**:::
|
||||
* Source: {blob}src/functional_test_runner/lib/config/config.ts[src/functional_test_runner/lib/config/config.ts]
|
||||
* Schema: {blob}src/functional_test_runner/lib/config/schema.ts[src/functional_test_runner/lib/config/schema.ts]
|
||||
* Source: {kibana-blob}src/functional_test_runner/lib/config/config.ts[src/functional_test_runner/lib/config/config.ts]
|
||||
* Schema: {kibana-blob}src/functional_test_runner/lib/config/schema.ts[src/functional_test_runner/lib/config/schema.ts]
|
||||
* Use `config.get(path)` to read any value from the config file
|
||||
|
||||
**log:**:::
|
||||
* Source: {blob}packages/kbn-dev-utils/src/tooling_log/tooling_log.js[packages/kbn-dev-utils/src/tooling_log/tooling_log.js]
|
||||
* Source: {kibana-blob}packages/kbn-dev-utils/src/tooling_log/tooling_log.js[packages/kbn-dev-utils/src/tooling_log/tooling_log.js]
|
||||
* `ToolingLog` instances are readable streams. The instance provided by this service is automatically piped to stdout by the `FunctionalTestRunner` CLI
|
||||
* `log.verbose()`, `log.debug()`, `log.info()`, `log.warning()` all work just like console.log but produce more organized output
|
||||
|
||||
**lifecycle:**:::
|
||||
* Source: {blob}src/functional_test_runner/lib/lifecycle.ts[src/functional_test_runner/lib/lifecycle.ts]
|
||||
* Source: {kibana-blob}src/functional_test_runner/lib/lifecycle.ts[src/functional_test_runner/lib/lifecycle.ts]
|
||||
* Designed primary for use in services
|
||||
* Exposes lifecycle events for basic coordination. Handlers can return a promise and resolve/fail asynchronously
|
||||
* Phases include: `beforeLoadTests`, `beforeTests`, `beforeEachTest`, `cleanup`
|
||||
|
@ -314,14 +314,14 @@ The `FunctionalTestRunner` comes with three built-in services:
|
|||
The {kib} functional tests define the vast majority of the actual functionality used by tests.
|
||||
|
||||
**browser**:::
|
||||
* Source: {blob}test/functional/services/browser.ts[test/functional/services/browser.ts]
|
||||
* Source: {kibana-blob}test/functional/services/browser.ts[test/functional/services/browser.ts]
|
||||
* Higher level wrapper for `remote` service, which exposes available browser actions
|
||||
* Popular methods:
|
||||
** `browser.getWindowSize()`
|
||||
** `browser.refresh()`
|
||||
|
||||
**testSubjects:**:::
|
||||
* Source: {blob}test/functional/services/test_subjects.ts[test/functional/services/test_subjects.ts]
|
||||
* Source: {kibana-blob}test/functional/services/test_subjects.ts[test/functional/services/test_subjects.ts]
|
||||
* Test subjects are elements that are tagged specifically for selecting from tests
|
||||
* Use `testSubjects` over CSS selectors when possible
|
||||
* Usage:
|
||||
|
@ -346,21 +346,21 @@ await testSubjects.click(‘containerButton’);
|
|||
** `testSubjects.click(testSubjectSelector)` - Click a test subject in the page; throw if it can't be found after some time
|
||||
|
||||
**find:**:::
|
||||
* Source: {blob}test/functional/services/find.ts[test/functional/services/find.ts]
|
||||
* Source: {kibana-blob}test/functional/services/find.ts[test/functional/services/find.ts]
|
||||
* Helpers for `remote.findBy*` methods that log and manage timeouts
|
||||
* Popular methods:
|
||||
** `find.byCssSelector()`
|
||||
** `find.allByCssSelector()`
|
||||
|
||||
**retry:**:::
|
||||
* Source: {blob}test/common/services/retry/retry.ts[test/common/services/retry/retry.ts]
|
||||
* Source: {kibana-blob}test/common/services/retry/retry.ts[test/common/services/retry/retry.ts]
|
||||
* Helpers for retrying operations
|
||||
* Popular methods:
|
||||
** `retry.try(fn, onFailureBlock)` - Execute `fn` in a loop until it succeeds or the default timeout elapses. The optional `onFailureBlock` is executed before each retry attempt.
|
||||
** `retry.tryForTime(ms, fn, onFailureBlock)` - Execute `fn` in a loop until it succeeds or `ms` milliseconds elapses. The optional `onFailureBlock` is executed before each retry attempt.
|
||||
|
||||
**kibanaServer:**:::
|
||||
* Source: {blob}test/common/services/kibana_server/kibana_server.js[test/common/services/kibana_server/kibana_server.js]
|
||||
* Source: {kibana-blob}test/common/services/kibana_server/kibana_server.js[test/common/services/kibana_server/kibana_server.js]
|
||||
* Helpers for interacting with {kib}'s server
|
||||
* Commonly used methods:
|
||||
** `kibanaServer.uiSettings.update()`
|
||||
|
@ -368,23 +368,23 @@ await testSubjects.click(‘containerButton’);
|
|||
** `kibanaServer.status.getOverallState()`
|
||||
|
||||
**esArchiver:**:::
|
||||
* Source: {blob}test/common/services/es_archiver.ts[test/common/services/es_archiver.ts]
|
||||
* Source: {kibana-blob}test/common/services/es_archiver.ts[test/common/services/es_archiver.ts]
|
||||
* Load/unload archives created with the `esArchiver`
|
||||
* Popular methods:
|
||||
** `esArchiver.load(path)`
|
||||
** `esArchiver.loadIfNeeded(path)`
|
||||
** `esArchiver.unload(path)`
|
||||
|
||||
Full list of services that are used in functional tests can be found here: {blob}test/functional/services[test/functional/services]
|
||||
Full list of services that are used in functional tests can be found here: {kibana-blob}test/functional/services[test/functional/services]
|
||||
|
||||
|
||||
**Low-level utilities:**:::
|
||||
* es
|
||||
** Source: {blob}test/common/services/es.ts[test/common/services/es.ts]
|
||||
** Source: {kibana-blob}test/common/services/es.ts[test/common/services/es.ts]
|
||||
** {es} client
|
||||
** Higher level options: `kibanaServer.uiSettings` or `esArchiver`
|
||||
* remote
|
||||
** Source: {blob}test/functional/services/remote/remote.ts[test/functional/services/remote/remote.ts]
|
||||
** Source: {kibana-blob}test/functional/services/remote/remote.ts[test/functional/services/remote/remote.ts]
|
||||
** Instance of https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebDriver.html[WebDriver] class
|
||||
** Responsible for all communication with the browser
|
||||
** To perform browser actions, use `remote` service
|
||||
|
|
|
@ -1,44 +1,34 @@
|
|||
[[interpreting-ci-failures]]
|
||||
== Interpreting CI Failures
|
||||
|
||||
{kib} CI uses a Jenkins feature called "Pipelines" to automate testing of the code in pull requests and on tracked branches. Pipelines are defined within the repository via the `Jenkinsfile` at the root of the project.
|
||||
{kib} CI uses a Buildkite feature called "Pipelines" to automate testing of the code in pull requests and on tracked branches. Pipelines are defined within the repository via the `Pipelines` at the `.buildkite/pipelines` folder.
|
||||
|
||||
More information about Jenkins Pipelines can be found link:https://jenkins.io/doc/book/pipeline/[in the Jenkins book].
|
||||
More information about Buildkite Pipelines can be found link:https://buildkite.com/docs/pipelines[in the docs].
|
||||
|
||||
[discrete]
|
||||
=== Github Checks
|
||||
|
||||
When a test fails it will be reported to Github via Github Checks. We currently bucket tests into several categories which run in parallel to make CI faster. Groups like `ciGroup{X}` get a single check in Github, and other tests like linting, or type checks, get their own checks.
|
||||
|
||||
Clicking the link next to the check in the conversation tab of a pull request will take you to the log output from that section of the tests. If that log output is truncated, or doesn't clearly identify what happened, you can usually get more complete information by visiting Jenkins directly.
|
||||
Clicking the link next to the check in the conversation tab of a pull request will take you to the log output from that section of the tests. If that log output is truncated, or doesn't clearly identify what happened, you can usually get more complete information by visiting Buildkite directly.
|
||||
|
||||
[discrete]
|
||||
=== Viewing Job Executions in Jenkins
|
||||
=== Viewing Job Executions in Kibana
|
||||
|
||||
To view the results of a job execution in Jenkins, either click the link in the comment left by `@elasticmachine` or search for the `kibana-ci` check in the list at the bottom of the PR. This link will take you to the top-level page for the specific job execution that failed.
|
||||
To view the results of a job execution in Buildkite, either click the link in the comment left by `@elasticmachine` or search for the `kibana-ci` check in the list at the bottom of the PR. This link will take you to the top-level page for the specific job execution that failed.
|
||||
|
||||
image::images/job_view.png[Jenkins job view showing a test failure]
|
||||
image::images/job_view.png[Buildkite pipeline view showing a few test failures]
|
||||
|
||||
1. *Git Changes:* the list of commits that were in this build which weren't in the previous build. For Pull Requests this list is calculated by comparing against the most recent Pull Request which was tested, it is not limited to build for this specific Pull Request, so it's not very useful.
|
||||
2. *Test Results:* A link to the test results screen, and shortcuts to the failed tests. Functional tests capture and store the log output from each specific test, and make it visible at these links. For other test runners only the error message is visible and log output must be tracked down in the *Pipeline Steps*.
|
||||
3. *Google Cloud Storage (GCS) Upload Report:* Link to the screen which lists out the artifacts uploaded to GCS during this job execution.
|
||||
4. *Pipeline Steps:*: A breakdown of the pipeline that was executed, along with individual log output for each step in the pipeline.
|
||||
|
||||
[discrete]
|
||||
=== Viewing ciGroup/test Logs
|
||||
|
||||
To view the logs for a failed specific ciGroup, jest, type checkers, linters, etc., click on the *Pipeline Steps* link in from the Job page.
|
||||
|
||||
image::images/pipeline_steps_view.png[Jenkins pipeline steps screenshot]
|
||||
|
||||
Scroll down the page until you find a failed step *(1)*, and then look up a few lines for the `Branch:` step to see which specific job this is. If this is the job you're looking for click the little terminal icon next to the failed step *(1)* to view the logs for that specific step in the Pipeline.
|
||||
1. *Git commit:* the git commit that caused this build.
|
||||
2. *Test Results:* A link to the test results screen, and shortcuts to the logs and jobs of the failed tests. Functional tests capture and store the log output from each specific test, and make it visible at these links.
|
||||
3. *Pipeline Steps:*: A breakdown of the pipeline that was executed, along with individual log output for each step in the pipeline.
|
||||
|
||||
[discrete]
|
||||
=== Debugging Functional UI Test Failures
|
||||
|
||||
The logs in Pipeline Steps contain `Info` level logging. To debug Functional UI tests it's usually helpful to see the debug logging. You can go to the list of all tests including failures (1), or directly to the failures (2).
|
||||
The logs in Pipeline Steps contain `Info` level logging. To debug Functional UI tests it's usually helpful to see the debug logging. You can go to the test failure details by clicking on the *logs* (1).
|
||||
|
||||
image::images/test_results.png[Jenkisn build screenshot]
|
||||
image::images/test_results.png[Buildkite build screenshot]
|
||||
|
||||
Looking at the failure, we first look at the Error and stack trace. In the example below, this test failed to find an element within the timeout;
|
||||
`Error: retry.try timeout: TimeoutError: Waiting for element to be located By(css selector, [data-test-subj="createSpace"])`
|
||||
|
|
Before Width: | Height: | Size: 624 KiB After Width: | Height: | Size: 570 KiB |
Before Width: | Height: | Size: 443 KiB |
Before Width: | Height: | Size: 266 KiB After Width: | Height: | Size: 324 KiB |
|
@ -431,7 +431,7 @@ The plugin exposes the static DefaultEditorController class to consume.
|
|||
|
||||
|
||||
|{kib-repo}blob/{branch}/x-pack/plugins/apm/readme.md[apm]
|
||||
|undefined
|
||||
|This plugin provides access to App Monitoring features provided by Elastic. It allows you to monitor your software services and applications in real-time; visualize detailed performance information on your services, identify and analyze errors, and monitor host-level and APM agent-specific metrics like JVM and Go runtime metrics.
|
||||
|
||||
|
||||
|{kib-repo}blob/{branch}/x-pack/plugins/banners/README.md[banners]
|
||||
|
|
|
@ -64,7 +64,7 @@ export default async function ({ readConfigFile }) {
|
|||
}
|
||||
|
||||
// more settings, like timeouts, mochaOpts, etc are
|
||||
// defined in the config schema. See {blob}src/functional_test_runner/lib/config/schema.js[src/functional_test_runner/lib/config/schema.js]
|
||||
// defined in the config schema. See {kibana-blob}src/functional_test_runner/lib/config/schema.js[src/functional_test_runner/lib/config/schema.js]
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -47,9 +47,9 @@ To use {kib} i18n tooling, create a `.i18nrc.json` file with the following confi
|
|||
}
|
||||
-----------
|
||||
|
||||
An example {kib} `.i18nrc.json` is {blob}.i18nrc.json[here].
|
||||
An example {kib} `.i18nrc.json` is {kibana-blob}.i18nrc.json[here].
|
||||
|
||||
Full documentation about i18n tooling is {blob}src/dev/i18n/README.md[here].
|
||||
Full documentation about i18n tooling is {kibana-blob}src/dev/i18n/README.md[here].
|
||||
|
||||
[discrete]
|
||||
=== Extracting default messages
|
||||
|
|
|
@ -9,10 +9,6 @@ release-state can be: released | prerelease | unreleased
|
|||
:major-version: 5.4
|
||||
:branch: 5.4
|
||||
|
||||
:docker-image: docker.elastic.co/kibana/kibana:{version}
|
||||
:es-ref: https://www.elastic.co/guide/en/elasticsearch/reference/{branch}/
|
||||
:security: https://www.elastic.co/community/security/
|
||||
|
||||
include::{docs-root}/shared/attributes.asciidoc[]
|
||||
|
||||
include::introduction.asciidoc[]
|
||||
|
|
|
@ -1,26 +1,12 @@
|
|||
[[kibana-guide]]
|
||||
= Kibana Guide
|
||||
|
||||
:include-xpack: true
|
||||
:lang: en
|
||||
:kib-repo-dir: {kibana-root}/docs
|
||||
|
||||
include::{docs-root}/shared/versions/stack/{source_branch}.asciidoc[]
|
||||
|
||||
:docker-repo: docker.elastic.co/kibana/kibana
|
||||
:docker-image: {docker-repo}:{version}
|
||||
:es-docker-repo: docker.elastic.co/elasticsearch/elasticsearch
|
||||
:es-docker-image: {es-docker-repo}:{version}
|
||||
:security-ref: https://www.elastic.co/community/security/
|
||||
:Data-source: Data view
|
||||
:data-source: data view
|
||||
:data-sources: data views
|
||||
:a-data-source: a data view
|
||||
|
||||
include::{docs-root}/shared/attributes.asciidoc[]
|
||||
|
||||
:blob: {kib-repo}blob/{branch}/
|
||||
|
||||
include::user/index.asciidoc[]
|
||||
|
||||
include::accessibility.asciidoc[]
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
[role="xpack"]
|
||||
[[maps-connect-to-ems]]
|
||||
== Connect to Elastic Maps Service
|
||||
|
||||
:ems-docker-repo: docker.elastic.co/elastic-maps-service/elastic-maps-server-ubi8
|
||||
:ems-docker-image: {ems-docker-repo}:{version}
|
||||
|
||||
https://www.elastic.co/elastic-maps-service[Elastic Maps Service (EMS)] is a service that hosts
|
||||
tile layers and vector shapes of administrative boundaries.
|
||||
If you are using Kibana's out-of-the-box settings, Maps is already configured to use EMS.
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
:ems-docker-repo: docker.elastic.co/elastic-maps-service/elastic-maps-server-ubi8
|
||||
:ems-docker-image: {ems-docker-repo}:{version}
|
||||
|
||||
[role="xpack"]
|
||||
[[maps]]
|
||||
= Maps
|
||||
|
||||
|
|
|
@ -41,13 +41,6 @@ Refer to {ref}/security-basic-setup-https.html#encrypt-kibana-elasticsearch[Encr
|
|||
See <<monitoring-kibana>>.
|
||||
|
||||
|
||||
[role="exclude",id="settings-xpack-kb"]
|
||||
== {xpack} Settings in {kib}
|
||||
|
||||
include::{asciidoc-dir}/../../shared/settings.asciidoc[]
|
||||
|
||||
For more {kib} configuration settings, see <<settings>>.
|
||||
|
||||
[role="exclude",id="uptime-security"]
|
||||
== Uptime security
|
||||
|
||||
|
|
|
@ -142,6 +142,8 @@ If configured in your `kibana.yml`, output settings are grayed out and
|
|||
unavailable in the {fleet} UI. To make these settings editable in the UI, do not
|
||||
configure them in the configuration file.
|
||||
+
|
||||
NOTE: The `xpack.fleet.outputs` settings are intended for advanced configurations such as having multiple outputs. We recommend not enabling the `xpack.fleet.agents.elasticsearch.host` settings when using `xpack.fleet.outputs`.
|
||||
+
|
||||
.Required properties of `xpack.fleet.outputs`
|
||||
[%collapsible%open]
|
||||
=====
|
||||
|
@ -161,7 +163,9 @@ configure them in the configuration file.
|
|||
[%collapsible%open]
|
||||
=====
|
||||
`is_default`:::
|
||||
If `true`, this output is the default output.
|
||||
If `true`, the output specified in `xpack.fleet.outputs` will be the one used to send agent data unless there is another one configured specifically for the agent policy.
|
||||
`is_default_monitoring`:::
|
||||
If `true`, the output specified in `xpack.fleet.outputs` will be the one used to send agent monitoring data unless there is another one configured specifically for the agent policy.
|
||||
=====
|
||||
+
|
||||
Example configuration:
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
[role="xpack"]
|
||||
[[settings-xpack-kb]]
|
||||
== {xpack} settings in {kib}
|
||||
[subs="attributes"]
|
||||
++++
|
||||
<titleabbrev>{xpack} settings</titleabbrev>
|
||||
++++
|
||||
|
||||
include::{asciidoc-dir}/../../shared/settings.asciidoc[]
|
||||
|
||||
For more {kib} configuration settings, see <<settings>>.
|
||||
|
||||
include::alert-action-settings.asciidoc[]
|
||||
include::apm-settings.asciidoc[]
|
||||
include::banners-settings.asciidoc[]
|
||||
include::infrastructure-ui-settings.asciidoc[]
|
||||
include::logs-ui-settings.asciidoc[]
|
||||
include::reporting-settings.asciidoc[]
|
||||
include::spaces-settings.asciidoc[]
|
||||
include::task-manager-settings.asciidoc[]
|
||||
include::i18n-settings.asciidoc[]
|
||||
include::fleet-settings.asciidoc[]
|
|
@ -4,6 +4,11 @@
|
|||
<titleabbrev>Install with Docker</titleabbrev>
|
||||
++++
|
||||
|
||||
:docker-repo: docker.elastic.co/kibana/kibana
|
||||
:docker-image: {docker-repo}:{version}
|
||||
:es-docker-repo: docker.elastic.co/elasticsearch/elasticsearch
|
||||
:es-docker-image: {es-docker-repo}:{version}
|
||||
|
||||
Docker images for {kib} are available from the Elastic Docker registry. The
|
||||
base image is https://hub.docker.com/_/ubuntu[ubuntu:20.04].
|
||||
|
||||
|
|
|
@ -166,7 +166,8 @@ responses:
|
|||
Click the rule name to access a rule details page:
|
||||
|
||||
[role="screenshot"]
|
||||
image::images/rule-details-alerts-active.png[Rule details page with three alerts]
|
||||
image::images/rule-details-alerts-active.png[Rule details page with multiple alerts]
|
||||
// NOTE: This is an autogenerated screenshot. Do not edit it directly.
|
||||
|
||||
In this example, the rule detects when a site serves more than a threshold number of bytes in a 24 hour period. Four sites are above the threshold. These are called alerts - occurrences of the condition being detected - and the alert name, status, time of detection, and duration of the condition are shown in this view. Alerts come and go from the list depending on whether the rule conditions are met.
|
||||
|
||||
|
@ -182,4 +183,4 @@ You can suppress future actions for a specific alert by turning on the *Mute* to
|
|||
|
||||
[role="screenshot"]
|
||||
image::images/rule-details-disabling.png[Use the disable toggle to turn off rule checks and clear alerts tracked]
|
||||
|
||||
// NOTE: This is an autogenerated screenshot. Do not edit it directly.
|
||||
|
|
Before Width: | Height: | Size: 190 KiB After Width: | Height: | Size: 193 KiB |
Before Width: | Height: | Size: 852 KiB After Width: | Height: | Size: 273 KiB |
Before Width: | Height: | Size: 292 KiB After Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 169 KiB After Width: | Height: | Size: 170 KiB |
Before Width: | Height: | Size: 230 KiB After Width: | Height: | Size: 234 KiB |
|
@ -14,7 +14,7 @@ For example:
|
|||
|
||||
[source,sh]
|
||||
--------------------------------------------------
|
||||
`GET kbn:/api/index_management/indices`
|
||||
GET kbn:/api/index_management/indices
|
||||
--------------------------------------------------
|
||||
|
||||
Note: this will automatically prefix `s/{space_id}/` on the API request if ran from a non-default Kibana Space.
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
[[whats-new]]
|
||||
== What's new in {minor-version}
|
||||
|
||||
coming::[8.7.0]
|
||||
|
||||
Here are the highlights of what's new and improved in {minor-version}.
|
||||
For detailed information about this release,
|
||||
check the <<release-notes, release notes>>.
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
[
|
||||
{
|
||||
"name": "apm",
|
||||
"version": "8.7.0-preview-1678362302",
|
||||
"version": "8.7.1-preview-1680253486",
|
||||
"forceAlignStackVersion": true,
|
||||
"allowSyncToPrerelease": true
|
||||
},
|
||||
|
@ -46,6 +46,6 @@
|
|||
},
|
||||
{
|
||||
"name": "security_detection_engine",
|
||||
"version": "8.7.1"
|
||||
"version": "8.7.2"
|
||||
}
|
||||
]
|
|
@ -11,7 +11,7 @@
|
|||
"dashboarding"
|
||||
],
|
||||
"private": true,
|
||||
"version": "8.7.0",
|
||||
"version": "8.7.1",
|
||||
"branch": "8.7",
|
||||
"types": "./kibana.d.ts",
|
||||
"tsdocMetadata": "./build/tsdoc-metadata.json",
|
||||
|
@ -1026,7 +1026,7 @@
|
|||
"backport": "^8.9.7",
|
||||
"callsites": "^3.1.0",
|
||||
"chance": "1.0.18",
|
||||
"chromedriver": "^110.0.0",
|
||||
"chromedriver": "^112.0.0",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"compression-webpack-plugin": "^4.0.0",
|
||||
"copy-webpack-plugin": "^6.0.2",
|
||||
|
|
|
@ -36,7 +36,7 @@ export interface FullStorySnippetConfig {
|
|||
}
|
||||
|
||||
export function loadSnippet({
|
||||
scriptUrl = 'edge.fullstory.com/s/fs.js',
|
||||
scriptUrl = 'https://edge.fullstory.com/s/fs.js',
|
||||
fullStoryOrgId,
|
||||
host = 'fullstory.com',
|
||||
namespace = 'FS',
|
||||
|
|
|
@ -366,12 +366,13 @@ export const getEuiContextMapping = (): EuiTokensObject => {
|
|||
values={{ searchValue }}
|
||||
/>
|
||||
),
|
||||
'euiComboBoxOptionsList.delimiterMessage': ({ delimiter }: EuiValues) =>
|
||||
i18n.translate('core.euiComboBoxOptionsList.delimiterMessage', {
|
||||
defaultMessage: 'Add each item separated by {delimiter}',
|
||||
values: { delimiter },
|
||||
description: 'Screen reader text describing adding delimited options',
|
||||
}),
|
||||
'euiComboBoxOptionsList.delimiterMessage': ({ delimiter }: EuiValues) => (
|
||||
<FormattedMessage
|
||||
id="core.euiComboBoxOptionsList.delimiterMessage"
|
||||
defaultMessage="Add each item separated by {delimiter}"
|
||||
values={{ delimiter }}
|
||||
/>
|
||||
),
|
||||
'euiComboBoxPill.removeSelection': ({ children }: EuiValues) =>
|
||||
i18n.translate('core.euiComboBoxPill.removeSelection', {
|
||||
defaultMessage: 'Remove {children} from selection in this group',
|
||||
|
|
|
@ -1,145 +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
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { ensureDeepObject } from './ensure_deep_object';
|
||||
|
||||
test('flat object', () => {
|
||||
const obj = {
|
||||
'foo.a': 1,
|
||||
'foo.b': 2,
|
||||
};
|
||||
|
||||
expect(ensureDeepObject(obj)).toEqual({
|
||||
foo: {
|
||||
a: 1,
|
||||
b: 2,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('deep object', () => {
|
||||
const obj = {
|
||||
foo: {
|
||||
a: 1,
|
||||
b: 2,
|
||||
},
|
||||
};
|
||||
|
||||
expect(ensureDeepObject(obj)).toEqual({
|
||||
foo: {
|
||||
a: 1,
|
||||
b: 2,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('flat within deep object', () => {
|
||||
const obj = {
|
||||
foo: {
|
||||
b: 2,
|
||||
'bar.a': 1,
|
||||
},
|
||||
};
|
||||
|
||||
expect(ensureDeepObject(obj)).toEqual({
|
||||
foo: {
|
||||
b: 2,
|
||||
bar: {
|
||||
a: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('flat then flat object', () => {
|
||||
const obj = {
|
||||
'foo.bar': {
|
||||
b: 2,
|
||||
'quux.a': 1,
|
||||
},
|
||||
};
|
||||
|
||||
expect(ensureDeepObject(obj)).toEqual({
|
||||
foo: {
|
||||
bar: {
|
||||
b: 2,
|
||||
quux: {
|
||||
a: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('full with empty array', () => {
|
||||
const obj = {
|
||||
a: 1,
|
||||
b: [],
|
||||
};
|
||||
|
||||
expect(ensureDeepObject(obj)).toEqual({
|
||||
a: 1,
|
||||
b: [],
|
||||
});
|
||||
});
|
||||
|
||||
test('full with array of primitive values', () => {
|
||||
const obj = {
|
||||
a: 1,
|
||||
b: [1, 2, 3],
|
||||
};
|
||||
|
||||
expect(ensureDeepObject(obj)).toEqual({
|
||||
a: 1,
|
||||
b: [1, 2, 3],
|
||||
});
|
||||
});
|
||||
|
||||
test('full with array of full objects', () => {
|
||||
const obj = {
|
||||
a: 1,
|
||||
b: [{ c: 2 }, { d: 3 }],
|
||||
};
|
||||
|
||||
expect(ensureDeepObject(obj)).toEqual({
|
||||
a: 1,
|
||||
b: [{ c: 2 }, { d: 3 }],
|
||||
});
|
||||
});
|
||||
|
||||
test('full with array of flat objects', () => {
|
||||
const obj = {
|
||||
a: 1,
|
||||
b: [{ 'c.d': 2 }, { 'e.f': 3 }],
|
||||
};
|
||||
|
||||
expect(ensureDeepObject(obj)).toEqual({
|
||||
a: 1,
|
||||
b: [{ c: { d: 2 } }, { e: { f: 3 } }],
|
||||
});
|
||||
});
|
||||
|
||||
test('flat with flat and array of flat objects', () => {
|
||||
const obj = {
|
||||
a: 1,
|
||||
'b.c': 2,
|
||||
d: [3, { 'e.f': 4 }, { 'g.h': 5 }],
|
||||
};
|
||||
|
||||
expect(ensureDeepObject(obj)).toEqual({
|
||||
a: 1,
|
||||
b: { c: 2 },
|
||||
d: [3, { e: { f: 4 } }, { g: { h: 5 } }],
|
||||
});
|
||||
});
|
||||
|
||||
test('array composed of flat objects', () => {
|
||||
const arr = [{ 'c.d': 2 }, { 'e.f': 3 }];
|
||||
|
||||
expect(ensureDeepObject(arr)).toEqual([{ c: { d: 2 } }, { e: { f: 3 } }]);
|
||||
});
|
|
@ -1,50 +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
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
const separator = '.';
|
||||
|
||||
/**
|
||||
* Recursively traverses through the object's properties and expands ones with
|
||||
* dot-separated names into nested objects (eg. { a.b: 'c'} -> { a: { b: 'c' }).
|
||||
* @param obj Object to traverse through.
|
||||
* @returns Same object instance with expanded properties.
|
||||
*/
|
||||
export function ensureDeepObject(obj: any): any {
|
||||
if (obj == null || typeof obj !== 'object') {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map((item) => ensureDeepObject(item));
|
||||
}
|
||||
|
||||
return Object.keys(obj).reduce((fullObject, propertyKey) => {
|
||||
const propertyValue = obj[propertyKey];
|
||||
if (!propertyKey.includes(separator)) {
|
||||
fullObject[propertyKey] = ensureDeepObject(propertyValue);
|
||||
} else {
|
||||
walk(fullObject, propertyKey.split(separator), propertyValue);
|
||||
}
|
||||
|
||||
return fullObject;
|
||||
}, {} as any);
|
||||
}
|
||||
|
||||
function walk(obj: any, keys: string[], value: any) {
|
||||
const key = keys.shift()!;
|
||||
if (keys.length === 0) {
|
||||
obj[key] = value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj[key] === undefined) {
|
||||
obj[key] = {};
|
||||
}
|
||||
|
||||
walk(obj[key], keys, ensureDeepObject(value));
|
||||
}
|
|
@ -10,8 +10,8 @@ import { readFileSync } from 'fs';
|
|||
import { safeLoad } from 'js-yaml';
|
||||
|
||||
import { set } from '@kbn/safer-lodash-set';
|
||||
import { ensureDeepObject } from '@kbn/std';
|
||||
import { isPlainObject } from 'lodash';
|
||||
import { ensureDeepObject } from './ensure_deep_object';
|
||||
|
||||
const readYaml = (path: string) => {
|
||||
try {
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
"@kbn/safer-lodash-set",
|
||||
"@kbn/utils",
|
||||
"@kbn/config-schema",
|
||||
"@kbn/std",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -140,6 +140,8 @@ export type ApmFields = Fields<{
|
|||
values: number[];
|
||||
counts: number[];
|
||||
};
|
||||
'transaction.result': string;
|
||||
'transaction.sampled': boolean;
|
||||
'service.environment': string;
|
||||
'service.framework.name': string;
|
||||
'service.framework.version': string;
|
||||
|
@ -163,8 +165,6 @@ export type ApmFields = Fields<{
|
|||
'span.self_time.sum.us': number;
|
||||
'span.subtype': string;
|
||||
'span.type': string;
|
||||
'transaction.result': string;
|
||||
'transaction.sampled': true;
|
||||
'span.links': Array<{
|
||||
trace: { id: string };
|
||||
span: { id: string };
|
||||
|
|
|
@ -32,6 +32,7 @@ export class Transaction extends BaseSpan {
|
|||
error.fields['trace.id'] = this.fields['trace.id'];
|
||||
error.fields['transaction.id'] = this.fields['transaction.id'];
|
||||
error.fields['transaction.type'] = this.fields['transaction.type'];
|
||||
error.fields['transaction.sampled'] = this.fields['transaction.sampled'];
|
||||
});
|
||||
|
||||
return this;
|
||||
|
@ -43,6 +44,7 @@ export class Transaction extends BaseSpan {
|
|||
error.fields['transaction.id'] = this.fields['transaction.id'];
|
||||
error.fields['transaction.name'] = this.fields['transaction.name'];
|
||||
error.fields['transaction.type'] = this.fields['transaction.type'];
|
||||
error.fields['transaction.sampled'] = this.fields['transaction.sampled'];
|
||||
});
|
||||
|
||||
this._errors.push(...errors);
|
||||
|
@ -56,7 +58,10 @@ export class Transaction extends BaseSpan {
|
|||
}
|
||||
|
||||
sample(sampled: boolean = true) {
|
||||
this._sampled = sampled;
|
||||
this._sampled = this.fields['transaction.sampled'] = sampled;
|
||||
this._errors.forEach((error) => {
|
||||
error.fields['transaction.sampled'] = sampled;
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
5
packages/kbn-config/src/__fixtures__/forbidden_1.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
test: {
|
||||
"aaa['__proto__.hello']": "Hello",
|
||||
"aaa['__proto__.nested.there']": "There",
|
||||
"aaa['__proto__.nested.here']": "This JS syntax is apparently valid for our parser"
|
||||
}
|
3
packages/kbn-config/src/__fixtures__/forbidden_2.yml
Normal file
|
@ -0,0 +1,3 @@
|
|||
test:
|
||||
hello:
|
||||
'__proto__.dolly': "Well hello there"
|
|
@ -1,50 +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
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
const separator = '.';
|
||||
|
||||
/**
|
||||
* Recursively traverses through the object's properties and expands ones with
|
||||
* dot-separated names into nested objects (eg. { a.b: 'c'} -> { a: { b: 'c' }).
|
||||
* @param obj Object to traverse through.
|
||||
* @returns Same object instance with expanded properties.
|
||||
*/
|
||||
export function ensureDeepObject(obj: any): any {
|
||||
if (obj == null || typeof obj !== 'object') {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map((item) => ensureDeepObject(item));
|
||||
}
|
||||
|
||||
return Object.keys(obj).reduce((fullObject, propertyKey) => {
|
||||
const propertyValue = obj[propertyKey];
|
||||
if (!propertyKey.includes(separator)) {
|
||||
fullObject[propertyKey] = ensureDeepObject(propertyValue);
|
||||
} else {
|
||||
walk(fullObject, propertyKey.split(separator), propertyValue);
|
||||
}
|
||||
|
||||
return fullObject;
|
||||
}, {} as any);
|
||||
}
|
||||
|
||||
function walk(obj: any, keys: string[], value: any) {
|
||||
const key = keys.shift()!;
|
||||
if (keys.length === 0) {
|
||||
obj[key] = value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj[key] === undefined) {
|
||||
obj[key] = {};
|
||||
}
|
||||
|
||||
walk(obj[key], keys, ensureDeepObject(value));
|
||||
}
|
|
@ -47,6 +47,18 @@ test('should throw an exception when referenced environment variable in a config
|
|||
).toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
|
||||
test('throws parsing a config with forbidden paths', () => {
|
||||
expect(() =>
|
||||
getConfigFromFiles([fixtureFile('forbidden_1.yml')])
|
||||
).toThrowErrorMatchingInlineSnapshot(`"Forbidden path detected: test.aaa.__proto__.hello"`);
|
||||
});
|
||||
|
||||
test('throws parsing another config with forbidden paths', () => {
|
||||
expect(() =>
|
||||
getConfigFromFiles([fixtureFile('forbidden_2.yml')])
|
||||
).toThrowErrorMatchingInlineSnapshot(`"Forbidden path detected: test.hello.__proto__"`);
|
||||
});
|
||||
|
||||
describe('different cwd()', () => {
|
||||
const originalCwd = process.cwd();
|
||||
const tempCwd = resolve(__dirname);
|
||||
|
|
|
@ -11,7 +11,7 @@ import { safeLoad } from 'js-yaml';
|
|||
|
||||
import { set } from '@kbn/safer-lodash-set';
|
||||
import { isPlainObject } from 'lodash';
|
||||
import { ensureDeepObject } from './ensure_deep_object';
|
||||
import { ensureDeepObject } from '@kbn/std';
|
||||
|
||||
const readYaml = (path: string) => safeLoad(readFileSync(path, 'utf8'));
|
||||
|
||||
|
|
|
@ -19,12 +19,24 @@ export async function createFailureIssue(
|
|||
) {
|
||||
const title = `Failing test: ${failure.classname} - ${failure.name}`;
|
||||
|
||||
// Github API body length maximum is 65536 characters
|
||||
// Let's keep consistency with Mocha output that is truncated to 8192 characters
|
||||
const failureMaxCharacters = 8192;
|
||||
|
||||
const failureBody =
|
||||
failure.failure.length <= failureMaxCharacters
|
||||
? failure.failure
|
||||
: [
|
||||
failure.failure.substring(0, failureMaxCharacters),
|
||||
`[report_failure] output truncated to ${failureMaxCharacters} characters`,
|
||||
].join('\n');
|
||||
|
||||
const body = updateIssueMetadata(
|
||||
[
|
||||
'A test failed on a tracked branch',
|
||||
'',
|
||||
'```',
|
||||
failure.failure,
|
||||
failureBody,
|
||||
'```',
|
||||
'',
|
||||
`First failure: [CI Build - ${branch}](${buildUrl})`,
|
||||
|
|
|
@ -253,7 +253,7 @@ exports[`guide cards snapshots should render all cards 1`] = `
|
|||
Object {
|
||||
"navigateTo": Object {
|
||||
"appId": "integrations",
|
||||
"path": "/detail/cloud_security_posture/overview",
|
||||
"path": "/detail/cloud_security_posture/overview?integration=cspm",
|
||||
},
|
||||
"order": 9,
|
||||
"solution": "security",
|
||||
|
|
|
@ -169,7 +169,7 @@ export const guideCards: GuideCardConstants[] = [
|
|||
),
|
||||
navigateTo: {
|
||||
appId: 'integrations',
|
||||
path: '/detail/cloud_security_posture/overview',
|
||||
path: '/detail/cloud_security_posture/overview?integration=cspm',
|
||||
},
|
||||
telemetryId: 'onboarding--security--cloud',
|
||||
order: 9,
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { getMappingConflictsInfo } from '.';
|
||||
import { getMappingConflictsInfo, fieldSupportsMatches } from '.';
|
||||
|
||||
describe('Helpers', () => {
|
||||
describe('getMappingConflictsInfo', () => {
|
||||
|
@ -143,4 +143,42 @@ describe('Helpers', () => {
|
|||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fieldSupportsMatches', () => {
|
||||
test('it returns true if esTypes is keyword', () => {
|
||||
expect(
|
||||
fieldSupportsMatches({ name: 'field', type: 'conflict', esTypes: ['keyword'] })
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
test('it returns true if one of the esTypes is kibana type string and another is not', () => {
|
||||
expect(
|
||||
fieldSupportsMatches({ name: 'field', type: 'conflict', esTypes: ['keyword', 'object'] })
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
test('it returns true if one of the esTypes is keyword', () => {
|
||||
expect(
|
||||
fieldSupportsMatches({ name: 'field', type: 'conflict', esTypes: ['keyword', 'unmapped'] })
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
test('it returns true if one of the esTypes is text', () => {
|
||||
expect(
|
||||
fieldSupportsMatches({ name: 'field', type: 'conflict', esTypes: ['text', 'unmapped'] })
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
test('it returns true if all of the esTypes is map to kibana type string', () => {
|
||||
expect(
|
||||
fieldSupportsMatches({ name: 'field', type: 'conflict', esTypes: ['text', 'keyword'] })
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
test('it returns false if none of the esTypes map to kibana type string', () => {
|
||||
expect(
|
||||
fieldSupportsMatches({ name: 'field', type: 'conflict', esTypes: ['bool', 'unmapped'] })
|
||||
).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -35,6 +35,7 @@ import {
|
|||
getDataViewFieldSubtypeNested,
|
||||
isDataViewFieldSubtypeNested,
|
||||
} from '@kbn/es-query';
|
||||
import { castEsToKbnFieldTypeName, KBN_FIELD_TYPES } from '@kbn/field-types';
|
||||
|
||||
import {
|
||||
ALL_OPERATORS,
|
||||
|
@ -676,8 +677,13 @@ export const getEntryOnOperatorChange = (
|
|||
}
|
||||
};
|
||||
|
||||
const fieldSupportsMatches = (field: DataViewFieldBase) => {
|
||||
return field.type === 'string';
|
||||
export const isKibanaStringType = (type: string) => {
|
||||
const kbnFieldType = castEsToKbnFieldTypeName(type);
|
||||
return kbnFieldType === KBN_FIELD_TYPES.STRING;
|
||||
};
|
||||
|
||||
export const fieldSupportsMatches = (field: DataViewFieldBase) => {
|
||||
return field.esTypes?.some(isKibanaStringType);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,6 +26,10 @@ import {
|
|||
EXCEPTION_LIST_NAMESPACE_AGNOSTIC,
|
||||
} from '@kbn/securitysolution-list-constants';
|
||||
|
||||
export interface DataViewField extends DataViewFieldBase {
|
||||
conflictDescriptions?: Record<string, string[]>;
|
||||
}
|
||||
|
||||
export interface OperatorOption {
|
||||
message: string;
|
||||
value: string;
|
||||
|
@ -35,7 +39,7 @@ export interface OperatorOption {
|
|||
|
||||
export interface FormattedBuilderEntry {
|
||||
id: string;
|
||||
field: DataViewFieldBase | undefined;
|
||||
field: DataViewField | undefined;
|
||||
operator: OperatorOption;
|
||||
value: string | string[] | undefined;
|
||||
nested: 'parent' | 'child' | undefined;
|
||||
|
@ -117,7 +121,3 @@ export const exceptionListAgnosticSavedObjectType = EXCEPTION_LIST_NAMESPACE_AGN
|
|||
export type SavedObjectType =
|
||||
| typeof EXCEPTION_LIST_NAMESPACE
|
||||
| typeof EXCEPTION_LIST_NAMESPACE_AGNOSTIC;
|
||||
|
||||
export interface DataViewField extends DataViewFieldBase {
|
||||
conflictDescriptions?: Record<string, string[]>;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
"@kbn/securitysolution-io-ts-list-types",
|
||||
"@kbn/securitysolution-io-ts-utils",
|
||||
"@kbn/securitysolution-list-constants",
|
||||
"@kbn/securitysolution-utils"
|
||||
"@kbn/securitysolution-utils",
|
||||
"@kbn/field-types"
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -27,4 +27,5 @@ export {
|
|||
asyncForEach,
|
||||
asyncForEachWithLimit,
|
||||
} from './src/iteration';
|
||||
export { ensureDeepObject } from './src/ensure_deep_object';
|
||||
export { Semaphore } from './src/semaphore';
|
||||
|
|
|
@ -143,3 +143,81 @@ test('array composed of flat objects', () => {
|
|||
|
||||
expect(ensureDeepObject(arr)).toEqual([{ c: { d: 2 } }, { e: { f: 3 } }]);
|
||||
});
|
||||
|
||||
describe('forbidden patterns', () => {
|
||||
describe('first pattern', () => {
|
||||
test('throws when finding the first pattern within an object', () => {
|
||||
const obj = {
|
||||
foo: {
|
||||
hello: 'dolly',
|
||||
'bar.__proto__': { yours: 'mine' },
|
||||
},
|
||||
};
|
||||
|
||||
expect(() => ensureDeepObject(obj)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Forbidden path detected: foo.bar.__proto__"`
|
||||
);
|
||||
});
|
||||
|
||||
test('throws when finding the first pattern within an array', () => {
|
||||
const obj = {
|
||||
array: [
|
||||
'hello',
|
||||
{
|
||||
'bar.__proto__': { their: 'mine' },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(() => ensureDeepObject(obj)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Forbidden path detected: array.1.bar.__proto__"`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('second pattern', () => {
|
||||
test('throws when finding the first pattern within an object', () => {
|
||||
const obj = {
|
||||
foo: {
|
||||
hello: 'dolly',
|
||||
'bar.constructor.prototype': { foo: 'bar' },
|
||||
},
|
||||
};
|
||||
|
||||
expect(() => ensureDeepObject(obj)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Forbidden path detected: foo.bar.constructor.prototype"`
|
||||
);
|
||||
});
|
||||
|
||||
test('throws when finding the first pattern within a nested object', () => {
|
||||
const obj = {
|
||||
foo: {
|
||||
hello: 'dolly',
|
||||
'bar.constructor': {
|
||||
main: 'mine',
|
||||
prototype: 'nope',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(() => ensureDeepObject(obj)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Forbidden path detected: foo.bar.constructor.prototype"`
|
||||
);
|
||||
});
|
||||
|
||||
test('throws when finding the first pattern within an array', () => {
|
||||
const obj = {
|
||||
array: [
|
||||
'hello',
|
||||
{
|
||||
'bar.constructor.prototype': { foo: 'bar' },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(() => ensureDeepObject(obj)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Forbidden path detected: array.1.bar.constructor.prototype"`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -6,56 +6,61 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
//
|
||||
// THIS IS A DIRECT COPY OF
|
||||
// 'packages/kbn-config/src/raw/ensure_deep_object.ts'
|
||||
// BECAUSE THAT IS BLOCKED FOR IMPORTING BY OUR LINTER.
|
||||
//
|
||||
// IF THAT IS EXPOSED, WE SHOULD USE IT RATHER THAN CLONE IT.
|
||||
//
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// ^ Disabling the rule for the entire file because of the complexity to type this
|
||||
|
||||
const FORBIDDEN_PATTERNS = ['__proto__', 'constructor.prototype'];
|
||||
const separator = '.';
|
||||
|
||||
/**
|
||||
* Recursively traverses through the object's properties and expands ones with
|
||||
* dot-separated names into nested objects (eg. { a.b: 'c'} -> { a: { b: 'c' }).
|
||||
* @param obj Object to traverse through.
|
||||
* @param path The current path of the traversal
|
||||
* @returns Same object instance with expanded properties.
|
||||
*/
|
||||
export function ensureDeepObject(obj: any): any {
|
||||
export function ensureDeepObject(obj: any, path: string[] = []): any {
|
||||
assertValidPath(path);
|
||||
|
||||
if (obj == null || typeof obj !== 'object') {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map((item) => ensureDeepObject(item));
|
||||
return obj.map((item, index) => ensureDeepObject(item, [...path, `${index}`]));
|
||||
}
|
||||
|
||||
return Object.keys(obj).reduce((fullObject, propertyKey) => {
|
||||
const propertyValue = obj[propertyKey];
|
||||
if (!propertyKey.includes(separator)) {
|
||||
fullObject[propertyKey] = ensureDeepObject(propertyValue);
|
||||
const propertySplits = propertyKey.split(separator);
|
||||
if (propertySplits.length === 1) {
|
||||
fullObject[propertyKey] = ensureDeepObject(propertyValue, [...path, propertyKey]);
|
||||
} else {
|
||||
walk(fullObject, propertyKey.split(separator), propertyValue);
|
||||
walk(fullObject, propertySplits, propertyValue, path);
|
||||
}
|
||||
|
||||
return fullObject;
|
||||
}, {} as any);
|
||||
}
|
||||
|
||||
function walk(obj: any, keys: string[], value: any) {
|
||||
function walk(obj: any, keys: string[], value: any, path: string[]) {
|
||||
assertValidPath([...path, ...keys]);
|
||||
|
||||
const key = keys.shift()!;
|
||||
if (keys.length === 0) {
|
||||
obj[key] = value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj[key] === undefined) {
|
||||
if (!obj.hasOwnProperty(key)) {
|
||||
obj[key] = {};
|
||||
}
|
||||
|
||||
walk(obj[key], keys, ensureDeepObject(value));
|
||||
walk(obj[key], keys, ensureDeepObject(value, [...path, key, ...keys]), [...path, key]);
|
||||
}
|
||||
|
||||
const assertValidPath = (path: string[]) => {
|
||||
const flat = path.join('.');
|
||||
FORBIDDEN_PATTERNS.forEach((pattern) => {
|
||||
if (flat.includes(pattern)) {
|
||||
throw new Error(`Forbidden path detected: ${flat}`);
|
||||
}
|
||||
});
|
||||
};
|
|
@ -6,7 +6,6 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
require('@kbn/babel-register').install();
|
||||
require('./apm')(process.env.ELASTIC_APM_SERVICE_NAME || 'kibana-proxy');
|
||||
require('../setup_node_env');
|
||||
require('./apm')(process.env.ELASTIC_APM_SERVICE_NAME || 'kibana-proxy');
|
||||
require('./cli');
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
require('./apm')();
|
||||
require('../setup_node_env/dist');
|
||||
require('./apm')();
|
||||
require('../setup_node_env/root');
|
||||
require('./cli');
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
"@kbn/config",
|
||||
"@kbn/dev-utils",
|
||||
"@kbn/apm-config-loader",
|
||||
"@kbn/babel-register",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
143
src/dev/build/tasks/fetch_agent_versions_list.test.ts
Normal file
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import fetch from 'node-fetch';
|
||||
import pRetry from 'p-retry';
|
||||
|
||||
import { REPO_ROOT } from '@kbn/repo-info';
|
||||
import { ToolingLog } from '@kbn/tooling-log';
|
||||
|
||||
import { FetchAgentVersionsList } from './fetch_agent_versions_list';
|
||||
import { Build, Config, write } from '../lib';
|
||||
|
||||
jest.mock('node-fetch');
|
||||
jest.mock('p-retry');
|
||||
jest.mock('../lib');
|
||||
|
||||
const config = new Config(
|
||||
true,
|
||||
{
|
||||
version: '8.0.0',
|
||||
engines: {
|
||||
node: '*',
|
||||
},
|
||||
workspaces: {
|
||||
packages: [],
|
||||
},
|
||||
} as any,
|
||||
'1.2.3',
|
||||
REPO_ROOT,
|
||||
{
|
||||
buildNumber: 1234,
|
||||
buildSha: 'abcd1234',
|
||||
buildVersion: '8.0.0',
|
||||
},
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
'',
|
||||
'',
|
||||
false,
|
||||
true
|
||||
);
|
||||
|
||||
const mockedFetch = fetch as jest.MockedFunction<typeof fetch>;
|
||||
const mockedPRetry = pRetry as jest.MockedFunction<typeof pRetry>;
|
||||
const mockedWrite = write as jest.MockedFunction<typeof write>;
|
||||
const mockedBuild = new Build(config);
|
||||
|
||||
mockedPRetry.mockImplementation((fn: any) => {
|
||||
return fn();
|
||||
});
|
||||
|
||||
const processEnv = process.env;
|
||||
|
||||
describe('FetchAgentVersionsList', () => {
|
||||
beforeEach(() => {
|
||||
process.env = {
|
||||
...processEnv,
|
||||
// Ensure these tests can run in PR builds
|
||||
BUILDKITE_PULL_REQUEST: undefined,
|
||||
};
|
||||
|
||||
mockedFetch.mockReset();
|
||||
(mockedBuild.resolvePath as jest.Mock<any>).mockReset();
|
||||
mockedWrite.mockReset();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.env = processEnv;
|
||||
});
|
||||
|
||||
describe('when BUILDKITE_PULL_REQUEST is set', () => {
|
||||
it('does not run task', async () => {
|
||||
process.env.BUILDKITE_PULL_REQUEST = '1234';
|
||||
|
||||
await FetchAgentVersionsList.run(config, new ToolingLog(), mockedBuild);
|
||||
|
||||
expect(mockedFetch).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when valid JSON is returned from versions endpoint', () => {
|
||||
it('does not throw', async () => {
|
||||
mockedFetch.mockResolvedValueOnce({
|
||||
status: 200,
|
||||
text: jest.fn().mockResolvedValueOnce(
|
||||
JSON.stringify([
|
||||
[
|
||||
{
|
||||
title: 'Elastic Agent 8.0.0',
|
||||
version_number: '8.0.0',
|
||||
},
|
||||
{
|
||||
title: 'Elastic Agent 8.0.1',
|
||||
version_number: '8.0.1',
|
||||
},
|
||||
],
|
||||
])
|
||||
),
|
||||
} as any);
|
||||
|
||||
await FetchAgentVersionsList.run(config, new ToolingLog(), mockedBuild);
|
||||
|
||||
expect(mockedWrite).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when a network error is thrown', () => {
|
||||
it('throws', async () => {
|
||||
mockedFetch.mockResolvedValueOnce({
|
||||
status: 503,
|
||||
text: jest.fn().mockResolvedValueOnce('Gateway timeout'),
|
||||
} as any);
|
||||
|
||||
try {
|
||||
await FetchAgentVersionsList.run(config, new ToolingLog(), mockedBuild);
|
||||
} catch (error) {
|
||||
expect(error).toBeTruthy();
|
||||
expect(mockedWrite).not.toHaveBeenCalled();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('when invalid json is returned from versions endpoint', () => {
|
||||
it('throws', async () => {
|
||||
mockedFetch.mockResolvedValueOnce({
|
||||
status: 200,
|
||||
text: jest.fn().mockResolvedValueOnce('not json'),
|
||||
} as any);
|
||||
|
||||
try {
|
||||
await FetchAgentVersionsList.run(config, new ToolingLog(), mockedBuild);
|
||||
} catch (error) {
|
||||
expect(error).toBeTruthy();
|
||||
expect(mockedWrite).not.toHaveBeenCalled();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
|
@ -7,24 +7,34 @@
|
|||
*/
|
||||
|
||||
import fetch from 'node-fetch';
|
||||
import pRetry from 'p-retry';
|
||||
|
||||
import { ToolingLog } from '@kbn/tooling-log';
|
||||
import { write, Task } from '../lib';
|
||||
|
||||
// Endpoint maintained by the web-team and hosted on the elastic website
|
||||
const PRODUCT_VERSIONS_URL = 'https://www.elastic.co/api/product_versions';
|
||||
|
||||
const isPr = () =>
|
||||
!!process.env.BUILDKITE_PULL_REQUEST && process.env.BUILDKITE_PULL_REQUEST !== 'false';
|
||||
|
||||
const getAvailableVersions = async (log: ToolingLog) => {
|
||||
const options = {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
};
|
||||
// Endpoint maintained by the web-team and hosted on the elastic website
|
||||
// See https://github.com/elastic/website-development/issues/9331
|
||||
const url = 'https://www.elastic.co/api/product_versions';
|
||||
try {
|
||||
log.info('Fetching Elastic Agent versions list');
|
||||
const results = await fetch(url, options);
|
||||
log.info('Fetching Elastic Agent versions list');
|
||||
|
||||
const jsonBody = await results.json();
|
||||
try {
|
||||
const results = await pRetry(() => fetch(PRODUCT_VERSIONS_URL, options), { retries: 3 });
|
||||
const rawBody = await results.text();
|
||||
|
||||
if (results.status >= 400) {
|
||||
throw new Error(`Status code ${results.status} received from versions API: ${rawBody}`);
|
||||
}
|
||||
|
||||
const jsonBody = JSON.parse(rawBody);
|
||||
|
||||
const versions: string[] = (jsonBody.length ? jsonBody[0] : [])
|
||||
.filter((item: any) => item?.title?.includes('Elastic Agent'))
|
||||
|
@ -33,8 +43,18 @@ const getAvailableVersions = async (log: ToolingLog) => {
|
|||
log.info(`Retrieved available versions`);
|
||||
return versions;
|
||||
} catch (error) {
|
||||
log.warning(`Failed to fetch versions list`);
|
||||
log.warning(error);
|
||||
const errorMessage = 'Failed to fetch Elastic Agent versions list';
|
||||
|
||||
if (isPr()) {
|
||||
// For PR jobs, just log the error as a warning and continue
|
||||
log.warning(errorMessage);
|
||||
log.warning(error);
|
||||
} else {
|
||||
// For non-PR jobs like nightly builds, log the error to stderror and throw
|
||||
// to ensure the build fails
|
||||
log.error(errorMessage);
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
@ -44,6 +64,11 @@ export const FetchAgentVersionsList: Task = {
|
|||
description: 'Build list of available Elastic Agent versions for Fleet UI',
|
||||
|
||||
async run(config, log, build) {
|
||||
// Agent version list task is skipped for PR's, so as not to overwhelm the versions API
|
||||
if (isPr()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const versionsList = await getAvailableVersions(log);
|
||||
const AGENT_VERSION_BUILD_FILE = 'x-pack/plugins/fleet/target/agent_versions_list.json';
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import useMount from 'react-use/lib/useMount';
|
||||
import useAsync from 'react-use/lib/useAsync';
|
||||
|
||||
import {
|
||||
EuiFlyoutHeader,
|
||||
|
@ -35,7 +36,7 @@ import {
|
|||
EuiSwitch,
|
||||
EuiTextColor,
|
||||
} from '@elastic/eui';
|
||||
import { DataViewListItem, DataView, DataViewField } from '@kbn/data-views-plugin/common';
|
||||
import { DataViewField } from '@kbn/data-views-plugin/common';
|
||||
import {
|
||||
LazyDataViewPicker,
|
||||
LazyFieldPicker,
|
||||
|
@ -46,13 +47,13 @@ import { ControlGroupStrings } from '../control_group_strings';
|
|||
import {
|
||||
ControlEmbeddable,
|
||||
ControlWidth,
|
||||
DataControlFieldRegistry,
|
||||
DataControlInput,
|
||||
IEditableControlFactory,
|
||||
} from '../../types';
|
||||
import { CONTROL_WIDTH_OPTIONS } from './editor_constants';
|
||||
import { pluginServices } from '../../services';
|
||||
import { getDataControlFieldRegistry } from './data_control_editor_tools';
|
||||
|
||||
interface EditControlProps {
|
||||
embeddable?: ControlEmbeddable<DataControlInput>;
|
||||
isCreate: boolean;
|
||||
|
@ -70,12 +71,6 @@ interface EditControlProps {
|
|||
onTypeEditorChange: (partial: Partial<DataControlInput>) => void;
|
||||
}
|
||||
|
||||
interface ControlEditorState {
|
||||
dataViewListItems: DataViewListItem[];
|
||||
selectedDataView?: DataView;
|
||||
selectedField?: DataViewField;
|
||||
}
|
||||
|
||||
const FieldPicker = withSuspense(LazyFieldPicker, null);
|
||||
const DataViewPicker = withSuspense(LazyDataViewPicker, null);
|
||||
|
||||
|
@ -99,59 +94,62 @@ export const ControlEditor = ({
|
|||
dataViews: { getIdsWithTitle, getDefaultId, get },
|
||||
controls: { getControlFactory },
|
||||
} = pluginServices.getServices();
|
||||
const [state, setState] = useState<ControlEditorState>({
|
||||
dataViewListItems: [],
|
||||
});
|
||||
|
||||
const [defaultTitle, setDefaultTitle] = useState<string>();
|
||||
const [currentTitle, setCurrentTitle] = useState(title);
|
||||
const [currentTitle, setCurrentTitle] = useState(title ?? '');
|
||||
const [currentWidth, setCurrentWidth] = useState(width);
|
||||
const [currentGrow, setCurrentGrow] = useState(grow);
|
||||
const [controlEditorValid, setControlEditorValid] = useState(false);
|
||||
const [selectedField, setSelectedField] = useState<string | undefined>(
|
||||
embeddable ? embeddable.getInput().fieldName : undefined
|
||||
);
|
||||
|
||||
const [fieldRegistry, setFieldRegistry] = useState<DataControlFieldRegistry>();
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
if (state.selectedDataView?.id) {
|
||||
setFieldRegistry(await getDataControlFieldRegistry(await get(state.selectedDataView.id)));
|
||||
}
|
||||
})();
|
||||
}, [state.selectedDataView?.id, get]);
|
||||
const [selectedDataViewId, setSelectedDataViewId] = useState<string>();
|
||||
|
||||
useMount(() => {
|
||||
let mounted = true;
|
||||
if (selectedField) setDefaultTitle(selectedField);
|
||||
|
||||
(async () => {
|
||||
const dataViewListItems = await getIdsWithTitle();
|
||||
if (!mounted) return;
|
||||
|
||||
const initialId =
|
||||
embeddable?.getInput().dataViewId ?? getRelevantDataViewId?.() ?? (await getDefaultId());
|
||||
let dataView: DataView | undefined;
|
||||
if (initialId) {
|
||||
onTypeEditorChange({ dataViewId: initialId });
|
||||
dataView = await get(initialId);
|
||||
setSelectedDataViewId(initialId);
|
||||
}
|
||||
if (!mounted) return;
|
||||
setState((s) => ({
|
||||
...s,
|
||||
selectedDataView: dataView,
|
||||
dataViewListItems,
|
||||
}));
|
||||
})();
|
||||
return () => {
|
||||
mounted = false;
|
||||
};
|
||||
});
|
||||
|
||||
const { loading: dataViewListLoading, value: dataViewListItems = [] } = useAsync(() => {
|
||||
return getIdsWithTitle();
|
||||
});
|
||||
|
||||
const {
|
||||
loading: dataViewLoading,
|
||||
value: { selectedDataView, fieldRegistry } = {
|
||||
selectedDataView: undefined,
|
||||
fieldRegistry: undefined,
|
||||
},
|
||||
} = useAsync(async () => {
|
||||
if (!selectedDataViewId) {
|
||||
return;
|
||||
}
|
||||
const dataView = await get(selectedDataViewId);
|
||||
const registry = await getDataControlFieldRegistry(dataView);
|
||||
return {
|
||||
selectedDataView: dataView,
|
||||
fieldRegistry: registry,
|
||||
};
|
||||
}, [selectedDataViewId]);
|
||||
|
||||
useEffect(
|
||||
() => setControlEditorValid(Boolean(selectedField) && Boolean(state.selectedDataView)),
|
||||
[selectedField, setControlEditorValid, state.selectedDataView]
|
||||
() => setControlEditorValid(Boolean(selectedField) && Boolean(selectedDataView)),
|
||||
[selectedField, setControlEditorValid, selectedDataView]
|
||||
);
|
||||
|
||||
const { selectedDataView: dataView } = state;
|
||||
const controlType =
|
||||
selectedField && fieldRegistry && fieldRegistry[selectedField].compatibleControlTypes[0];
|
||||
const factory = controlType && getControlFactory(controlType);
|
||||
|
@ -172,46 +170,45 @@ export const ControlEditor = ({
|
|||
<EuiForm>
|
||||
<EuiFormRow label={ControlGroupStrings.manageControl.getDataViewTitle()}>
|
||||
<DataViewPicker
|
||||
dataViews={state.dataViewListItems}
|
||||
selectedDataViewId={dataView?.id}
|
||||
dataViews={dataViewListItems}
|
||||
selectedDataViewId={selectedDataViewId}
|
||||
onChangeDataViewId={(dataViewId) => {
|
||||
setLastUsedDataViewId?.(dataViewId);
|
||||
if (dataViewId === dataView?.id) return;
|
||||
|
||||
if (dataViewId === selectedDataViewId) return;
|
||||
onTypeEditorChange({ dataViewId });
|
||||
setSelectedField(undefined);
|
||||
get(dataViewId).then((newDataView) => {
|
||||
setState((s) => ({ ...s, selectedDataView: newDataView }));
|
||||
});
|
||||
setSelectedDataViewId(dataViewId);
|
||||
}}
|
||||
trigger={{
|
||||
label:
|
||||
state.selectedDataView?.getName() ??
|
||||
selectedDataView?.getName() ??
|
||||
ControlGroupStrings.manageControl.getSelectDataViewMessage(),
|
||||
}}
|
||||
selectableProps={{ isLoading: dataViewListLoading }}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow label={ControlGroupStrings.manageControl.getFieldTitle()}>
|
||||
<FieldPicker
|
||||
filterPredicate={(field: DataViewField) => {
|
||||
return Boolean(fieldRegistry?.[field.name]);
|
||||
}}
|
||||
selectedFieldName={selectedField}
|
||||
dataView={dataView}
|
||||
onSelectField={(field) => {
|
||||
onTypeEditorChange({
|
||||
fieldName: field.name,
|
||||
});
|
||||
const newDefaultTitle = field.displayName ?? field.name;
|
||||
setDefaultTitle(newDefaultTitle);
|
||||
setSelectedField(field.name);
|
||||
if (!currentTitle || currentTitle === defaultTitle) {
|
||||
setCurrentTitle(newDefaultTitle);
|
||||
updateTitle(newDefaultTitle);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
{fieldRegistry && (
|
||||
<EuiFormRow label={ControlGroupStrings.manageControl.getFieldTitle()}>
|
||||
<FieldPicker
|
||||
filterPredicate={(field: DataViewField) => Boolean(fieldRegistry[field.name])}
|
||||
selectedFieldName={selectedField}
|
||||
dataView={selectedDataView}
|
||||
onSelectField={(field) => {
|
||||
onTypeEditorChange({
|
||||
fieldName: field.name,
|
||||
});
|
||||
const newDefaultTitle = field.displayName ?? field.name;
|
||||
setDefaultTitle(newDefaultTitle);
|
||||
setSelectedField(field.name);
|
||||
if (!currentTitle || currentTitle === defaultTitle) {
|
||||
setCurrentTitle(newDefaultTitle);
|
||||
updateTitle(newDefaultTitle);
|
||||
}
|
||||
}}
|
||||
selectableProps={{ isLoading: dataViewListLoading || dataViewLoading }}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
)}
|
||||
<EuiFormRow label={ControlGroupStrings.manageControl.getControlTypeTitle()}>
|
||||
{factory ? (
|
||||
<EuiFlexGroup alignItems="center" gutterSize="xs">
|
||||
|
@ -239,6 +236,7 @@ export const ControlEditor = ({
|
|||
}}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
|
||||
<EuiFormRow label={ControlGroupStrings.manageControl.getWidthInputTitle()}>
|
||||
<>
|
||||
<EuiButtonGroup
|
||||
|
@ -273,7 +271,7 @@ export const ControlEditor = ({
|
|||
<CustomSettings
|
||||
onChange={onTypeEditorChange}
|
||||
initialInput={embeddable?.getInput()}
|
||||
fieldType={fieldRegistry[selectedField].field.type}
|
||||
fieldType={fieldRegistry?.[selectedField].field.type}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
)}
|
||||
|
|
|
@ -11,7 +11,7 @@ import { memoize } from 'lodash';
|
|||
import { DataView } from '@kbn/data-views-plugin/common';
|
||||
|
||||
import { pluginServices } from '../../services';
|
||||
import { DataControlField, DataControlFieldRegistry, IEditableControlFactory } from '../../types';
|
||||
import { DataControlFieldRegistry, IEditableControlFactory } from '../../types';
|
||||
|
||||
export const getDataControlFieldRegistry = memoize(
|
||||
async (dataView: DataView) => {
|
||||
|
@ -30,20 +30,19 @@ const loadFieldRegistryFromDataView = async (
|
|||
const controlFactories = getControlTypes().map(
|
||||
(controlType) => getControlFactory(controlType) as IEditableControlFactory
|
||||
);
|
||||
const fieldRegistry: DataControlFieldRegistry = dataView.fields
|
||||
.getAll()
|
||||
.reduce((registry, field) => {
|
||||
const test: DataControlField = { field, compatibleControlTypes: [] };
|
||||
const fieldRegistry: DataControlFieldRegistry = {};
|
||||
return new Promise<DataControlFieldRegistry>((resolve) => {
|
||||
for (const field of dataView.fields.getAll()) {
|
||||
const compatibleControlTypes = [];
|
||||
for (const factory of controlFactories) {
|
||||
if (factory.isFieldCompatible) {
|
||||
factory.isFieldCompatible(test);
|
||||
if (factory.isFieldCompatible && factory.isFieldCompatible(field)) {
|
||||
compatibleControlTypes.push(factory.type);
|
||||
}
|
||||
}
|
||||
if (test.compatibleControlTypes.length === 0) {
|
||||
return { ...registry };
|
||||
if (compatibleControlTypes.length > 0) {
|
||||
fieldRegistry[field.name] = { field, compatibleControlTypes };
|
||||
}
|
||||
return { ...registry, [field.name]: test };
|
||||
}, {});
|
||||
|
||||
return fieldRegistry;
|
||||
}
|
||||
resolve(fieldRegistry);
|
||||
});
|
||||
};
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
import deepEqual from 'fast-deep-equal';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { DataViewField } from '@kbn/data-views-plugin/common';
|
||||
import { lazyLoadReduxEmbeddablePackage } from '@kbn/presentation-util-plugin/public';
|
||||
import { EmbeddableFactoryDefinition, IContainer } from '@kbn/embeddable-plugin/public';
|
||||
|
||||
|
@ -20,8 +21,8 @@ import {
|
|||
OptionsListEmbeddableInput,
|
||||
OPTIONS_LIST_CONTROL,
|
||||
} from '../../../common/options_list/types';
|
||||
import { ControlEmbeddable, DataControlField, IEditableControlFactory } from '../../types';
|
||||
import { OptionsListEditorOptions } from '../components/options_list_editor_options';
|
||||
import { ControlEmbeddable, IEditableControlFactory } from '../../types';
|
||||
|
||||
export class OptionsListEmbeddableFactory
|
||||
implements EmbeddableFactoryDefinition, IEditableControlFactory<OptionsListEmbeddableInput>
|
||||
|
@ -54,15 +55,13 @@ export class OptionsListEmbeddableFactory
|
|||
return newInput;
|
||||
};
|
||||
|
||||
public isFieldCompatible = (dataControlField: DataControlField) => {
|
||||
if (
|
||||
!dataControlField.field.spec.scripted &&
|
||||
((dataControlField.field.aggregatable && dataControlField.field.type === 'string') ||
|
||||
dataControlField.field.type === 'boolean' ||
|
||||
dataControlField.field.type === 'ip')
|
||||
) {
|
||||
dataControlField.compatibleControlTypes.push(this.type);
|
||||
}
|
||||
public isFieldCompatible = (field: DataViewField) => {
|
||||
return (
|
||||
!field.spec.scripted &&
|
||||
((field.aggregatable && field.type === 'string') ||
|
||||
field.type === 'boolean' ||
|
||||
field.type === 'ip')
|
||||
);
|
||||
};
|
||||
|
||||
public controlEditorOptionsComponent = OptionsListEditorOptions;
|
||||
|
|
|
@ -10,6 +10,7 @@ import deepEqual from 'fast-deep-equal';
|
|||
|
||||
import { EmbeddableFactoryDefinition, IContainer } from '@kbn/embeddable-plugin/public';
|
||||
import { lazyLoadReduxEmbeddablePackage } from '@kbn/presentation-util-plugin/public';
|
||||
import { DataViewField } from '@kbn/data-views-plugin/common';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import {
|
||||
|
@ -20,7 +21,7 @@ import {
|
|||
RangeSliderEmbeddableInput,
|
||||
RANGE_SLIDER_CONTROL,
|
||||
} from '../../../common/range_slider/types';
|
||||
import { ControlEmbeddable, DataControlField, IEditableControlFactory } from '../../types';
|
||||
import { ControlEmbeddable, IEditableControlFactory } from '../../types';
|
||||
|
||||
export class RangeSliderEmbeddableFactory
|
||||
implements EmbeddableFactoryDefinition, IEditableControlFactory<RangeSliderEmbeddableInput>
|
||||
|
@ -68,10 +69,8 @@ export class RangeSliderEmbeddableFactory
|
|||
return newInput;
|
||||
};
|
||||
|
||||
public isFieldCompatible = (dataControlField: DataControlField) => {
|
||||
if (dataControlField.field.aggregatable && dataControlField.field.type === 'number') {
|
||||
dataControlField.compatibleControlTypes.push(this.type);
|
||||
}
|
||||
public isFieldCompatible = (field: DataViewField) => {
|
||||
return field.aggregatable && field.type === 'number';
|
||||
};
|
||||
|
||||
public inject = createRangeSliderInject();
|
||||
|
|
|
@ -46,13 +46,14 @@ export type ControlEmbeddable<
|
|||
/**
|
||||
* Control embeddable editor types
|
||||
*/
|
||||
export interface IEditableControlFactory<T extends ControlInput = ControlInput> {
|
||||
export interface IEditableControlFactory<T extends ControlInput = ControlInput>
|
||||
extends Pick<EmbeddableFactory, 'type'> {
|
||||
controlEditorOptionsComponent?: (props: ControlEditorProps<T>) => JSX.Element;
|
||||
presaveTransformFunction?: (
|
||||
newState: Partial<T>,
|
||||
embeddable?: ControlEmbeddable<T>
|
||||
) => Partial<T>;
|
||||
isFieldCompatible?: (dataControlField: DataControlField) => void; // reducer
|
||||
isFieldCompatible?: (field: DataViewField) => boolean;
|
||||
}
|
||||
|
||||
export interface ControlEditorProps<T extends ControlInput = ControlInput> {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import { getKbnServerError, reportServerError } from '@kbn/kibana-utils-plugin/server';
|
||||
import { CoreSetup } from '@kbn/core/server';
|
||||
import { errors } from '@elastic/elasticsearch';
|
||||
|
||||
export const setupOptionsListClusterSettingsRoute = ({ http }: CoreSetup) => {
|
||||
const router = http.createRouter();
|
||||
|
@ -39,6 +40,17 @@ export const setupOptionsListClusterSettingsRoute = ({ http }: CoreSetup) => {
|
|||
},
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof errors.ResponseError && e.body.error.type === 'security_exception') {
|
||||
/**
|
||||
* in cases where the user does not have the 'monitor' permission this check will fail. In these cases, we will
|
||||
* fall back to assume that the allowExpensiveQueries setting is on, because it defaults to true.
|
||||
*/
|
||||
return response.ok({
|
||||
body: {
|
||||
allowExpensiveQueries: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
const kbnErr = getKbnServerError(e);
|
||||
return reportServerError(response, kbnErr);
|
||||
}
|
||||
|
|
|
@ -7,117 +7,31 @@
|
|||
*/
|
||||
|
||||
import { mapSpatialFilter } from './map_spatial_filter';
|
||||
import { mapFilter } from '../map_filter';
|
||||
import { FilterMeta, Filter, FILTERS } from '@kbn/es-query';
|
||||
|
||||
describe('mapSpatialFilter()', () => {
|
||||
test('should return the key for matching multi polygon filter', async () => {
|
||||
describe('mapSpatialFilter', () => {
|
||||
test('should set meta type field', async () => {
|
||||
const filter = {
|
||||
meta: {
|
||||
key: 'location',
|
||||
alias: 'my spatial filter',
|
||||
type: FILTERS.SPATIAL_FILTER,
|
||||
} as FilterMeta,
|
||||
query: {
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
geo_polygon: {
|
||||
geoCoordinates: { points: [] },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
query: {},
|
||||
} as Filter;
|
||||
const result = mapSpatialFilter(filter);
|
||||
|
||||
expect(result).toHaveProperty('key', 'location');
|
||||
expect(result).toHaveProperty('value', '');
|
||||
expect(result).toHaveProperty('type', FILTERS.SPATIAL_FILTER);
|
||||
});
|
||||
|
||||
test('should return the key for matching polygon filter', async () => {
|
||||
const filter = {
|
||||
meta: {
|
||||
key: 'location',
|
||||
alias: 'my spatial filter',
|
||||
type: FILTERS.SPATIAL_FILTER,
|
||||
} as FilterMeta,
|
||||
geo_polygon: {
|
||||
geoCoordinates: { points: [] },
|
||||
},
|
||||
} as Filter;
|
||||
const result = mapSpatialFilter(filter);
|
||||
|
||||
expect(result).toHaveProperty('key', 'location');
|
||||
expect(result).toHaveProperty('value', '');
|
||||
expect(result).toHaveProperty('type', FILTERS.SPATIAL_FILTER);
|
||||
});
|
||||
|
||||
test('should return the key for matching multi field filter', async () => {
|
||||
const filter = {
|
||||
meta: {
|
||||
alias: 'my spatial filter',
|
||||
isMultiIndex: true,
|
||||
type: FILTERS.SPATIAL_FILTER,
|
||||
} as FilterMeta,
|
||||
query: {
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
bool: {
|
||||
must: [
|
||||
{
|
||||
exists: {
|
||||
field: 'geo.coordinates',
|
||||
},
|
||||
},
|
||||
{
|
||||
geo_distance: {
|
||||
distance: '1000km',
|
||||
'geo.coordinates': [120, 30],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
bool: {
|
||||
must: [
|
||||
{
|
||||
exists: {
|
||||
field: 'location',
|
||||
},
|
||||
},
|
||||
{
|
||||
geo_distance: {
|
||||
distance: '1000km',
|
||||
location: [120, 30],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
} as Filter;
|
||||
const result = mapSpatialFilter(filter);
|
||||
|
||||
expect(result).toHaveProperty('key', 'query');
|
||||
expect(result).toHaveProperty('value', '');
|
||||
expect(result).toHaveProperty('type', FILTERS.SPATIAL_FILTER);
|
||||
expect(result).toHaveProperty('key', undefined);
|
||||
expect(result).toHaveProperty('value', undefined);
|
||||
});
|
||||
|
||||
test('should return undefined for none matching', async () => {
|
||||
const filter = {
|
||||
meta: {
|
||||
key: 'location',
|
||||
alias: 'my spatial filter',
|
||||
alias: 'my non-spatial filter',
|
||||
} as FilterMeta,
|
||||
geo_polygon: {
|
||||
geoCoordinates: { points: [] },
|
||||
},
|
||||
query: {},
|
||||
} as Filter;
|
||||
|
||||
try {
|
||||
|
@ -127,3 +41,17 @@ describe('mapSpatialFilter()', () => {
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('mapFilter', () => {
|
||||
test('should set key and value properties to undefined', async () => {
|
||||
const before = {
|
||||
meta: { type: FILTERS.SPATIAL_FILTER } as FilterMeta,
|
||||
query: {},
|
||||
} as Filter;
|
||||
const after = mapFilter(before);
|
||||
|
||||
expect(after).toHaveProperty('meta');
|
||||
expect(after.meta).toHaveProperty('key', undefined);
|
||||
expect(after.meta).toHaveProperty('value', undefined);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,30 +10,17 @@ import { Filter, FILTERS } from '@kbn/es-query';
|
|||
|
||||
// Use mapSpatialFilter mapper to avoid bloated meta with value and params for spatial filters.
|
||||
export const mapSpatialFilter = (filter: Filter) => {
|
||||
if (
|
||||
filter.meta &&
|
||||
filter.meta.key &&
|
||||
filter.meta.alias &&
|
||||
filter.meta.type === FILTERS.SPATIAL_FILTER
|
||||
) {
|
||||
if (filter.meta?.type === FILTERS.SPATIAL_FILTER) {
|
||||
return {
|
||||
key: filter.meta.key,
|
||||
type: filter.meta.type,
|
||||
value: '',
|
||||
// spatial filters support multiple fields across multiple data views
|
||||
// do not provide "key" since filter does not provide a single field
|
||||
key: undefined,
|
||||
// default mapper puts stringified filter in "value"
|
||||
// do not provide "value" to avoid bloating URL
|
||||
value: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
filter.meta &&
|
||||
filter.meta.type === FILTERS.SPATIAL_FILTER &&
|
||||
filter.meta.isMultiIndex &&
|
||||
filter.query?.bool?.should
|
||||
) {
|
||||
return {
|
||||
key: 'query',
|
||||
type: filter.meta.type,
|
||||
value: '',
|
||||
};
|
||||
}
|
||||
throw filter;
|
||||
};
|
||||
|
|
|
@ -59,6 +59,9 @@ const currentDocumentSelector = (state: PreviewState) => state.documents[state.c
|
|||
const currentDocumentIsLoadingSelector = (state: PreviewState) => state.isLoadingDocuments;
|
||||
|
||||
const ScriptFieldComponent = ({ existingConcreteFields, links, placeholder }: Props) => {
|
||||
const {
|
||||
validation: { setScriptEditorValidation },
|
||||
} = useFieldPreviewContext();
|
||||
const monacoEditor = useRef<monaco.editor.IStandaloneCodeEditor | null>(null);
|
||||
const editorValidationSubscription = useRef<Subscription>();
|
||||
const fieldCurrentValue = useRef<string>('');
|
||||
|
@ -143,7 +146,7 @@ const ScriptFieldComponent = ({ existingConcreteFields, links, placeholder }: Pr
|
|||
|
||||
editorValidationSubscription.current = PainlessLang.validation$().subscribe(
|
||||
({ isValid, isValidating, errors }) => {
|
||||
controller.setScriptEditorValidation({
|
||||
setScriptEditorValidation({
|
||||
isValid,
|
||||
isValidating,
|
||||
message: errors[0]?.message ?? null,
|
||||
|
@ -151,7 +154,7 @@ const ScriptFieldComponent = ({ existingConcreteFields, links, placeholder }: Pr
|
|||
}
|
||||
);
|
||||
},
|
||||
[controller]
|
||||
[setScriptEditorValidation]
|
||||
);
|
||||
|
||||
const updateMonacoMarkers = useCallback((markers: monaco.editor.IMarkerData[]) => {
|
||||
|
|
|
@ -75,8 +75,6 @@ const documentsSelector = (state: PreviewState) => {
|
|||
};
|
||||
};
|
||||
|
||||
const scriptEditorValidationSelector = (state: PreviewState) => state.scriptEditorValidation;
|
||||
|
||||
export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewController }> = ({
|
||||
controller,
|
||||
children,
|
||||
|
@ -121,6 +119,12 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro
|
|||
/** The parameters required for the Painless _execute API */
|
||||
const [params, setParams] = useState<Params>(defaultParams);
|
||||
|
||||
const [scriptEditorValidation, setScriptEditorValidation] = useState<{
|
||||
isValidating: boolean;
|
||||
isValid: boolean;
|
||||
message: string | null;
|
||||
}>({ isValidating: false, isValid: true, message: null });
|
||||
|
||||
/** Flag to show/hide the preview panel */
|
||||
const [isPanelVisible, setIsPanelVisible] = useState(true);
|
||||
/** Flag to indicate if we are loading document from cluster */
|
||||
|
@ -133,10 +137,6 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro
|
|||
|
||||
const { currentDocument, currentDocIndex, currentDocId, totalDocs, currentIdx } =
|
||||
useStateSelector(controller.state$, documentsSelector);
|
||||
const scriptEditorValidation = useStateSelector(
|
||||
controller.state$,
|
||||
scriptEditorValidationSelector
|
||||
);
|
||||
|
||||
let isPreviewAvailable = true;
|
||||
|
||||
|
@ -513,6 +513,9 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro
|
|||
isVisible: isPanelVisible,
|
||||
setIsVisible: setIsPanelVisible,
|
||||
},
|
||||
validation: {
|
||||
setScriptEditorValidation,
|
||||
},
|
||||
reset,
|
||||
}),
|
||||
[
|
||||
|
|
|
@ -99,9 +99,11 @@ export class PreviewController {
|
|||
}
|
||||
};
|
||||
|
||||
/* disabled while investigating issues with painless script editor
|
||||
setScriptEditorValidation = (scriptEditorValidation: PreviewState['scriptEditorValidation']) => {
|
||||
this.updateState({ scriptEditorValidation });
|
||||
};
|
||||
*/
|
||||
|
||||
setCustomId = (customId?: string) => {
|
||||
this.updateState({ customId });
|
||||
|
|
|
@ -133,6 +133,11 @@ export interface Context {
|
|||
isLastDoc: boolean;
|
||||
};
|
||||
reset: () => void;
|
||||
validation: {
|
||||
setScriptEditorValidation: React.Dispatch<
|
||||
React.SetStateAction<{ isValid: boolean; isValidating: boolean; message: string | null }>
|
||||
>;
|
||||
};
|
||||
}
|
||||
|
||||
export type PainlessExecuteContext =
|
||||
|
|