From eb9d8798c227cf580a299beff454c7bb1803ae5e Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 23 Feb 2022 12:41:38 +0000 Subject: [PATCH 001/137] skip flaky suite (#124681) --- .../apps/observability/alerts/rule_stats.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/observability_functional/apps/observability/alerts/rule_stats.ts b/x-pack/test/observability_functional/apps/observability/alerts/rule_stats.ts index 41ff88be079d4f..6dabf813f1d56d 100644 --- a/x-pack/test/observability_functional/apps/observability/alerts/rule_stats.ts +++ b/x-pack/test/observability_functional/apps/observability/alerts/rule_stats.ts @@ -45,7 +45,8 @@ export default ({ getService }: FtrProviderContext) => { }); }); - describe('Stat counters', () => { + // FLAKY: https://github.com/elastic/kibana/issues/124681 + describe.skip('Stat counters', () => { beforeEach(async () => { const uniqueKey = generateUniqueKey(); From 573fdce59fbf95de8c6937f7153bac4028903888 Mon Sep 17 00:00:00 2001 From: Faisal Kanout Date: Wed, 23 Feb 2022 15:59:44 +0300 Subject: [PATCH 002/137] [RAC][APM] Add {{context.reason}} variable to the rule templating language. (#125196) * Add reason message an available rule variable * Add alert reason to error threshold * Add reason msg as a rule variable to the other rule types * Fix checks * Fix lint * Fix tests * Fix tests and add param * Fix tests * Fix transaction test * Add msg for the transaction test --- .../server/routes/alerts/action_variables.ts | 9 ++++++++ .../register_error_count_alert_type.test.ts | 3 +++ .../alerts/register_error_count_alert_type.ts | 18 ++++++++------- ...er_transaction_duration_alert_type.test.ts | 3 +++ ...egister_transaction_duration_alert_type.ts | 22 ++++++++++--------- ...action_duration_anomaly_alert_type.test.ts | 8 ++++++- ...transaction_duration_anomaly_alert_type.ts | 18 ++++++++------- ..._transaction_error_rate_alert_type.test.ts | 2 ++ ...ister_transaction_error_rate_alert_type.ts | 20 +++++++++-------- 9 files changed, 67 insertions(+), 36 deletions(-) diff --git a/x-pack/plugins/apm/server/routes/alerts/action_variables.ts b/x-pack/plugins/apm/server/routes/alerts/action_variables.ts index b065da7123decc..540cd9ffd49464 100644 --- a/x-pack/plugins/apm/server/routes/alerts/action_variables.ts +++ b/x-pack/plugins/apm/server/routes/alerts/action_variables.ts @@ -56,4 +56,13 @@ export const apmActionVariables = { ), name: 'interval' as const, }, + reason: { + description: i18n.translate( + 'xpack.apm.alerts.action_variables.reasonMessage', + { + defaultMessage: 'A concise description of the reason for the alert', + } + ), + name: 'reason' as const, + }, }; diff --git a/x-pack/plugins/apm/server/routes/alerts/register_error_count_alert_type.test.ts b/x-pack/plugins/apm/server/routes/alerts/register_error_count_alert_type.test.ts index e1e807c05400cb..175b87f7943b0e 100644 --- a/x-pack/plugins/apm/server/routes/alerts/register_error_count_alert_type.test.ts +++ b/x-pack/plugins/apm/server/routes/alerts/register_error_count_alert_type.test.ts @@ -142,6 +142,7 @@ describe('Error count alert', () => { environment: 'env-foo', threshold: 2, triggerValue: 5, + reason: 'Error count is 5 in the last 5 mins for foo. Alert when > 2.', interval: '5m', }); expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { @@ -149,11 +150,13 @@ describe('Error count alert', () => { environment: 'env-foo-2', threshold: 2, triggerValue: 4, + reason: 'Error count is 4 in the last 5 mins for foo. Alert when > 2.', interval: '5m', }); expect(scheduleActions).toHaveBeenCalledWith('threshold_met', { serviceName: 'bar', environment: 'env-bar', + reason: 'Error count is 3 in the last 5 mins for bar. Alert when > 2.', threshold: 2, triggerValue: 3, interval: '5m', diff --git a/x-pack/plugins/apm/server/routes/alerts/register_error_count_alert_type.ts b/x-pack/plugins/apm/server/routes/alerts/register_error_count_alert_type.ts index b75f687d0dcaff..f5df3c946f46e0 100644 --- a/x-pack/plugins/apm/server/routes/alerts/register_error_count_alert_type.ts +++ b/x-pack/plugins/apm/server/routes/alerts/register_error_count_alert_type.ts @@ -74,6 +74,7 @@ export function registerErrorCountAlertType({ apmActionVariables.threshold, apmActionVariables.triggerValue, apmActionVariables.interval, + apmActionVariables.reason, ], }, producer: APM_SERVER_FEATURE_ID, @@ -139,7 +140,13 @@ export function registerErrorCountAlertType({ .filter((result) => result.errorCount >= ruleParams.threshold) .forEach((result) => { const { serviceName, environment, errorCount } = result; - + const alertReason = formatErrorCountReason({ + serviceName, + threshold: ruleParams.threshold, + measured: errorCount, + windowSize: ruleParams.windowSize, + windowUnit: ruleParams.windowUnit, + }); services .alertWithLifecycle({ id: [AlertType.ErrorCount, serviceName, environment] @@ -151,13 +158,7 @@ export function registerErrorCountAlertType({ [PROCESSOR_EVENT]: ProcessorEvent.error, [ALERT_EVALUATION_VALUE]: errorCount, [ALERT_EVALUATION_THRESHOLD]: ruleParams.threshold, - [ALERT_REASON]: formatErrorCountReason({ - serviceName, - threshold: ruleParams.threshold, - measured: errorCount, - windowSize: ruleParams.windowSize, - windowUnit: ruleParams.windowUnit, - }), + [ALERT_REASON]: alertReason, }, }) .scheduleActions(alertTypeConfig.defaultActionGroupId, { @@ -166,6 +167,7 @@ export function registerErrorCountAlertType({ threshold: ruleParams.threshold, triggerValue: errorCount, interval: `${ruleParams.windowSize}${ruleParams.windowUnit}`, + reason: alertReason, }); }); diff --git a/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_alert_type.test.ts b/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_alert_type.test.ts index debe14e41db111..6a3feed69c19a1 100644 --- a/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_alert_type.test.ts +++ b/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_alert_type.test.ts @@ -44,6 +44,7 @@ describe('registerTransactionDurationAlertType', () => { windowUnit: 'm', transactionType: 'request', serviceName: 'opbeans-java', + aggregationType: 'avg', }; await executor({ params }); expect(scheduleActions).toHaveBeenCalledTimes(1); @@ -54,6 +55,8 @@ describe('registerTransactionDurationAlertType', () => { threshold: 3000000, triggerValue: '5,500 ms', interval: `5m`, + reason: + 'Avg. latency is 5,500 ms in the last 5 mins for opbeans-java. Alert when > 3,000 ms.', }); }); }); diff --git a/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_alert_type.ts b/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_alert_type.ts index 0c5546f3549f5c..4567670129720b 100644 --- a/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_alert_type.ts +++ b/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_alert_type.ts @@ -86,6 +86,7 @@ export function registerTransactionDurationAlertType({ apmActionVariables.threshold, apmActionVariables.triggerValue, apmActionVariables.interval, + apmActionVariables.reason, ], }, producer: APM_SERVER_FEATURE_ID, @@ -178,7 +179,15 @@ export function registerTransactionDurationAlertType({ const durationFormatter = getDurationFormatter(transactionDuration); const transactionDurationFormatted = durationFormatter(transactionDuration).formatted; - + const reasonMessage = formatTransactionDurationReason({ + measured: transactionDuration, + serviceName: ruleParams.serviceName, + threshold: thresholdMicroseconds, + asDuration, + aggregationType: String(ruleParams.aggregationType), + windowSize: ruleParams.windowSize, + windowUnit: ruleParams.windowUnit, + }); services .alertWithLifecycle({ id: `${AlertType.TransactionDuration}_${getEnvironmentLabel( @@ -191,15 +200,7 @@ export function registerTransactionDurationAlertType({ [PROCESSOR_EVENT]: ProcessorEvent.transaction, [ALERT_EVALUATION_VALUE]: transactionDuration, [ALERT_EVALUATION_THRESHOLD]: thresholdMicroseconds, - [ALERT_REASON]: formatTransactionDurationReason({ - measured: transactionDuration, - serviceName: ruleParams.serviceName, - threshold: thresholdMicroseconds, - asDuration, - aggregationType: String(ruleParams.aggregationType), - windowSize: ruleParams.windowSize, - windowUnit: ruleParams.windowUnit, - }), + [ALERT_REASON]: reasonMessage, }, }) .scheduleActions(alertTypeConfig.defaultActionGroupId, { @@ -209,6 +210,7 @@ export function registerTransactionDurationAlertType({ threshold: thresholdMicroseconds, triggerValue: transactionDurationFormatted, interval: `${ruleParams.windowSize}${ruleParams.windowUnit}`, + reason: reasonMessage, }); } diff --git a/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_anomaly_alert_type.test.ts b/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_anomaly_alert_type.test.ts index 41bb5126646fca..585fadc348700d 100644 --- a/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_anomaly_alert_type.test.ts +++ b/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_anomaly_alert_type.test.ts @@ -179,7 +179,11 @@ describe('Transaction duration anomaly alert', () => { ml, }); - const params = { anomalySeverityType: ANOMALY_SEVERITY.MINOR }; + const params = { + anomalySeverityType: ANOMALY_SEVERITY.MINOR, + windowSize: 5, + windowUnit: 'm', + }; await executor({ params }); @@ -195,6 +199,8 @@ describe('Transaction duration anomaly alert', () => { environment: 'development', threshold: 'minor', triggerValue: 'critical', + reason: + 'critical anomaly with a score of 80 was detected in the last 5 mins for foo.', }); }); }); diff --git a/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_anomaly_alert_type.ts b/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_anomaly_alert_type.ts index d95432458d0683..fbdd7f5e33f0a5 100644 --- a/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_anomaly_alert_type.ts +++ b/x-pack/plugins/apm/server/routes/alerts/register_transaction_duration_anomaly_alert_type.ts @@ -85,6 +85,7 @@ export function registerTransactionDurationAnomalyAlertType({ apmActionVariables.environment, apmActionVariables.threshold, apmActionVariables.triggerValue, + apmActionVariables.reason, ], }, producer: 'apm', @@ -210,7 +211,13 @@ export function registerTransactionDurationAnomalyAlertType({ compact(anomalies).forEach((anomaly) => { const { serviceName, environment, transactionType, score } = anomaly; const severityLevel = getSeverity(score); - + const reasonMessage = formatTransactionDurationAnomalyReason({ + measured: score, + serviceName, + severityLevel, + windowSize: params.windowSize, + windowUnit: params.windowUnit, + }); services .alertWithLifecycle({ id: [ @@ -229,13 +236,7 @@ export function registerTransactionDurationAnomalyAlertType({ [ALERT_SEVERITY]: severityLevel, [ALERT_EVALUATION_VALUE]: score, [ALERT_EVALUATION_THRESHOLD]: threshold, - [ALERT_REASON]: formatTransactionDurationAnomalyReason({ - measured: score, - serviceName, - severityLevel, - windowSize: params.windowSize, - windowUnit: params.windowUnit, - }), + [ALERT_REASON]: reasonMessage, }, }) .scheduleActions(alertTypeConfig.defaultActionGroupId, { @@ -244,6 +245,7 @@ export function registerTransactionDurationAnomalyAlertType({ environment: getEnvironmentLabel(environment), threshold: selectedOption?.label, triggerValue: severityLevel, + reason: reasonMessage, }); }); diff --git a/x-pack/plugins/apm/server/routes/alerts/register_transaction_error_rate_alert_type.test.ts b/x-pack/plugins/apm/server/routes/alerts/register_transaction_error_rate_alert_type.test.ts index a054dcba53a3b7..36ec8e6ce205fe 100644 --- a/x-pack/plugins/apm/server/routes/alerts/register_transaction_error_rate_alert_type.test.ts +++ b/x-pack/plugins/apm/server/routes/alerts/register_transaction_error_rate_alert_type.test.ts @@ -124,6 +124,8 @@ describe('Transaction error rate alert', () => { serviceName: 'foo', transactionType: 'type-foo', environment: 'env-foo', + reason: + 'Failed transactions is 10% in the last 5 mins for foo. Alert when > 10%.', threshold: 10, triggerValue: '10', interval: '5m', diff --git a/x-pack/plugins/apm/server/routes/alerts/register_transaction_error_rate_alert_type.ts b/x-pack/plugins/apm/server/routes/alerts/register_transaction_error_rate_alert_type.ts index c48baf0457335a..0f68e74a2a9bce 100644 --- a/x-pack/plugins/apm/server/routes/alerts/register_transaction_error_rate_alert_type.ts +++ b/x-pack/plugins/apm/server/routes/alerts/register_transaction_error_rate_alert_type.ts @@ -83,6 +83,7 @@ export function registerTransactionErrorRateAlertType({ apmActionVariables.threshold, apmActionVariables.triggerValue, apmActionVariables.interval, + apmActionVariables.reason, ], }, producer: APM_SERVER_FEATURE_ID, @@ -198,7 +199,14 @@ export function registerTransactionErrorRateAlertType({ results.forEach((result) => { const { serviceName, environment, transactionType, errorRate } = result; - + const reasonMessage = formatTransactionErrorRateReason({ + threshold: ruleParams.threshold, + measured: errorRate, + asPercent, + serviceName, + windowSize: ruleParams.windowSize, + windowUnit: ruleParams.windowUnit, + }); services .alertWithLifecycle({ id: [ @@ -216,14 +224,7 @@ export function registerTransactionErrorRateAlertType({ [PROCESSOR_EVENT]: ProcessorEvent.transaction, [ALERT_EVALUATION_VALUE]: errorRate, [ALERT_EVALUATION_THRESHOLD]: ruleParams.threshold, - [ALERT_REASON]: formatTransactionErrorRateReason({ - threshold: ruleParams.threshold, - measured: errorRate, - asPercent, - serviceName, - windowSize: ruleParams.windowSize, - windowUnit: ruleParams.windowUnit, - }), + [ALERT_REASON]: reasonMessage, }, }) .scheduleActions(alertTypeConfig.defaultActionGroupId, { @@ -233,6 +234,7 @@ export function registerTransactionErrorRateAlertType({ threshold: ruleParams.threshold, triggerValue: asDecimalOrInteger(errorRate), interval: `${ruleParams.windowSize}${ruleParams.windowUnit}`, + reason: reasonMessage, }); }); From 324245c94c6808089b06966ae60bf9035ab965db Mon Sep 17 00:00:00 2001 From: Josh Dover <1813008+joshdover@users.noreply.github.com> Date: Wed, 23 Feb 2022 14:28:22 +0100 Subject: [PATCH 003/137] Remove Fleet QA labeling automation (#126244) --- .github/workflows/label-qa-fixed-in.yml | 80 ------------------------- 1 file changed, 80 deletions(-) delete mode 100644 .github/workflows/label-qa-fixed-in.yml diff --git a/.github/workflows/label-qa-fixed-in.yml b/.github/workflows/label-qa-fixed-in.yml deleted file mode 100644 index bb203d7d17c43a..00000000000000 --- a/.github/workflows/label-qa-fixed-in.yml +++ /dev/null @@ -1,80 +0,0 @@ -name: Add QA labels to Fleet issues -on: - pull_request: - types: - - closed - -jobs: - fetch_issues_to_label: - runs-on: ubuntu-latest - # Only run on PRs that were merged for the Fleet team - if: | - github.event.pull_request.merged_at && - contains(github.event.pull_request.labels.*.name, 'Team:Fleet') - outputs: - matrix: ${{ steps.issues_to_label.outputs.value }} - label_ids: ${{ steps.label_ids.outputs.value }} - steps: - - uses: octokit/graphql-action@v2.x - id: closing_issues - with: - query: | - query closingIssueNumbersQuery($prnumber: Int!) { - repository(owner: "elastic", name: "kibana") { - pullRequest(number: $prnumber) { - closingIssuesReferences(first: 10) { - nodes { - id - labels(first: 20) { - nodes { - id - name - } - } - } - } - } - } - } - prnumber: ${{ github.event.number }} - env: - GITHUB_TOKEN: ${{ secrets.FLEET_TECH_KIBANA_USER_TOKEN }} - - uses: sergeysova/jq-action@v2 - id: issues_to_label - with: - # Map to the issues' node id - cmd: echo $CLOSING_ISSUES | jq -c '.repository.pullRequest.closingIssuesReferences.nodes | map(.id)' - multiline: true - env: - CLOSING_ISSUES: ${{ steps.closing_issues.outputs.data }} - - uses: sergeysova/jq-action@v2 - id: label_ids - with: - # Get list of version labels on pull request and map to label's node id, append 'QA:Ready For Testing' id ("MDU6TGFiZWwyNTQ1NjcwOTI4") - cmd: echo $PR_LABELS | jq -c 'map(select(.name | test("v[0-9]+\\.[0-9]+\\.[0-9]+")) | .node_id) + ["MDU6TGFiZWwyNTQ1NjcwOTI4"]' - multiline: true - env: - PR_LABELS: ${{ toJSON(github.event.pull_request.labels) }} - - label_issues: - needs: fetch_issues_to_label - runs-on: ubuntu-latest - # For each issue closed by the PR run this job - strategy: - matrix: - issueNodeId: ${{ fromJSON(needs.fetch_issues_to_label.outputs.matrix) }} - name: Label issue ${{ matrix.issueNodeId }} - steps: - - uses: octokit/graphql-action@v2.x - id: add_labels_to_closed_issue - with: - query: | - mutation add_label($issueid:String!, $labelids:[String!]!) { - addLabelsToLabelable(input: {labelableId: $issueid, labelIds: $labelids}) { - clientMutationId - } - } - issueid: ${{ matrix.issueNodeId }} - labelids: ${{ needs.fetch_issues_to_label.outputs.label_ids }} - env: - GITHUB_TOKEN: ${{ secrets.FLEET_TECH_KIBANA_USER_TOKEN }} From b1444ca4beaefe01d747663c9e52429aeea8a0b8 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Wed, 23 Feb 2022 08:39:31 -0500 Subject: [PATCH 004/137] [Fleet] Fix preconfiguration inputs enablement (#126205) --- .../plugins/fleet/server/services/package_policy.test.ts | 8 ++++---- x-pack/plugins/fleet/server/services/package_policy.ts | 9 +++------ 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/fleet/server/services/package_policy.test.ts b/x-pack/plugins/fleet/server/services/package_policy.test.ts index 92a3e9ac99d2b3..40a60e2b525cc2 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.test.ts @@ -1704,8 +1704,8 @@ describe('Package policy service', () => { }); }); - describe('when an input or stream is disabled on the original policy object', () => { - it('remains disabled on the resulting policy object', () => { + describe('when an input or stream is disabled by default in the package', () => { + it('allow preconfiguration to enable it', () => { const basePackagePolicy: NewPackagePolicy = { name: 'base-package-policy', description: 'Base Package Policy', @@ -1914,13 +1914,13 @@ describe('Package policy service', () => { expect(template2Inputs).toHaveLength(1); const logsInput = template1Inputs?.find((input) => input.type === 'logs'); - expect(logsInput?.enabled).toBe(false); + expect(logsInput?.enabled).toBe(true); const logfileStream = logsInput?.streams.find( (stream) => stream.data_stream.type === 'logfile' ); - expect(logfileStream?.enabled).toBe(false); + expect(logfileStream?.enabled).toBe(true); }); }); diff --git a/x-pack/plugins/fleet/server/services/package_policy.ts b/x-pack/plugins/fleet/server/services/package_policy.ts index 2ac10ccfe77803..0a4ab8e2f26d56 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.ts @@ -1323,14 +1323,11 @@ export function preconfigurePackageInputs( continue; } - // For flags like this, we only want to override the original value if it was set - // as `undefined` in the original object. An explicit true/false value should be - // persisted from the original object to the result after the override process is complete. - if (originalInput.enabled === undefined && preconfiguredInput.enabled !== undefined) { + if (preconfiguredInput.enabled !== undefined) { originalInput.enabled = preconfiguredInput.enabled; } - if (originalInput.keep_enabled === undefined && preconfiguredInput.keep_enabled !== undefined) { + if (preconfiguredInput.keep_enabled !== undefined) { originalInput.keep_enabled = preconfiguredInput.keep_enabled; } @@ -1353,7 +1350,7 @@ export function preconfigurePackageInputs( continue; } - if (originalStream?.enabled === undefined) { + if (stream.enabled !== undefined) { originalStream.enabled = stream.enabled; } From bedad2a0c9e8dfe7760d6c0f30d7c292e9e143aa Mon Sep 17 00:00:00 2001 From: Esteban Beltran Date: Wed, 23 Feb 2022 14:41:09 +0100 Subject: [PATCH 005/137] [Cases] Refactor show/hide cases "add to existing case" modal out of timelines (#126071) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Julia Bardi <90178898+juliaElastic@users.noreply.github.com> Co-authored-by: István Zoltán Szabó Co-authored-by: Joe Reuter Co-authored-by: Tiago Costa Co-authored-by: Cristina Amico Co-authored-by: Aleh Zasypkin Co-authored-by: Gloria Hornero Co-authored-by: Matthew Kime Co-authored-by: Ying Mao Co-authored-by: Maja Grubic Co-authored-by: Lee Drengenberg Co-authored-by: Joe Portner <5295965+jportner@users.noreply.github.com> --- .../all_cases/all_cases_list.test.tsx | 46 ++++++++++- .../components/all_cases/all_cases_list.tsx | 28 ++++++- .../public/components/all_cases/columns.tsx | 36 ++++----- .../all_cases_selector_modal.tsx | 8 +- .../use_cases_add_to_existing_case_modal.tsx | 46 +++++++++++ ..._cases_add_to_existing_case_modal.test.tsx | 81 +++++++++++++++++++ .../cases_context/cases_context_reducer.ts | 26 +++++- .../cases_global_components.test.tsx | 45 ++++++++++- .../cases_context/cases_global_components.tsx | 8 +- .../create/flyout/create_case_flyout.tsx | 5 +- .../use_cases_add_to_new_case_flyout.tsx | 10 +-- .../cases/public/components/create/form.tsx | 12 +-- .../public/components/create/form_context.tsx | 4 +- x-pack/plugins/cases/public/index.tsx | 2 +- .../methods/get_all_cases_selector_modal.tsx | 24 ++++++ x-pack/plugins/cases/public/mocks.ts | 2 + x-pack/plugins/cases/public/plugin.ts | 6 ++ x-pack/plugins/cases/public/types.ts | 27 ++++--- .../cases/add_to_existing_case_button.tsx | 23 +++++- .../timeline/cases/add_to_new_case_button.tsx | 2 + 20 files changed, 383 insertions(+), 58 deletions(-) create mode 100644 x-pack/plugins/cases/public/components/all_cases/selector_modal/use_cases_add_to_existing_case_modal.tsx create mode 100644 x-pack/plugins/cases/public/components/all_cases/selector_modal/uses_cases_add_to_existing_case_modal.test.tsx diff --git a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx index 4f400bb52601b2..8865d67703121a 100644 --- a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx @@ -17,7 +17,7 @@ import { TestProviders } from '../../common/mock'; import { casesStatus, useGetCasesMockState, mockCase, connectorsMock } from '../../containers/mock'; import { StatusAll } from '../../../common/ui/types'; -import { CaseStatuses } from '../../../common/api'; +import { CaseStatuses, CommentType } from '../../../common/api'; import { SECURITY_SOLUTION_OWNER } from '../../../common/constants'; import { getEmptyTagValue } from '../empty_value'; import { useDeleteCases } from '../../containers/use_delete_cases'; @@ -33,7 +33,9 @@ import { triggersActionsUiMock } from '../../../../triggers_actions_ui/public/mo import { registerConnectorsToMockActionRegistry } from '../../common/mock/register_connectors'; import { createStartServicesMock } from '../../common/lib/kibana/kibana_react.mock'; import { waitForComponentToUpdate } from '../../common/test_utils'; +import { usePostComment } from '../../containers/use_post_comment'; +jest.mock('../../containers/use_post_comment'); jest.mock('../../containers/use_bulk_update_case'); jest.mock('../../containers/use_delete_cases'); jest.mock('../../containers/use_get_cases'); @@ -53,6 +55,7 @@ const useUpdateCasesMock = useUpdateCases as jest.Mock; const useGetActionLicenseMock = useGetActionLicense as jest.Mock; const useKibanaMock = useKibana as jest.MockedFunction; const useConnectorsMock = useConnectors as jest.Mock; +const usePostCommentMock = usePostComment as jest.Mock; const mockTriggersActionsUiService = triggersActionsUiMock.createStart(); @@ -79,6 +82,7 @@ describe('AllCasesListGeneric', () => { const fetchCasesStatus = jest.fn(); const onRowClick = jest.fn(); const emptyTag = getEmptyTagValue().props.children; + usePostCommentMock.mockReturnValue({ status: { isLoading: false }, postComment: jest.fn() }); const defaultGetCases = { ...useGetCasesMockState, @@ -492,6 +496,46 @@ describe('AllCasesListGeneric', () => { }); }); + it('should call postComment when a case is selected in isSelectorView=true and has attachments', async () => { + const postCommentMockedValue = { status: { isLoading: false }, postComment: jest.fn() }; + usePostCommentMock.mockReturnValueOnce(postCommentMockedValue); + const wrapper = mount( + + + + ); + wrapper.find('[data-test-subj="cases-table-row-select-1"]').first().simulate('click'); + await waitFor(() => { + expect(postCommentMockedValue.postComment).toHaveBeenCalledWith({ + caseId: '1', + data: { + alertId: 'alert-id-201', + index: 'index-id-1', + owner: 'test', + rule: { + id: 'rule-id-1', + name: 'Awesome myrule', + }, + type: 'alert', + }, + }); + }); + }); + it('should call onRowClick with no cases and isSelectorView=true', async () => { useGetCasesMock.mockReturnValue({ ...defaultGetCases, diff --git a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx index 1ede1a2b736d35..1ceb950ee201d4 100644 --- a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx @@ -16,7 +16,12 @@ import { FilterOptions, SortFieldCase, } from '../../../common/ui/types'; -import { CaseStatuses, CommentRequestAlertType, caseStatuses } from '../../../common/api'; +import { + CaseStatuses, + CommentRequestAlertType, + caseStatuses, + CommentType, +} from '../../../common/api'; import { useGetCases } from '../../containers/use_get_cases'; import { usePostComment } from '../../containers/use_post_comment'; @@ -28,6 +33,7 @@ import { EuiBasicTableOnChange } from './types'; import { CasesTable } from './table'; import { useConnectors } from '../../containers/configure/use_connectors'; import { useCasesContext } from '../cases_context/use_cases_context'; +import { CaseAttachments } from '../../types'; const ProgressLoader = styled(EuiProgress)` ${({ $isShow }: { $isShow: boolean }) => @@ -46,17 +52,22 @@ const getSortField = (field: string): SortFieldCase => field === SortFieldCase.closedAt ? SortFieldCase.closedAt : SortFieldCase.createdAt; export interface AllCasesListProps { + /** + * @deprecated Use the attachments prop instead + */ alertData?: Omit; hiddenStatuses?: CaseStatusWithAllStatus[]; isSelectorView?: boolean; onRowClick?: (theCase?: Case) => void; updateCase?: (newCase: Case) => void; doRefresh?: () => void; + attachments?: CaseAttachments; } export const AllCasesList = React.memo( ({ alertData, + attachments, hiddenStatuses = [], isSelectorView = false, onRowClick, @@ -170,6 +181,19 @@ export const AllCasesList = React.memo( const showActions = userCanCrud && !isSelectorView; + // TODO remove the deprecated alertData field when cleaning up + // code https://github.com/elastic/kibana/issues/123183 + // This code is to support the deprecated alertData prop + const toAttach = useMemo((): CaseAttachments | undefined => { + if (attachments !== undefined || alertData !== undefined) { + const _toAttach = attachments ?? []; + if (alertData !== undefined) { + _toAttach.push({ ...alertData, type: CommentType.alert }); + } + return _toAttach; + } + }, [alertData, attachments]); + const columns = useCasesColumns({ dispatchUpdateCaseProperty, filterStatus: filterOptions.status, @@ -180,7 +204,7 @@ export const AllCasesList = React.memo( userCanCrud, connectors, onRowClick, - alertData, + attachments: toAttach, postComment, updateCase, showSolutionColumn: !hasOwner && availableSolutions.length > 1, diff --git a/x-pack/plugins/cases/public/components/all_cases/columns.tsx b/x-pack/plugins/cases/public/components/all_cases/columns.tsx index 9287d9db7e5b09..36a7a2c240a4f0 100644 --- a/x-pack/plugins/cases/public/components/all_cases/columns.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/columns.tsx @@ -23,12 +23,7 @@ import { RIGHT_ALIGNMENT } from '@elastic/eui/lib/services'; import styled from 'styled-components'; import { Case, DeleteCase } from '../../../common/ui/types'; -import { - CaseStatuses, - CommentType, - CommentRequestAlertType, - ActionConnector, -} from '../../../common/api'; +import { CaseStatuses, ActionConnector } from '../../../common/api'; import { OWNER_INFO } from '../../../common/constants'; import { getEmptyTagValue } from '../empty_value'; import { FormattedRelativePreferenceDate } from '../formatted_date'; @@ -45,6 +40,7 @@ import { TruncatedText } from '../truncated_text'; import { getConnectorIcon } from '../utils'; import { PostComment } from '../../containers/use_post_comment'; import type { CasesOwners } from '../../methods/can_use_cases'; +import { CaseAttachments } from '../../types'; export type CasesColumns = | EuiTableActionsColumnType @@ -76,9 +72,10 @@ export interface GetCasesColumn { userCanCrud: boolean; connectors?: ActionConnector[]; onRowClick?: (theCase: Case) => void; - alertData?: Omit; + attachments?: CaseAttachments; postComment?: (args: PostComment) => Promise; updateCase?: (newCase: Case) => void; + showSolutionColumn?: boolean; } export const useCasesColumns = ({ @@ -91,7 +88,7 @@ export const useCasesColumns = ({ userCanCrud, connectors = [], onRowClick, - alertData, + attachments, postComment, updateCase, showSolutionColumn, @@ -141,21 +138,24 @@ export const useCasesColumns = ({ const assignCaseAction = useCallback( async (theCase: Case) => { - if (alertData != null) { - await postComment?.({ - caseId: theCase.id, - data: { - type: CommentType.alert, - ...alertData, - }, - updateCase, - }); + // TODO currently the API only supports to add a comment at the time + // once the API is updated we should use bulk post comment #124814 + // this operation is intentionally made in sequence + if (attachments !== undefined && attachments.length > 0) { + for (const attachment of attachments) { + await postComment?.({ + caseId: theCase.id, + data: attachment, + }); + } + updateCase?.(theCase); } + if (onRowClick) { onRowClick(theCase); } }, - [alertData, onRowClick, postComment, updateCase] + [attachments, onRowClick, postComment, updateCase] ); useEffect(() => { diff --git a/x-pack/plugins/cases/public/components/all_cases/selector_modal/all_cases_selector_modal.tsx b/x-pack/plugins/cases/public/components/all_cases/selector_modal/all_cases_selector_modal.tsx index d63c56456b07e3..08c99c51593997 100644 --- a/x-pack/plugins/cases/public/components/all_cases/selector_modal/all_cases_selector_modal.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/selector_modal/all_cases_selector_modal.tsx @@ -19,12 +19,17 @@ import { Case, CaseStatusWithAllStatus } from '../../../../common/ui/types'; import { CommentRequestAlertType } from '../../../../common/api'; import * as i18n from '../../../common/translations'; import { AllCasesList } from '../all_cases_list'; +import { CaseAttachments } from '../../../types'; export interface AllCasesSelectorModalProps { + /** + * @deprecated Use the attachments prop instead + */ alertData?: Omit; hiddenStatuses?: CaseStatusWithAllStatus[]; onRowClick: (theCase?: Case) => void; updateCase?: (newCase: Case) => void; onClose?: () => void; + attachments?: CaseAttachments; } const Modal = styled(EuiModal)` @@ -35,7 +40,7 @@ const Modal = styled(EuiModal)` `; export const AllCasesSelectorModal = React.memo( - ({ alertData, hiddenStatuses, onRowClick, updateCase, onClose }) => { + ({ alertData, attachments, hiddenStatuses, onRowClick, updateCase, onClose }) => { const [isModalOpen, setIsModalOpen] = useState(true); const closeModal = useCallback(() => { if (onClose) { @@ -60,6 +65,7 @@ export const AllCasesSelectorModal = React.memo( { + const { dispatch } = useCasesContext(); + const closeModal = useCallback(() => { + dispatch({ + type: CasesContextStoreActionsList.CLOSE_ADD_TO_CASE_MODAL, + }); + }, [dispatch]); + + const openModal = useCallback(() => { + dispatch({ + type: CasesContextStoreActionsList.OPEN_ADD_TO_CASE_MODAL, + payload: { + ...props, + onClose: () => { + closeModal(); + if (props.onClose) { + return props.onClose(); + } + }, + updateCase: async (...args) => { + closeModal(); + if (props.updateCase) { + return props.updateCase(...args); + } + }, + }, + }); + }, [closeModal, dispatch, props]); + return { + open: openModal, + close: closeModal, + }; +}; +export type UseCasesAddToExistingCaseModal = typeof useCasesAddToExistingCaseModal; diff --git a/x-pack/plugins/cases/public/components/all_cases/selector_modal/uses_cases_add_to_existing_case_modal.test.tsx b/x-pack/plugins/cases/public/components/all_cases/selector_modal/uses_cases_add_to_existing_case_modal.test.tsx new file mode 100644 index 00000000000000..954284a670fd2c --- /dev/null +++ b/x-pack/plugins/cases/public/components/all_cases/selector_modal/uses_cases_add_to_existing_case_modal.test.tsx @@ -0,0 +1,81 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable react/display-name */ + +import { renderHook } from '@testing-library/react-hooks'; +import React from 'react'; +import { CasesContext } from '../../cases_context'; +import { CasesContextStoreActionsList } from '../../cases_context/cases_context_reducer'; +import { useCasesAddToExistingCaseModal } from './use_cases_add_to_existing_case_modal'; + +describe('use cases add to existing case modal hook', () => { + const dispatch = jest.fn(); + let wrapper: React.FC; + const defaultParams = () => { + return { onRowClick: jest.fn() }; + }; + beforeEach(() => { + dispatch.mockReset(); + wrapper = ({ children }) => { + return ( + + {children} + + ); + }; + }); + + it('should throw if called outside of a cases context', () => { + const { result } = renderHook(() => { + useCasesAddToExistingCaseModal(defaultParams()); + }); + expect(result.error?.message).toContain( + 'useCasesContext must be used within a CasesProvider and have a defined value' + ); + }); + + it('should dispatch the open action when invoked', () => { + const { result } = renderHook( + () => { + return useCasesAddToExistingCaseModal(defaultParams()); + }, + { wrapper } + ); + result.current.open(); + expect(dispatch).toHaveBeenCalledWith( + expect.objectContaining({ + type: CasesContextStoreActionsList.OPEN_ADD_TO_CASE_MODAL, + }) + ); + }); + + it('should dispatch the close action when invoked', () => { + const { result } = renderHook( + () => { + return useCasesAddToExistingCaseModal(defaultParams()); + }, + { wrapper } + ); + result.current.close(); + expect(dispatch).toHaveBeenCalledWith( + expect.objectContaining({ + type: CasesContextStoreActionsList.CLOSE_ADD_TO_CASE_MODAL, + }) + ); + }); +}); diff --git a/x-pack/plugins/cases/public/components/cases_context/cases_context_reducer.ts b/x-pack/plugins/cases/public/components/cases_context/cases_context_reducer.ts index f9480723232146..53af5d75f2dbee 100644 --- a/x-pack/plugins/cases/public/components/cases_context/cases_context_reducer.ts +++ b/x-pack/plugins/cases/public/components/cases_context/cases_context_reducer.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { assertNever } from '@kbn/std'; +import { AllCasesSelectorModalProps } from '../all_cases/selector_modal'; import { CreateCaseFlyoutProps } from '../create/flyout'; export const getInitialCasesContextState = (): CasesContextState => { @@ -12,6 +14,9 @@ export const getInitialCasesContextState = (): CasesContextState => { createCaseFlyout: { isFlyoutOpen: false, }, + selectCaseModal: { + isModalOpen: false, + }, }; }; @@ -20,18 +25,29 @@ export interface CasesContextState { isFlyoutOpen: boolean; props?: CreateCaseFlyoutProps; }; + selectCaseModal: { + isModalOpen: boolean; + props?: AllCasesSelectorModalProps; + }; } export enum CasesContextStoreActionsList { OPEN_CREATE_CASE_FLYOUT, CLOSE_CREATE_CASE_FLYOUT, + OPEN_ADD_TO_CASE_MODAL, + CLOSE_ADD_TO_CASE_MODAL, } export type CasesContextStoreAction = | { type: CasesContextStoreActionsList.OPEN_CREATE_CASE_FLYOUT; payload: CreateCaseFlyoutProps; } - | { type: CasesContextStoreActionsList.CLOSE_CREATE_CASE_FLYOUT }; + | { type: CasesContextStoreActionsList.CLOSE_CREATE_CASE_FLYOUT } + | { + type: CasesContextStoreActionsList.OPEN_ADD_TO_CASE_MODAL; + payload: AllCasesSelectorModalProps; + } + | { type: CasesContextStoreActionsList.CLOSE_ADD_TO_CASE_MODAL }; export const casesContextReducer: React.Reducer = ( state: CasesContextState, @@ -44,7 +60,13 @@ export const casesContextReducer: React.Reducer { let appMock: AppMockRenderer; @@ -25,6 +31,7 @@ describe('Cases context UI', () => { describe('create case flyout', () => { it('should render the create case flyout when isFlyoutOpen is true', async () => { const state = { + ...getInitialCasesContextState(), createCaseFlyout: { isFlyoutOpen: true, props: { @@ -35,8 +42,10 @@ describe('Cases context UI', () => { appMock.render(); expect(getCreateCaseFlyoutLazyNoProviderMock).toHaveBeenCalledWith({ attachments: [] }); }); + it('should not render the create case flyout when isFlyoutOpen is false', async () => { const state = { + ...getInitialCasesContextState(), createCaseFlyout: { isFlyoutOpen: false, }, @@ -45,4 +54,36 @@ describe('Cases context UI', () => { expect(getCreateCaseFlyoutLazyNoProviderMock).not.toHaveBeenCalled(); }); }); + + describe('select case modal', () => { + it('should render the select case modal when isModalOpen is true', async () => { + const onRowClick = jest.fn(); + const state = { + ...getInitialCasesContextState(), + selectCaseModal: { + isModalOpen: true, + props: { + attachments: [], + onRowClick, + }, + }, + }; + appMock.render(); + expect(getAllCasesSelectorModalNoProviderLazyMock).toHaveBeenCalledWith({ + attachments: [], + onRowClick, + }); + }); + + it('should not render the select case modal when isModalOpen is false', async () => { + const state = { + ...getInitialCasesContextState(), + selectCaseModal: { + isModalOpen: false, + }, + }; + appMock.render(); + expect(getAllCasesSelectorModalNoProviderLazyMock).toHaveBeenCalled(); + }); + }); }); diff --git a/x-pack/plugins/cases/public/components/cases_context/cases_global_components.tsx b/x-pack/plugins/cases/public/components/cases_context/cases_global_components.tsx index 42ff36e201df47..36891b34a8f7a1 100644 --- a/x-pack/plugins/cases/public/components/cases_context/cases_global_components.tsx +++ b/x-pack/plugins/cases/public/components/cases_context/cases_global_components.tsx @@ -6,7 +6,10 @@ */ import React from 'react'; -import { getCreateCaseFlyoutLazyNoProvider } from '../../methods'; +import { + getAllCasesSelectorModalNoProviderLazy, + getCreateCaseFlyoutLazyNoProvider, +} from '../../methods'; import { CasesContextState } from './cases_context_reducer'; export const CasesGlobalComponents = React.memo(({ state }: { state: CasesContextState }) => { @@ -15,6 +18,9 @@ export const CasesGlobalComponents = React.memo(({ state }: { state: CasesContex {state.createCaseFlyout.isFlyoutOpen && state.createCaseFlyout.props !== undefined ? getCreateCaseFlyoutLazyNoProvider(state.createCaseFlyout.props) : null} + {state.selectCaseModal.isModalOpen && state.selectCaseModal.props !== undefined + ? getAllCasesSelectorModalNoProviderLazy(state.selectCaseModal.props) + : null} ); }); diff --git a/x-pack/plugins/cases/public/components/create/flyout/create_case_flyout.tsx b/x-pack/plugins/cases/public/components/create/flyout/create_case_flyout.tsx index c40dfc98513d82..e2448bf8fbf8a3 100644 --- a/x-pack/plugins/cases/public/components/create/flyout/create_case_flyout.tsx +++ b/x-pack/plugins/cases/public/components/create/flyout/create_case_flyout.tsx @@ -11,14 +11,15 @@ import { EuiFlyout, EuiFlyoutHeader, EuiTitle, EuiFlyoutBody } from '@elastic/eu import * as i18n from '../translations'; import { Case } from '../../../../common/ui/types'; -import { CreateCaseForm, CreateCaseAttachment } from '../form'; +import { CreateCaseForm } from '../form'; import { UsePostComment } from '../../../containers/use_post_comment'; +import { CaseAttachments } from '../../../types'; export interface CreateCaseFlyoutProps { afterCaseCreated?: (theCase: Case, postComment: UsePostComment['postComment']) => Promise; onClose?: () => void; onSuccess?: (theCase: Case) => Promise; - attachments?: CreateCaseAttachment; + attachments?: CaseAttachments; } const StyledFlyout = styled(EuiFlyout)` diff --git a/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.tsx b/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.tsx index e9514ee582d992..e4ae4d72f48dab 100644 --- a/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.tsx +++ b/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.tsx @@ -11,16 +11,16 @@ import { useCasesContext } from '../../cases_context/use_cases_context'; import { CreateCaseFlyoutProps } from './create_case_flyout'; export const useCasesAddToNewCaseFlyout = (props: CreateCaseFlyoutProps) => { - const context = useCasesContext(); + const { dispatch } = useCasesContext(); const closeFlyout = useCallback(() => { - context.dispatch({ + dispatch({ type: CasesContextStoreActionsList.CLOSE_CREATE_CASE_FLYOUT, }); - }, [context]); + }, [dispatch]); const openFlyout = useCallback(() => { - context.dispatch({ + dispatch({ type: CasesContextStoreActionsList.OPEN_CREATE_CASE_FLYOUT, payload: { ...props, @@ -38,7 +38,7 @@ export const useCasesAddToNewCaseFlyout = (props: CreateCaseFlyoutProps) => { }, }, }); - }, [closeFlyout, context, props]); + }, [closeFlyout, dispatch, props]); return { open: openFlyout, close: closeFlyout, diff --git a/x-pack/plugins/cases/public/components/create/form.tsx b/x-pack/plugins/cases/public/components/create/form.tsx index c4646ff7f7c02e..c353ddc08a3102 100644 --- a/x-pack/plugins/cases/public/components/create/form.tsx +++ b/x-pack/plugins/cases/public/components/create/form.tsx @@ -23,11 +23,7 @@ import { Tags } from './tags'; import { Connector } from './connector'; import * as i18n from './translations'; import { SyncAlertsToggle } from './sync_alerts_toggle'; -import { - ActionConnector, - CommentRequestUserType, - CommentRequestAlertType, -} from '../../../common/api'; +import { ActionConnector } from '../../../common/api'; import { Case } from '../../containers/types'; import { CasesTimelineIntegration, CasesTimelineIntegrationProvider } from '../timeline_context'; import { InsertTimeline } from '../insert_timeline'; @@ -38,6 +34,7 @@ import { useCasesFeatures } from '../cases_context/use_cases_features'; import { CreateCaseOwnerSelector } from './owner_selector'; import { useCasesContext } from '../cases_context/use_cases_context'; import { useAvailableCasesOwners } from '../app/use_available_owners'; +import { CaseAttachments } from '../../types'; interface ContainerProps { big?: boolean; @@ -55,9 +52,6 @@ const MySpinner = styled(EuiLoadingSpinner)` left: 50%; z-index: 99; `; -export type SupportedCreateCaseAttachment = CommentRequestAlertType | CommentRequestUserType; -export type CreateCaseAttachment = SupportedCreateCaseAttachment[]; -export type CaseAttachments = SupportedCreateCaseAttachment[]; export interface CreateCaseFormFieldsProps { connectors: ActionConnector[]; @@ -69,7 +63,7 @@ export interface CreateCaseFormProps extends Pick Promise; afterCaseCreated?: (theCase: Case, postComment: UsePostComment['postComment']) => Promise; timelineIntegration?: CasesTimelineIntegration; - attachments?: CreateCaseAttachment; + attachments?: CaseAttachments; } const empty: ActionConnector[] = []; diff --git a/x-pack/plugins/cases/public/components/create/form_context.tsx b/x-pack/plugins/cases/public/components/create/form_context.tsx index 499996942f6b9d..3576ea8377731b 100644 --- a/x-pack/plugins/cases/public/components/create/form_context.tsx +++ b/x-pack/plugins/cases/public/components/create/form_context.tsx @@ -19,7 +19,7 @@ import { UsePostComment, usePostComment } from '../../containers/use_post_commen import { useCasesContext } from '../cases_context/use_cases_context'; import { useCasesFeatures } from '../cases_context/use_cases_features'; import { getConnectorById } from '../utils'; -import { CreateCaseAttachment } from './form'; +import { CaseAttachments } from '../../types'; const initialCaseValue: FormProps = { description: '', @@ -35,7 +35,7 @@ interface Props { afterCaseCreated?: (theCase: Case, postComment: UsePostComment['postComment']) => Promise; children?: JSX.Element | JSX.Element[]; onSuccess?: (theCase: Case) => Promise; - attachments?: CreateCaseAttachment; + attachments?: CaseAttachments; } export const FormContext: React.FC = ({ diff --git a/x-pack/plugins/cases/public/index.tsx b/x-pack/plugins/cases/public/index.tsx index be23b9a46893b9..13b9c64b13475d 100644 --- a/x-pack/plugins/cases/public/index.tsx +++ b/x-pack/plugins/cases/public/index.tsx @@ -19,7 +19,7 @@ export type { GetCreateCaseFlyoutProps } from './methods/get_create_case_flyout' export type { GetAllCasesSelectorModalProps } from './methods/get_all_cases_selector_modal'; export type { GetRecentCasesProps } from './methods/get_recent_cases'; -export type { CaseAttachments } from './components/create/form'; +export type { CaseAttachments } from './types'; export type { ICasesDeepLinkId } from './common/navigation'; export { diff --git a/x-pack/plugins/cases/public/methods/get_all_cases_selector_modal.tsx b/x-pack/plugins/cases/public/methods/get_all_cases_selector_modal.tsx index 0710dcd30508b9..447725cab27b37 100644 --- a/x-pack/plugins/cases/public/methods/get_all_cases_selector_modal.tsx +++ b/x-pack/plugins/cases/public/methods/get_all_cases_selector_modal.tsx @@ -36,3 +36,27 @@ export const getAllCasesSelectorModalLazy = ({ ); + +/** + * Same as getAllCasesSelectorModalLazy but without injecting the + * cases provider. to be further refactored https://github.com/elastic/kibana/issues/123183 + */ +export const getAllCasesSelectorModalNoProviderLazy = ({ + alertData, + attachments, + hiddenStatuses, + onRowClick, + updateCase, + onClose, +}: AllCasesSelectorModalProps) => ( + }> + + +); diff --git a/x-pack/plugins/cases/public/mocks.ts b/x-pack/plugins/cases/public/mocks.ts index 7c89bb1ddc2f95..f7f80170a87750 100644 --- a/x-pack/plugins/cases/public/mocks.ts +++ b/x-pack/plugins/cases/public/mocks.ts @@ -12,11 +12,13 @@ const createStartContract = (): jest.Mocked => ({ getCases: jest.fn(), getCasesContext: jest.fn(), getAllCasesSelectorModal: jest.fn(), + getAllCasesSelectorModalNoProvider: jest.fn(), getCreateCaseFlyout: jest.fn(), getRecentCases: jest.fn(), getCreateCaseFlyoutNoProvider: jest.fn(), hooks: { getUseCasesAddToNewCaseFlyout: jest.fn(), + getUseCasesAddToExistingCaseModal: jest.fn(), }, }); diff --git a/x-pack/plugins/cases/public/plugin.ts b/x-pack/plugins/cases/public/plugin.ts index acf51c8380f658..9dbc6ea35125ad 100644 --- a/x-pack/plugins/cases/public/plugin.ts +++ b/x-pack/plugins/cases/public/plugin.ts @@ -15,10 +15,12 @@ import { getCreateCaseFlyoutLazy, canUseCases, getCreateCaseFlyoutLazyNoProvider, + getAllCasesSelectorModalNoProviderLazy, } from './methods'; import { CasesUiConfigType } from '../common/ui/types'; import { getCasesContextLazy } from './methods/get_cases_context'; import { useCasesAddToNewCaseFlyout } from './components/create/flyout/use_cases_add_to_new_case_flyout'; +import { useCasesAddToExistingCaseModal } from './components/all_cases/selector_modal/use_cases_add_to_existing_case_modal'; /** * @public @@ -42,9 +44,13 @@ export class CasesUiPlugin implements Plugin ReactElement; + getAllCasesSelectorModalNoProvider: ( + props: GetAllCasesSelectorModalProps + ) => ReactElement; /** * Flyout with the form to create a case for the owner * @param props GetCreateCaseFlyoutProps @@ -95,5 +98,9 @@ export interface CasesUiStart { getRecentCases: (props: GetRecentCasesProps) => ReactElement; hooks: { getUseCasesAddToNewCaseFlyout: UseCasesAddToNewCaseFlyout; + getUseCasesAddToExistingCaseModal: UseCasesAddToExistingCaseModal; }; } + +export type SupportedCaseAttachment = CommentRequestAlertType | CommentRequestUserType; +export type CaseAttachments = SupportedCaseAttachment[]; diff --git a/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_existing_case_button.tsx b/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_existing_case_button.tsx index a9238c95ee9161..4d19a890968820 100644 --- a/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_existing_case_button.tsx +++ b/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_existing_case_button.tsx @@ -8,6 +8,8 @@ import React, { memo } from 'react'; import { EuiContextMenuItem } from '@elastic/eui'; +import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; +import { TimelinesStartServices } from '../../../../types'; import { useAddToCase } from '../../../../hooks/use_add_to_case'; import { AddToCaseActionProps } from './add_to_case_action'; import * as i18n from './translations'; @@ -25,7 +27,7 @@ const AddToCaseActionButtonComponent: React.FC = ({ owner, onClose, }) => { - const { addExistingCaseClick, isDisabled, userCanCrud } = useAddToCase({ + const { onCaseSuccess, onCaseClicked, isDisabled, userCanCrud, caseAttachments } = useAddToCase({ event, useInsertTimeline, casePermissions, @@ -33,13 +35,30 @@ const AddToCaseActionButtonComponent: React.FC = ({ owner, onClose, }); + const { cases } = useKibana().services; + const addToCaseModal = cases.hooks.getUseCasesAddToExistingCaseModal({ + attachments: caseAttachments, + updateCase: onCaseSuccess, + onRowClick: onCaseClicked, + }); + + // TODO To be further refactored and moved to cases plugins + // https://github.com/elastic/kibana/issues/123183 + const handleClick = () => { + // close the popover + if (onClose) { + onClose(); + } + addToCaseModal.open(); + }; + return ( <> {userCanCrud && ( = ({ onSuccess: onCaseSuccess, }); + // TODO To be further refactored and moved to cases plugins + // https://github.com/elastic/kibana/issues/123183 const handleClick = () => { // close the popover if (onClose) { From cc8b949b628dc260523ee49d53250d3201bf7bc5 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 23 Feb 2022 14:03:21 +0000 Subject: [PATCH 006/137] chore(NA): fixing APM translations (#126243) --- x-pack/plugins/translations/translations/ja-JP.json | 1 - x-pack/plugins/translations/translations/zh-CN.json | 1 - 2 files changed, 2 deletions(-) diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 437980e71b488e..881984cc719eb1 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -7808,7 +7808,6 @@ "xpack.apm.tutorial.windowsServerInstructions.textPre": "1.[ダウンロードページ]({downloadPageLink})から APM Server Windows zip ファイルをダウンロードします。\n2.zip ファイルの内容を {zipFileExtractFolder} に抽出します。\n3.「{apmServerDirectory} ディレクトリの名前を「APM-Server」に変更します。\n4.管理者としてPowerShellプロンプトを開きます(PowerShellアイコンを右クリックして「管理者として実行」を選択します)。Windows XPをご使用の場合、PowerShellのダウンロードとインストールが必要な場合があります。\n5.PowerShell プロンプトで次のコマンドを実行し、APM Server を Windows サービスとしてインストールします。", "xpack.apm.unitLabel": "単位を選択", "xpack.apm.ux.overview.agent.description": "APMエージェントを使用して、APMデータを収集します。多数の一般的な言語では、エージェントを使用することで処理が簡単になっています。", - "xpack.apm.ux.overview.agent.title": "APM統合を追加", "xpack.apm.views.dependencies.title": "依存関係", "xpack.apm.views.dependenciesInventory.title": "依存関係", "xpack.apm.views.errors.title": "エラー", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index bfe694b73bc5ad..197c982b01fd91 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -7881,7 +7881,6 @@ "xpack.apm.unitLabel": "选择单位", "xpack.apm.unsavedChanges": "{unsavedChangesCount, plural, =0{0 个未保存更改} one {1 个未保存更改} other {# 个未保存更改}} ", "xpack.apm.ux.overview.agent.description": "使用 APM 代理来收集 APM 数据。我们的代理支持许多流行语言,可以轻松使用。", - "xpack.apm.ux.overview.agent.title": "添加 APM 集成", "xpack.apm.views.dependencies.title": "依赖项", "xpack.apm.views.dependenciesInventory.title": "依赖项", "xpack.apm.views.errors.title": "错误", From 6ee93e9470e1c7b45de0c1e9cab39c0cf9d11761 Mon Sep 17 00:00:00 2001 From: Vadim Kibana <82822460+vadimkibana@users.noreply.github.com> Date: Wed, 23 Feb 2022 15:23:04 +0100 Subject: [PATCH 007/137] URL service docs (#125697) * improve sharing menu docs * improve locator docs * improve locator docs * add short URL docs * add tsconfig * update plugin list * add share plugin readme to dev docs * fix linter errors * update docs list Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- docs/developer/plugin-list.asciidoc | 4 +- examples/share_examples/README.md | 3 + examples/share_examples/kibana.json | 14 ++ examples/share_examples/public/index.ts | 11 + examples/share_examples/public/plugin.tsx | 42 ++++ examples/share_examples/tsconfig.json | 18 ++ nav-kibana-dev.docnav.json | 3 +- src/plugins/share/README.md | 23 -- src/plugins/share/README.mdx | 243 ++++++++++++++++++++++ 9 files changed, 335 insertions(+), 26 deletions(-) create mode 100644 examples/share_examples/README.md create mode 100644 examples/share_examples/kibana.json create mode 100644 examples/share_examples/public/index.ts create mode 100644 examples/share_examples/public/plugin.tsx create mode 100644 examples/share_examples/tsconfig.json delete mode 100644 src/plugins/share/README.md create mode 100644 src/plugins/share/README.mdx diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index ea7ea246908861..c79189c1423dc7 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -238,9 +238,9 @@ oss plugins. |The service exposed by this plugin informs consumers whether they should optimize for non-interactivity. In this way plugins can avoid loading unnecessary code, data or other services. -|{kib-repo}blob/{branch}/src/plugins/share/README.md[share] +|{kib-repo}blob/{branch}/src/plugins/share/README.mdx[share] |The share plugin contains various utilities for displaying sharing context menu, -generating deep links to other apps, and creating short URLs. +generating deep links to other apps using locators, and creating short URLs. |{kib-repo}blob/{branch}/src/plugins/shared_ux/README.md[sharedUX] diff --git a/examples/share_examples/README.md b/examples/share_examples/README.md new file mode 100644 index 00000000000000..827e8e812668dd --- /dev/null +++ b/examples/share_examples/README.md @@ -0,0 +1,3 @@ +# Share plugin examples + +Small demos of share plugin usage. diff --git a/examples/share_examples/kibana.json b/examples/share_examples/kibana.json new file mode 100644 index 00000000000000..ac2157ad97b28b --- /dev/null +++ b/examples/share_examples/kibana.json @@ -0,0 +1,14 @@ +{ + "id": "shareExamples", + "kibanaVersion": "kibana", + "version": "0.0.1", + "server": false, + "ui": true, + "owner": { + "name": "App Services", + "githubTeam": "kibana-app-services" + }, + "description": "Small demos of share plugin usage", + "requiredPlugins": ["share"], + "optionalPlugins": [] +} diff --git a/examples/share_examples/public/index.ts b/examples/share_examples/public/index.ts new file mode 100644 index 00000000000000..2dba5ac17c29f3 --- /dev/null +++ b/examples/share_examples/public/index.ts @@ -0,0 +1,11 @@ +/* + * 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 { ShareDemoPlugin } from './plugin'; + +export const plugin = () => new ShareDemoPlugin(); diff --git a/examples/share_examples/public/plugin.tsx b/examples/share_examples/public/plugin.tsx new file mode 100644 index 00000000000000..24ca45e8f5f45c --- /dev/null +++ b/examples/share_examples/public/plugin.tsx @@ -0,0 +1,42 @@ +/* + * 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 { SharePluginStart, SharePluginSetup } from '../../../src/plugins/share/public'; +import { Plugin, CoreSetup, CoreStart } from '../../../src/core/public'; + +interface SetupDeps { + share: SharePluginSetup; +} + +interface StartDeps { + share: SharePluginStart; +} + +export class ShareDemoPlugin implements Plugin { + public setup(core: CoreSetup, { share }: SetupDeps) { + share.register({ + id: 'demo', + getShareMenuItems: (context) => [ + { + panel: { + id: 'demo', + title: 'Panel title', + content: 'Panel content', + }, + shareMenuItem: { + name: 'Demo list item (from share_example plugin)', + }, + }, + ], + }); + } + + public start(core: CoreStart, { share }: StartDeps) {} + + public stop() {} +} diff --git a/examples/share_examples/tsconfig.json b/examples/share_examples/tsconfig.json new file mode 100644 index 00000000000000..5010ad5a5fe0f2 --- /dev/null +++ b/examples/share_examples/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./target/types" + }, + "include": [ + "index.ts", + "public/**/*.ts", + "public/**/*.tsx", + "server/**/*.ts", + "../../typings/**/*" + ], + "exclude": [], + "references": [ + { "path": "../../src/core/tsconfig.json" }, + { "path": "../../src/plugins/share/tsconfig.json" }, + ] +} diff --git a/nav-kibana-dev.docnav.json b/nav-kibana-dev.docnav.json index a946cfa0dd56b9..7a1e9ea323ed88 100644 --- a/nav-kibana-dev.docnav.json +++ b/nav-kibana-dev.docnav.json @@ -48,7 +48,8 @@ }, { "id": "kibDevTutorialCI" }, { "id": "kibDevTutorialServerEndpoint" }, - { "id": "kibDevTutorialAdvancedSettings"} + { "id": "kibDevTutorialAdvancedSettings"}, + { "id": "kibDevSharePluginReadme"} ] }, { diff --git a/src/plugins/share/README.md b/src/plugins/share/README.md deleted file mode 100644 index b3ce5e88322442..00000000000000 --- a/src/plugins/share/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Share plugin - -The `share` plugin contains various utilities for displaying sharing context menu, -generating deep links to other apps, and creating short URLs. - - -## Sharing context menu - -You can register an item into sharing context menu (which is displayed in -Dahsboard, Discover, and Visuzlize apps). - -### Example registration - -```ts -import { ShareContext, ShareMenuItem } from 'src/plugins/share/public'; - -plugins.share.register({ - id: 'MY_MENU', - getShareMenuItems: (context: ShareContext): ShareMenuItem[] => { - // ... - }, -}; -``` diff --git a/src/plugins/share/README.mdx b/src/plugins/share/README.mdx new file mode 100644 index 00000000000000..1a1e2e721c2abd --- /dev/null +++ b/src/plugins/share/README.mdx @@ -0,0 +1,243 @@ +--- +id: kibDevSharePluginReadme +slug: /kibana-dev-docs/tutorials/share +title: Kibana share Plugin +summary: Introduction to Kibana "share" plugin services (sharing popup menu, URL locators, and short URLs). +date: 2022-02-21 +tags: ['kibana', 'onboarding', 'dev', 'tutorials', 'share', 'url', 'short-url', 'locator'] +--- + +# Share plugin + +The `share` plugin contains various utilities for displaying sharing context menu, +generating deep links to other apps using *locators*, and creating short URLs. + + +## Sharing context menu + +You can register an item into sharing context menu (which is displayed in +Dashboard, Discover, and Visualize apps). + +### Example registration + +```ts +import { ShareContext, ShareMenuItem } from 'src/plugins/share/public'; + +plugins.share.register({ + id: 'demo', + getShareMenuItems: (context) => [ + { + panel: { + id: 'demo', + title: 'Panel title', + content: 'Panel content', + }, + shareMenuItem: { + name: 'Demo list item (from share_example plugin)', + }, + } + ], +}); +``` + +Now the "Demo list item" will appear under the "Share" menu in Discover and +Dashboard applications. + + +## Locators + +*Locators* are entities which given some *parameters* can navigate Kibana user +to some deep link in Kibana or return back a full formatted URL. + +Kibana apps create locators and expose them from their plugin contracts. Then +other plugins can use those locators to navigate deeply into Kibana applications. + +Locators work, both, on the server and browser. However, it is the responsibility +of the app, which provides the locator, to register and expose the same locator +on the server and browser. + +### Creating a locator for your Kibana app + +A Kibana app can create one or more *locators*, which given an object of *parameters* +knows how to specify return a *Kibana location* 3-tuple. The 3-tuple consists of: +(1) the app ID; (2) a path within the app; (3) a serializable *location state* object. +The Kibana location 3-tuple is used to navigate the Kibana app in SPA (single page app) +way—without reloading the page. + +```ts +import type { SerializableRecord } from '@kbn/utility-types'; +import { LocatorDefinition, LocatorPublic } from 'src/plugins/share/public'; + +export interface HelloLocatorParams extends SerializableRecord { + name: string; +} + +export type HelloLocator = LocatorPublic; + +export class HelloLocatorDefinition implements LocatorDefinition { + public readonly id = 'HELLO_LOCATOR'; + + public readonly getLocation = async ({ name }: HelloLocatorParams) => { + return { + app: 'sampleAppId', + path: `/hello?name=${encodeURIComponent(name)}`, + state: {}, + }; + }; +} +``` + +Once your locator has been defined, you need to register it in the central locator +registry with the `share` plugin: + +```ts +const locator = plugins.share.url.locators.create(new HelloLocatorDefinition()); +``` + +You can then return the `locator` reference from your plugin contract fro consumption +by other plugins: + +```ts +class MyPlugin { + setup(core, plugins) { + const locator = plugins.share.url.locators.create(new HelloLocatorDefinition()); + + return { + locator, + }; + } +} +``` + +### Using locator of another app + +First you need to get access of the locator you want to use. Best way to do it is +by acquiring it from the plugin contract of the app you are interested to navigate +to. For example, you can get hold of the Dashboard app locator from the `dashboard` +plugin contract: + +```ts +class MyPlugin { + setup(core, plugins) { + const dashboardLocator = plugins.dashboard.locator; + } +} +``` + +Alternatively, you can also acquire any locator from the central registry in +the `share` plugin using the ID of the locator. This is useful, if you run into +circular dependency issues, however, it is better to acquire the locator directly +from the plugin contract as above, which makes dependencies explicit. + +You can use the `.get()` method on the locators service to acquire the locator +from the central registry: + +```ts +class MyPlugin { + setup(core, plugins) { + const dashboardLocator = plugins.share.url.locators.get('DASHBOARD_APP_LOCATOR'); + } +} +``` + +Once have have a reference to the desired locator object, you can use that locator +to navigate to various pages in Kibana or use it to build a URL string. + +The best way to use the locator is to use its `.navigateSync()` method to navigate +in SPA way (without page reload): + +```ts +dashboardLocator.navigateSync({ dashboardId: '123' }); +``` + +The above will immediately navigate to the Dashboard app, to the dashboard with +ID `123`. + +Another useful feature of locators is their ability to format a URL string of the +destination. The best way to do it is using the `.getRedirectUrl()` method +of locators. + +```ts +const url = dashboardLocator.getRedirectUrl({ dashboardId: '123' }); +``` + +The above will generate a *redirect* URL, which also preserves the serializable +*location state* object, which will be passed to the destination app. + +Alternatively, for compatibility with old URL generators, you can also use the +`.getUrl()` method of locators to generate just the final app destination URL +string without the location state object. + +```ts +const url = await dashboardLocator.getUrl({ dashboardId: '123' }); +``` + +Note, in this process you lose the location state object, which effectively +makes the final destination incorrect, if the app makes use of the location +state object. + +For more ways working with locators see the `LocatorPublic` interface. + + +## Short URLs + +The `share` plugin has Short URL service, which allows to create Kibana short +URLs. The service with the same interface is available, both, on the server +and browser. + +When creating a short URL it is possible to use a custom short *slug*, used +to dereference the short URL. Also, a human-readable short slug can be +automatically generated. + +First, to be able to create a short URL, one needs to get hold of a locator +reference, for which the short URL will be created. (See above section on +*Locators* for more info.) Lets say we want to create a short URL for the +Dashboard application, for that, we first acquire reference to the dashboard +locator: + +```ts +class MyPlugin { + setup(core, plugins) { + const dashboardLocator = plugins.dashboard.locator; + } +} +``` + +Now you can create a short URL using your locator and the short URL service: + +```ts +const shortUrls = plugins.share.url.shortUrls.get(null); +const url = await shortUrls.create({ + locator, + params: { + dashboardId: '123', + } +}); +``` + +You can make the short URL slug human-readable by specifying the +`humanReadableSlug` flag: + +```ts +const url = await shortUrls.create({ + locator, + params: { + dashboardId: '123', + }, + humanReadableSlug: true, +}); +``` + +Or you can manually specify the slug for the short URL using the `slug` option: + +```ts +const url = await shortUrls.create({ + locator, + params: { + dashboardId: '123', + }, + slug: 'my-slug', +}); +``` + +To resolve the short URL, navigate to `/r/s/` in the browser. From d053a7f0261eeab470fbef2af05ab840b43105c1 Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Wed, 23 Feb 2022 15:27:26 +0100 Subject: [PATCH 008/137] Stop using `aborted` event for `KibanaRequest.events.aborted$` (#126184) * Stop using `aborted` event for `KibanaRequest.events.aborted$` * add another test, just in case * use a single `fromEvent` * add replay effect to aborted$ * improve impl * remove useless bottom-stream replay * yup, that's simpler --- .../http/integration_tests/request.test.ts | 71 +++++++++++++++++++ src/core/server/http/router/request.ts | 17 +++-- 2 files changed, 81 insertions(+), 7 deletions(-) diff --git a/src/core/server/http/integration_tests/request.test.ts b/src/core/server/http/integration_tests/request.test.ts index a2560c2c39fade..83dffdc73468e8 100644 --- a/src/core/server/http/integration_tests/request.test.ts +++ b/src/core/server/http/integration_tests/request.test.ts @@ -198,6 +198,42 @@ describe('KibanaRequest', () => { expect(nextSpy).toHaveBeenCalledTimes(1); }); + it('emits once and completes when request aborted after the payload has been consumed', async () => { + expect.assertions(1); + const { server: innerServer, createRouter } = await server.setup(setupDeps); + const router = createRouter('/'); + + const nextSpy = jest.fn(); + + const done = new Promise((resolve) => { + router.post( + { path: '/', validate: { body: schema.any() } }, + async (context, request, res) => { + request.events.aborted$.subscribe({ + next: nextSpy, + complete: resolve, + }); + + // prevents the server to respond + await delay(30000); + return res.ok({ body: 'ok' }); + } + ); + }); + + await server.start(); + + const incomingRequest = supertest(innerServer.listener) + .post('/') + .send({ hello: 'dolly' }) + // end required to send request + .end(); + + setTimeout(() => incomingRequest.abort(), 50); + await done; + expect(nextSpy).toHaveBeenCalledTimes(1); + }); + it('completes & does not emit when request handled', async () => { const { server: innerServer, createRouter } = await server.setup(setupDeps); const router = createRouter('/'); @@ -336,6 +372,41 @@ describe('KibanaRequest', () => { await done; expect(nextSpy).toHaveBeenCalledTimes(1); }); + + it('emits once and completes when response is aborted after the payload has been consumed', async () => { + expect.assertions(2); + const { server: innerServer, createRouter } = await server.setup(setupDeps); + const router = createRouter('/'); + + const nextSpy = jest.fn(); + + const done = new Promise((resolve) => { + router.post( + { path: '/', validate: { body: schema.any() } }, + async (context, req, res) => { + req.events.completed$.subscribe({ + next: nextSpy, + complete: resolve, + }); + + expect(nextSpy).not.toHaveBeenCalled(); + await delay(30000); + return res.ok({ body: 'ok' }); + } + ); + }); + + await server.start(); + + const incomingRequest = supertest(innerServer.listener) + .post('/') + .send({ foo: 'bar' }) + // end required to send request + .end(); + setTimeout(() => incomingRequest.abort(), 50); + await done; + expect(nextSpy).toHaveBeenCalledTimes(1); + }); }); }); diff --git a/src/core/server/http/router/request.ts b/src/core/server/http/router/request.ts index e53a30124a4205..5a914fb800da9c 100644 --- a/src/core/server/http/router/request.ts +++ b/src/core/server/http/router/request.ts @@ -9,8 +9,8 @@ import { URL } from 'url'; import uuid from 'uuid'; import { Request, RouteOptionsApp, RequestApplicationState, RouteOptions } from '@hapi/hapi'; -import { Observable, fromEvent, merge } from 'rxjs'; -import { shareReplay, first, takeUntil } from 'rxjs/operators'; +import { Observable, fromEvent } from 'rxjs'; +import { shareReplay, first, filter } from 'rxjs/operators'; import { RecursiveReadonly } from '@kbn/utility-types'; import { deepFreeze } from '@kbn/std'; @@ -127,6 +127,7 @@ export class KibanaRequest< const body = routeValidator.getBody(req.payload, 'request body'); return { query, params, body }; } + /** * A identifier to identify this request. * @@ -215,11 +216,9 @@ export class KibanaRequest< } private getEvents(request: Request): KibanaRequestEvents { - // the response is completed, or its underlying connection was terminated prematurely - const finish$ = fromEvent(request.raw.res, 'close').pipe(shareReplay(1), first()); - - const aborted$ = fromEvent(request.raw.req, 'aborted').pipe(first(), takeUntil(finish$)); - const completed$ = merge(finish$, aborted$).pipe(shareReplay(1), first()); + const completed$ = fromEvent(request.raw.res, 'close').pipe(shareReplay(1), first()); + // the response's underlying connection was terminated prematurely + const aborted$ = completed$.pipe(filter(() => !isCompleted(request))); return { aborted$, @@ -331,3 +330,7 @@ function isRequest(request: any): request is Request { export function isRealRequest(request: unknown): request is KibanaRequest | Request { return isKibanaRequest(request) || isRequest(request); } + +function isCompleted(request: Request) { + return request.raw.res.writableFinished; +} From c45358df91a8be7933ee8470ce0e5d0898df9d09 Mon Sep 17 00:00:00 2001 From: Dzmitry Lemechko Date: Wed, 23 Feb 2022 17:31:33 +0300 Subject: [PATCH 009/137] update apm config for load testing (#126227) --- x-pack/test/load/config.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/x-pack/test/load/config.ts b/x-pack/test/load/config.ts index 41899eaece0bc5..a243635e5ea4c0 100644 --- a/x-pack/test/load/config.ts +++ b/x-pack/test/load/config.ts @@ -47,13 +47,11 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { env: { ELASTIC_APM_ACTIVE: process.env.ELASTIC_APM_ACTIVE, ELASTIC_APM_CENTRAL_CONFIG: false, - ELASTIC_APM_TRANSACTION_SAMPLE_RATE: '0.1', + ELASTIC_APM_TRANSACTION_SAMPLE_RATE: '1', ELASTIC_APM_BREAKDOWN_METRICS: false, ELASTIC_APM_CAPTURE_SPAN_STACK_TRACES: false, ELASTIC_APM_METRICS_INTERVAL: '120s', ELASTIC_APM_MAX_QUEUE_SIZE: 20480, - ELASTIC_SANITIZE_FIELD_NAMES: - 'password,passwd,pwd,secret,*key,*token*,*session*,*credit*,*card*,*auth*,set-cookie,pw,pass,connect.sid', ELASTIC_APM_ENVIRONMENT: process.env.CI ? 'ci' : 'development', ELASTIC_APM_SERVER_URL: APM_SERVER_URL, ELASTIC_APM_SECRET_TOKEN: APM_PUBLIC_TOKEN, From f493191beae310d83dec2b610cbd5c0b12c406bf Mon Sep 17 00:00:00 2001 From: Stef Nestor Date: Wed, 23 Feb 2022 07:43:30 -0700 Subject: [PATCH 010/137] [+DOC] Add Session Settings Link (#126026) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [+DOC] Add Session Settings Link 👋🏼 hiya, team! Can we add a link to [Security Sessions & Cookies](https://www.elastic.co/guide/en/kibana/current/security-settings-kb.html#security-session-and-cookie-settings) from [Session Management](https://www.elastic.co/guide/en/kibana/current/xpack-security-session-management.html). (Some info duplicates which I'm ignoring, hoping to call out the supplementary settings which users should be cognizant exist.) Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> --- docs/user/security/session-management.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user/security/session-management.asciidoc b/docs/user/security/session-management.asciidoc index e896c8fe77254c..45e3d479858ba4 100644 --- a/docs/user/security/session-management.asciidoc +++ b/docs/user/security/session-management.asciidoc @@ -6,7 +6,7 @@ When you log in, {kib} creates a session that is used to authenticate subsequent When your session expires, or you log out, {kib} will invalidate your cookie and remove session information from the index. {kib} also periodically invalidates and removes any expired sessions that weren't explicitly invalidated. -To manage user sessions programmatically, {kib} exposes <>. +To manage user sessions programmatically, {kib} exposes <>. For details, check out <>. [[session-idle-timeout]] ==== Session idle timeout From 59e8f3d6f9953b81d27a4f0f6a6f00b88e2aaef8 Mon Sep 17 00:00:00 2001 From: Faisal Kanout Date: Wed, 23 Feb 2022 17:52:26 +0300 Subject: [PATCH 011/137] [RAC][METRICS]Update reason msg rule variable description (#126053) * Update reason msg variable description * Update i18n files --- x-pack/plugins/infra/server/lib/alerting/common/messages.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x-pack/plugins/infra/server/lib/alerting/common/messages.ts b/x-pack/plugins/infra/server/lib/alerting/common/messages.ts index 068861b2bac420..193b5191bc98ff 100644 --- a/x-pack/plugins/infra/server/lib/alerting/common/messages.ts +++ b/x-pack/plugins/infra/server/lib/alerting/common/messages.ts @@ -172,8 +172,7 @@ export const alertStateActionVariableDescription = i18n.translate( export const reasonActionVariableDescription = i18n.translate( 'xpack.infra.metrics.alerting.reasonActionVariableDescription', { - defaultMessage: - 'A description of why the alert is in this state, including which metrics have crossed which thresholds', + defaultMessage: 'A concise description of the reason for the alert', } ); From a20e4baa5479621341a9c45e316f033c07d37b56 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 23 Feb 2022 17:08:04 +0200 Subject: [PATCH 012/137] [Gauge] Added logDatatable functionality to the Gauge. (#126225) * Added `logDatatable` functionality to the Gauge. Co-authored-by: Marco Liberati --- .../__snapshots__/gauge_function.test.ts.snap | 29 +++++++++++++++++ .../gauge_function.test.ts | 16 ++++++++++ .../expression_functions/gauge_function.ts | 32 +++++++++++++++++++ .../common/utils/prepare_log_table.ts | 31 ++++++++++++++---- 4 files changed, 102 insertions(+), 6 deletions(-) diff --git a/src/plugins/chart_expressions/expression_gauge/common/expression_functions/__snapshots__/gauge_function.test.ts.snap b/src/plugins/chart_expressions/expression_gauge/common/expression_functions/__snapshots__/gauge_function.test.ts.snap index 56e22aedf42edd..3ff6f1608eb091 100644 --- a/src/plugins/chart_expressions/expression_gauge/common/expression_functions/__snapshots__/gauge_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_gauge/common/expression_functions/__snapshots__/gauge_function.test.ts.snap @@ -1,5 +1,34 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`interpreter/functions#gauge logs correct datatable to inspector 1`] = ` +Object { + "columns": Array [ + Object { + "id": "col-0-1", + "meta": Object { + "dimensionName": "Metric", + "type": "number", + }, + "name": "Count", + }, + Object { + "id": "col-1-2", + "meta": Object { + "dimensionName": "Min", + "type": "string", + }, + "name": "Dest", + }, + ], + "rows": Array [ + Object { + "col-0-1": 0, + }, + ], + "type": "datatable", +} +`; + exports[`interpreter/functions#gauge returns an object with the correct structure for the arc if centralMajor and centralMajorMode are passed 1`] = ` Object { "as": "gauge", diff --git a/src/plugins/chart_expressions/expression_gauge/common/expression_functions/gauge_function.test.ts b/src/plugins/chart_expressions/expression_gauge/common/expression_functions/gauge_function.test.ts index 0060cc267bf720..54c7fed1a9b9b1 100644 --- a/src/plugins/chart_expressions/expression_gauge/common/expression_functions/gauge_function.test.ts +++ b/src/plugins/chart_expressions/expression_gauge/common/expression_functions/gauge_function.test.ts @@ -111,4 +111,20 @@ describe('interpreter/functions#gauge', () => { ); expect(actual).toThrowErrorMatchingSnapshot(); }); + + it('logs correct datatable to inspector', async () => { + let loggedTable: Datatable; + const handlers = { + inspectorAdapters: { + tables: { + logDatatable: (name: string, datatable: Datatable) => { + loggedTable = datatable; + }, + }, + }, + }; + await fn(context, args, handlers as any); + + expect(loggedTable!).toMatchSnapshot(); + }); }); diff --git a/src/plugins/chart_expressions/expression_gauge/common/expression_functions/gauge_function.ts b/src/plugins/chart_expressions/expression_gauge/common/expression_functions/gauge_function.ts index 14e1bdef6e6543..ae9da90ffb5727 100644 --- a/src/plugins/chart_expressions/expression_gauge/common/expression_functions/gauge_function.ts +++ b/src/plugins/chart_expressions/expression_gauge/common/expression_functions/gauge_function.ts @@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n'; import { findAccessorOrFail } from '../../../../visualizations/common/utils'; import type { ExpressionValueVisDimension } from '../../../../visualizations/common'; +import { prepareLogTable } from '../../../../visualizations/common/utils'; import type { DatatableColumn } from '../../../../expressions'; import { GaugeExpressionFunctionDefinition } from '../types'; import { @@ -50,6 +51,25 @@ export const errors = { }), }; +const strings = { + getMetricHelp: () => + i18n.translate('expressionGauge.logDatatable.metric', { + defaultMessage: 'Metric', + }), + getMinHelp: () => + i18n.translate('expressionGauge.logDatatable.min', { + defaultMessage: 'Min', + }), + getMaxHelp: () => + i18n.translate('expressionGauge.logDatatable.max', { + defaultMessage: 'Max', + }), + getGoalHelp: () => + i18n.translate('expressionGauge.logDatatable.goal', { + defaultMessage: 'Goal', + }), +}; + const validateAccessor = ( accessor: string | undefined | ExpressionValueVisDimension, columns: DatatableColumn[] @@ -189,11 +209,23 @@ export const gaugeFunction = (): GaugeExpressionFunctionDefinition => ({ validateAccessor(args.goal, data.columns); const { centralMajor, centralMajorMode, ...restArgs } = args; + const { metric, min, max, goal } = restArgs; if (!isRoundShape(args.shape) && (centralMajorMode || centralMajor)) { throw new Error(errors.centralMajorNotSupportedForShapeError(args.shape)); } + if (handlers?.inspectorAdapters?.tables) { + const logTable = prepareLogTable(data, [ + [metric ? [metric] : undefined, strings.getMetricHelp()], + [min ? [min] : undefined, strings.getMinHelp()], + [max ? [max] : undefined, strings.getMaxHelp()], + [goal ? [goal] : undefined, strings.getGoalHelp()], + ]); + + handlers.inspectorAdapters.tables.logDatatable('default', logTable); + } + const centralMajorArgs = isRoundShape(args.shape) ? { centralMajorMode: !centralMajorMode ? GaugeCentralMajorModes.AUTO : centralMajorMode, diff --git a/src/plugins/visualizations/common/utils/prepare_log_table.ts b/src/plugins/visualizations/common/utils/prepare_log_table.ts index e6d5627953522d..af36ccccff350a 100644 --- a/src/plugins/visualizations/common/utils/prepare_log_table.ts +++ b/src/plugins/visualizations/common/utils/prepare_log_table.ts @@ -10,29 +10,48 @@ import { ExpressionValueVisDimension } from '../expression_functions/vis_dimensi import { ExpressionValueXYDimension } from '../expression_functions/xy_dimension'; import { Datatable, DatatableColumn } from '../../../expressions/common/expression_types/specs'; -export type Dimension = [ - Array | undefined, - string -]; +type DimensionColumn = ExpressionValueVisDimension | ExpressionValueXYDimension | string; + +export type Dimension = [DimensionColumn[] | undefined, string]; const isColumnEqualToAccessor = ( column: DatatableColumn, columnIndex: number, - accessor: ExpressionValueVisDimension['accessor'] | ExpressionValueXYDimension['accessor'] + accessor: + | ExpressionValueVisDimension['accessor'] + | ExpressionValueXYDimension['accessor'] + | string ) => { + if (typeof accessor === 'string') { + return accessor === column.id; + } + if (typeof accessor === 'number') { return accessor === columnIndex; } + return accessor.id === column.id; }; +const getAccessorFromDimension = (dimension: DimensionColumn) => { + if (typeof dimension === 'string') { + return dimension; + } + + return dimension.accessor; +}; + const getDimensionName = ( column: DatatableColumn, columnIndex: number, dimensions: Dimension[] ) => { for (const dimension of dimensions) { - if (dimension[0]?.find((d) => isColumnEqualToAccessor(column, columnIndex, d.accessor))) { + if ( + dimension[0]?.find((d) => + isColumnEqualToAccessor(column, columnIndex, getAccessorFromDimension(d)) + ) + ) { return dimension[1]; } } From 1a1a191eb76c8034d66a4e832f596fc63438975c Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 23 Feb 2022 09:36:30 -0600 Subject: [PATCH 013/137] [jest] add *_node presets (#126192) --- packages/kbn-es/jest.config.js | 2 +- packages/kbn-es/jest.integration.config.js | 2 +- packages/kbn-optimizer/jest.config.js | 2 +- .../kbn-optimizer/jest.integration.config.js | 2 +- packages/kbn-plugin-generator/jest.config.js | 2 +- .../jest.integration.config.js | 2 +- .../jest.integration.config.js | 2 +- packages/kbn-test/BUILD.bazel | 2 + packages/kbn-test/jest-preset.js | 5 +- packages/kbn-test/jest.config.js | 2 +- packages/kbn-test/jest.integration.config.js | 2 +- .../kbn-test/jest_integration/jest-preset.js | 3 +- .../jest_integration_node/jest-preset.js | 46 +++++++++++++++++++ packages/kbn-test/jest_node/jest-preset.js | 16 +++++++ packages/kbn-test/src/jest/setup/mocks.eui.js | 18 ++++++++ .../{mocks.js => mocks.moment_timezone.js} | 9 ---- .../{polyfills.js => polyfills.jsdom.js} | 0 src/dev/precommit_hook/casing_check_config.js | 2 +- 18 files changed, 97 insertions(+), 22 deletions(-) create mode 100644 packages/kbn-test/jest_integration_node/jest-preset.js create mode 100644 packages/kbn-test/jest_node/jest-preset.js create mode 100644 packages/kbn-test/src/jest/setup/mocks.eui.js rename packages/kbn-test/src/jest/setup/{mocks.js => mocks.moment_timezone.js} (80%) rename packages/kbn-test/src/jest/setup/{polyfills.js => polyfills.jsdom.js} (100%) diff --git a/packages/kbn-es/jest.config.js b/packages/kbn-es/jest.config.js index 7ca76c8104a649..02aa1ca63facf6 100644 --- a/packages/kbn-es/jest.config.js +++ b/packages/kbn-es/jest.config.js @@ -7,7 +7,7 @@ */ module.exports = { - preset: '@kbn/test', + preset: '@kbn/test/jest_node', rootDir: '../..', roots: ['/packages/kbn-es'], }; diff --git a/packages/kbn-es/jest.integration.config.js b/packages/kbn-es/jest.integration.config.js index 58ed5614f26be2..664cee8512fc1c 100644 --- a/packages/kbn-es/jest.integration.config.js +++ b/packages/kbn-es/jest.integration.config.js @@ -7,7 +7,7 @@ */ module.exports = { - preset: '@kbn/test/jest_integration', + preset: '@kbn/test/jest_integration_node', rootDir: '../..', roots: ['/packages/kbn-es'], }; diff --git a/packages/kbn-optimizer/jest.config.js b/packages/kbn-optimizer/jest.config.js index 91218bf815992f..c7e938a072660f 100644 --- a/packages/kbn-optimizer/jest.config.js +++ b/packages/kbn-optimizer/jest.config.js @@ -7,7 +7,7 @@ */ module.exports = { - preset: '@kbn/test', + preset: '@kbn/test/jest_node', rootDir: '../..', roots: ['/packages/kbn-optimizer'], }; diff --git a/packages/kbn-optimizer/jest.integration.config.js b/packages/kbn-optimizer/jest.integration.config.js index 7357f8f6a34b06..a51f4c588af8c1 100644 --- a/packages/kbn-optimizer/jest.integration.config.js +++ b/packages/kbn-optimizer/jest.integration.config.js @@ -7,7 +7,7 @@ */ module.exports = { - preset: '@kbn/test/jest_integration', + preset: '@kbn/test/jest_integration_node', rootDir: '../..', roots: ['/packages/kbn-optimizer'], }; diff --git a/packages/kbn-plugin-generator/jest.config.js b/packages/kbn-plugin-generator/jest.config.js index 34f1be77b48933..f27dfa6ca42db1 100644 --- a/packages/kbn-plugin-generator/jest.config.js +++ b/packages/kbn-plugin-generator/jest.config.js @@ -7,7 +7,7 @@ */ module.exports = { - preset: '@kbn/test', + preset: '@kbn/test/jest_node', rootDir: '../..', roots: ['/packages/kbn-plugin-generator'], }; diff --git a/packages/kbn-plugin-generator/jest.integration.config.js b/packages/kbn-plugin-generator/jest.integration.config.js index 0eac4b764101a5..3031e2c7bd03ab 100644 --- a/packages/kbn-plugin-generator/jest.integration.config.js +++ b/packages/kbn-plugin-generator/jest.integration.config.js @@ -7,7 +7,7 @@ */ module.exports = { - preset: '@kbn/test/jest_integration', + preset: '@kbn/test/jest_integration_node', rootDir: '../..', roots: ['/packages/kbn-plugin-generator'], }; diff --git a/packages/kbn-plugin-helpers/jest.integration.config.js b/packages/kbn-plugin-helpers/jest.integration.config.js index 069989abc01e39..eb7de8ae1596e6 100644 --- a/packages/kbn-plugin-helpers/jest.integration.config.js +++ b/packages/kbn-plugin-helpers/jest.integration.config.js @@ -7,7 +7,7 @@ */ module.exports = { - preset: '@kbn/test/jest_integration', + preset: '@kbn/test/jest_integration_node', rootDir: '../..', roots: ['/packages/kbn-plugin-helpers'], }; diff --git a/packages/kbn-test/BUILD.bazel b/packages/kbn-test/BUILD.bazel index 33d299058168db..233aeab6250b19 100644 --- a/packages/kbn-test/BUILD.bazel +++ b/packages/kbn-test/BUILD.bazel @@ -30,6 +30,8 @@ filegroup( NPM_MODULE_EXTRA_FILES = [ "jest-preset.js", "jest_integration/jest-preset.js", + "jest_integration_node/jest-preset.js", + "jest_node/jest-preset.js", "jest.config.js", "README.md", "package.json", diff --git a/packages/kbn-test/jest-preset.js b/packages/kbn-test/jest-preset.js index 3c9b222e98df5e..9dad901f5eb272 100644 --- a/packages/kbn-test/jest-preset.js +++ b/packages/kbn-test/jest-preset.js @@ -66,14 +66,15 @@ module.exports = { // The paths to modules that run some code to configure or set up the testing environment before each test setupFiles: [ '/node_modules/@kbn/test/target_node/jest/setup/babel_polyfill.js', - '/node_modules/@kbn/test/target_node/jest/setup/polyfills.js', + '/node_modules/@kbn/test/target_node/jest/setup/polyfills.jsdom.js', '/node_modules/@kbn/test/target_node/jest/setup/enzyme.js', ], // A list of paths to modules that run some code to configure or set up the testing framework before each test setupFilesAfterEnv: [ '/node_modules/@kbn/test/target_node/jest/setup/setup_test.js', - '/node_modules/@kbn/test/target_node/jest/setup/mocks.js', + '/node_modules/@kbn/test/target_node/jest/setup/mocks.moment_timezone.js', + '/node_modules/@kbn/test/target_node/jest/setup/mocks.eui.js', '/node_modules/@kbn/test/target_node/jest/setup/react_testing_library.js', ], diff --git a/packages/kbn-test/jest.config.js b/packages/kbn-test/jest.config.js index 6a4847174dde0b..ad5513c7ef31c9 100644 --- a/packages/kbn-test/jest.config.js +++ b/packages/kbn-test/jest.config.js @@ -7,7 +7,7 @@ */ module.exports = { - preset: '@kbn/test', + preset: '@kbn/test/jest_node', rootDir: '../..', roots: ['/packages/kbn-test'], }; diff --git a/packages/kbn-test/jest.integration.config.js b/packages/kbn-test/jest.integration.config.js index 091a7a73de484c..0141c86e8fa59b 100644 --- a/packages/kbn-test/jest.integration.config.js +++ b/packages/kbn-test/jest.integration.config.js @@ -7,7 +7,7 @@ */ module.exports = { - preset: '@kbn/test/jest_integration', + preset: '@kbn/test/jest_integration_node', rootDir: '../..', roots: ['/packages/kbn-test'], }; diff --git a/packages/kbn-test/jest_integration/jest-preset.js b/packages/kbn-test/jest_integration/jest-preset.js index 1d665a4e6a16c2..f5593e3f57fb6f 100644 --- a/packages/kbn-test/jest_integration/jest-preset.js +++ b/packages/kbn-test/jest_integration/jest-preset.js @@ -16,7 +16,8 @@ module.exports = { ), setupFilesAfterEnv: [ '/node_modules/@kbn/test/target_node/jest/setup/after_env.integration.js', - '/node_modules/@kbn/test/target_node/jest/setup/mocks.js', + '/node_modules/@kbn/test/target_node/jest/setup/mocks.moment_timezone.js', + '/node_modules/@kbn/test/target_node/jest/setup/mocks.eui.js', ], reporters: [ 'default', diff --git a/packages/kbn-test/jest_integration_node/jest-preset.js b/packages/kbn-test/jest_integration_node/jest-preset.js new file mode 100644 index 00000000000000..50f097f0386d31 --- /dev/null +++ b/packages/kbn-test/jest_integration_node/jest-preset.js @@ -0,0 +1,46 @@ +/* + * 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 preset = require('../jest-preset'); + +/** @typedef {import("@jest/types").Config.InitialOptions} JestConfig */ +/** @type {JestConfig} */ +module.exports = { + ...preset, + testMatch: ['**/integration_tests**/*.test.{js,mjs,ts,tsx}'], + testPathIgnorePatterns: preset.testPathIgnorePatterns.filter( + (pattern) => !pattern.includes('integration_tests') + ), + setupFilesAfterEnv: [ + '/node_modules/@kbn/test/target_node/jest/setup/after_env.integration.js', + '/node_modules/@kbn/test/target_node/jest/setup/mocks.moment_timezone.js', + ], + reporters: [ + 'default', + [ + '@kbn/test/target_node/jest/junit_reporter', + { + rootDirectory: '.', + reportName: 'Jest Integration Tests', + }, + ], + [ + '@kbn/test/target_node/jest/ci_stats_jest_reporter', + { + testGroupType: 'Jest Integration Tests', + }, + ], + ], + coverageReporters: !!process.env.CI + ? [['json', { file: 'jest-integration.json' }]] + : ['html', 'text'], + + testEnvironment: 'node', + snapshotSerializers: [], + setupFiles: ['/node_modules/@kbn/test/target_node/jest/setup/babel_polyfill.js'], +}; diff --git a/packages/kbn-test/jest_node/jest-preset.js b/packages/kbn-test/jest_node/jest-preset.js new file mode 100644 index 00000000000000..9a0e9a16321e48 --- /dev/null +++ b/packages/kbn-test/jest_node/jest-preset.js @@ -0,0 +1,16 @@ +/* + * 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 preset = require('../jest-preset'); + +module.exports = { + ...preset, + testEnvironment: 'node', + snapshotSerializers: [], + setupFiles: ['/node_modules/@kbn/test/target_node/jest/setup/babel_polyfill.js'], +}; diff --git a/packages/kbn-test/src/jest/setup/mocks.eui.js b/packages/kbn-test/src/jest/setup/mocks.eui.js new file mode 100644 index 00000000000000..caf7f8df6eb5f0 --- /dev/null +++ b/packages/kbn-test/src/jest/setup/mocks.eui.js @@ -0,0 +1,18 @@ +/* + * 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. + */ + +/* eslint-env jest */ + +jest.mock('@elastic/eui/lib/services/react', () => { + // `enqueueStateChange` is an EUI method to batch queued functions that trigger React `setState` calls. + // This is for performance, but when used in certain Jest scernarios it can be nondeterministic. + // Jest tests are never concerned about the state prior to batch completion, so we bypass batching entirely. + return { + enqueueStateChange: (fn) => fn(), + }; +}); diff --git a/packages/kbn-test/src/jest/setup/mocks.js b/packages/kbn-test/src/jest/setup/mocks.moment_timezone.js similarity index 80% rename from packages/kbn-test/src/jest/setup/mocks.js rename to packages/kbn-test/src/jest/setup/mocks.moment_timezone.js index df6e51b782f210..bfe1e58f60b121 100644 --- a/packages/kbn-test/src/jest/setup/mocks.js +++ b/packages/kbn-test/src/jest/setup/mocks.moment_timezone.js @@ -33,12 +33,3 @@ jest.mock('moment-timezone', () => { moment.tz.setDefault('America/New_York'); return moment; }); - -jest.mock('@elastic/eui/lib/services/react', () => { - // `enqueueStateChange` is an EUI method to batch queued functions that trigger React `setState` calls. - // This is for performance, but when used in certain Jest scernarios it can be nondeterministic. - // Jest tests are never concerned about the state prior to batch completion, so we bypass batching entirely. - return { - enqueueStateChange: (fn) => fn(), - }; -}); diff --git a/packages/kbn-test/src/jest/setup/polyfills.js b/packages/kbn-test/src/jest/setup/polyfills.jsdom.js similarity index 100% rename from packages/kbn-test/src/jest/setup/polyfills.js rename to packages/kbn-test/src/jest/setup/polyfills.jsdom.js diff --git a/src/dev/precommit_hook/casing_check_config.js b/src/dev/precommit_hook/casing_check_config.js index e5581631f6baf7..860886811da547 100644 --- a/src/dev/precommit_hook/casing_check_config.js +++ b/src/dev/precommit_hook/casing_check_config.js @@ -39,7 +39,7 @@ export const IGNORE_FILE_GLOBS = [ 'vars/*', '.ci/pipeline-library/**/*', 'packages/kbn-test/jest-preset.js', - 'packages/kbn-test/jest_integration/jest-preset.js', + 'packages/kbn-test/*/jest-preset.js', 'test/package/Vagrantfile', '**/test/**/fixtures/**/*', From f96af1660ca41ec69135ca4db25f8048cbbee298 Mon Sep 17 00:00:00 2001 From: Julia Bardi <90178898+juliaElastic@users.noreply.github.com> Date: Wed, 23 Feb 2022 16:50:44 +0100 Subject: [PATCH 014/137] [Fleet] optimizing package policy upgrade and dry run (#126088) * optimizing package policy upgrade and dry run * optimizing package policy upgrade and dry run * optimizing package policy upgrade and dry run * missing params * fixed tests * removed unused import * fixed tests * fixed checks * reduced duplication, separated validate logic * reduced duplication, separated validate logic Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/fleet/server/mocks/index.ts | 2 - .../routes/package_policy/handlers.test.ts | 9 +- .../services/managed_package_policies.test.ts | 42 +- .../services/managed_package_policies.ts | 29 +- .../server/services/package_policy.test.ts | 56 +- .../fleet/server/services/package_policy.ts | 483 +++++++++++------- 6 files changed, 371 insertions(+), 250 deletions(-) diff --git a/x-pack/plugins/fleet/server/mocks/index.ts b/x-pack/plugins/fleet/server/mocks/index.ts index 7c4c822bd550a5..8e6155e6bf7c2d 100644 --- a/x-pack/plugins/fleet/server/mocks/index.ts +++ b/x-pack/plugins/fleet/server/mocks/index.ts @@ -99,9 +99,7 @@ export const xpackMocks = { export const createPackagePolicyServiceMock = (): jest.Mocked => { return { - _compilePackagePolicyInputs: jest.fn(), buildPackagePolicyFromPackage: jest.fn(), - buildPackagePolicyFromPackageWithVersion: jest.fn(), bulkCreate: jest.fn(), create: jest.fn(), delete: jest.fn(), diff --git a/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts b/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts index a178422e20ee5b..25d0f5c5776678 100644 --- a/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts +++ b/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts @@ -28,21 +28,16 @@ import type { PackagePolicy } from '../../types'; import { registerRoutes } from './index'; -type PackagePolicyServicePublicInterface = Omit< - PackagePolicyServiceInterface, - 'getUpgradePackagePolicyInfo' ->; - const packagePolicyServiceMock = packagePolicyService as jest.Mocked; jest.mock( '../../services/package_policy', (): { - packagePolicyService: jest.Mocked; + packagePolicyService: jest.Mocked; } => { return { packagePolicyService: { - _compilePackagePolicyInputs: jest.fn((registryPkgInfo, packageInfo, vars, dataInputs) => + _compilePackagePolicyInputs: jest.fn((packageInfo, vars, dataInputs) => Promise.resolve(dataInputs) ), buildPackagePolicyFromPackage: jest.fn(), diff --git a/x-pack/plugins/fleet/server/services/managed_package_policies.test.ts b/x-pack/plugins/fleet/server/services/managed_package_policies.test.ts index 402b65334121af..6cbb21a37a196a 100644 --- a/x-pack/plugins/fleet/server/services/managed_package_policies.test.ts +++ b/x-pack/plugins/fleet/server/services/managed_package_policies.test.ts @@ -45,25 +45,24 @@ describe('upgradeManagedPackagePolicies', () => { it('should upgrade policies for managed package', async () => { const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; const soClient = savedObjectsClientMock.create(); + const packagePolicy = { + id: 'managed-package-id', + inputs: {}, + version: '', + revision: 1, + updated_at: '', + updated_by: '', + created_at: '', + created_by: '', + package: { + name: 'managed-package', + title: 'Managed Package', + version: '0.0.1', + }, + }; (packagePolicyService.list as jest.Mock).mockResolvedValueOnce({ - items: [ - { - id: 'managed-package-id', - inputs: {}, - version: '', - revision: 1, - updated_at: '', - updated_by: '', - created_at: '', - created_by: '', - package: { - name: 'managed-package', - title: 'Managed Package', - version: '0.0.1', - }, - }, - ], + items: [packagePolicy], }); (packagePolicyService.getUpgradeDryRunDiff as jest.Mock).mockResolvedValueOnce({ @@ -89,7 +88,14 @@ describe('upgradeManagedPackagePolicies', () => { { packagePolicyId: 'managed-package-id', diff: [{ id: 'foo' }, { id: 'bar' }], errors: [] }, ]); - expect(packagePolicyService.upgrade).toBeCalledWith(soClient, esClient, ['managed-package-id']); + expect(packagePolicyService.upgrade).toBeCalledWith( + soClient, + esClient, + ['managed-package-id'], + undefined, + packagePolicy, + '1.0.0' + ); }); it('should not upgrade policy if newer than installed package version', async () => { diff --git a/x-pack/plugins/fleet/server/services/managed_package_policies.ts b/x-pack/plugins/fleet/server/services/managed_package_policies.ts index 2c4b326d565325..e17ca5ebcd1de5 100644 --- a/x-pack/plugins/fleet/server/services/managed_package_policies.ts +++ b/x-pack/plugins/fleet/server/services/managed_package_policies.ts @@ -50,7 +50,7 @@ export const upgradeManagedPackagePolicies = async ( for (const packagePolicy of packagePolicies) { if (isPolicyVersionLtInstalledVersion(packagePolicy, installedPackage)) { - await upgradePackagePolicy(soClient, esClient, packagePolicy.id, results); + await upgradePackagePolicy(soClient, esClient, packagePolicy, installedPackage, results); } } } @@ -84,13 +84,19 @@ function isPolicyVersionLtInstalledVersion( async function upgradePackagePolicy( soClient: SavedObjectsClientContract, esClient: ElasticsearchClient, - packagePolicyId: string, + packagePolicy: PackagePolicy, + installedPackage: Installation, results: UpgradeManagedPackagePoliciesResult[] ) { // Since upgrades don't report diffs/errors, we need to perform a dry run first in order // to notify the user of any granular policy upgrade errors that occur during Fleet's // preconfiguration check - const dryRunResults = await packagePolicyService.getUpgradeDryRunDiff(soClient, packagePolicyId); + const dryRunResults = await packagePolicyService.getUpgradeDryRunDiff( + soClient, + packagePolicy.id, + packagePolicy, + installedPackage.version + ); if (dryRunResults.hasErrors) { const errors = dryRunResults.diff @@ -100,17 +106,24 @@ async function upgradePackagePolicy( appContextService .getLogger() .error( - new Error(`Error upgrading package policy ${packagePolicyId}: ${JSON.stringify(errors)}`) + new Error(`Error upgrading package policy ${packagePolicy.id}: ${JSON.stringify(errors)}`) ); - results.push({ packagePolicyId, diff: dryRunResults.diff, errors }); + results.push({ packagePolicyId: packagePolicy.id, diff: dryRunResults.diff, errors }); return; } try { - await packagePolicyService.upgrade(soClient, esClient, [packagePolicyId]); - results.push({ packagePolicyId, diff: dryRunResults.diff, errors: [] }); + await packagePolicyService.upgrade( + soClient, + esClient, + [packagePolicy.id], + undefined, + packagePolicy, + installedPackage.version + ); + results.push({ packagePolicyId: packagePolicy.id, diff: dryRunResults.diff, errors: [] }); } catch (error) { - results.push({ packagePolicyId, diff: dryRunResults.diff, errors: [error] }); + results.push({ packagePolicyId: packagePolicy.id, diff: dryRunResults.diff, errors: [error] }); } } diff --git a/x-pack/plugins/fleet/server/services/package_policy.test.ts b/x-pack/plugins/fleet/server/services/package_policy.test.ts index 40a60e2b525cc2..704071154ac942 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.test.ts @@ -39,7 +39,6 @@ import type { NewPackagePolicy, NewPackagePolicyInput, PackagePolicyPackage, - RegistryPackage, } from '../../common'; import { packageToPackagePolicy } from '../../common'; @@ -50,9 +49,11 @@ import { updatePackageInputs, packagePolicyService, _applyIndexPrivileges, + _compilePackagePolicyInputs, } from './package_policy'; import { appContextService } from './app_context'; -import { fetchInfo } from './epm/registry'; + +import { getPackageInfo } from './epm/packages'; async function mockedGetAssetsData(_a: any, _b: any, dataset: string) { if (dataset === 'dataset1') { @@ -113,10 +114,6 @@ async function mockedGetPackageInfo(params: any) { return Promise.resolve(pkg); } -function mockedRegistryInfo(): RegistryPackage { - return {} as RegistryPackage; -} - jest.mock('./epm/packages/assets', () => { return { getAssetsData: mockedGetAssetsData, @@ -125,7 +122,7 @@ jest.mock('./epm/packages/assets', () => { jest.mock('./epm/packages', () => { return { - getPackageInfo: mockedGetPackageInfo, + getPackageInfo: jest.fn().mockImplementation(mockedGetPackageInfo), getInstallation: mockedGetInstallation, }; }); @@ -170,18 +167,12 @@ jest.mock('./upgrade_sender', () => { }; }); -const mockedFetchInfo = fetchInfo as jest.Mock>; - type CombinedExternalCallback = PutPackagePolicyUpdateCallback | PostPackagePolicyCreateCallback; describe('Package policy service', () => { - beforeEach(() => { - mockedFetchInfo.mockResolvedValue({} as RegistryPackage); - }); describe('_compilePackagePolicyInputs', () => { it('should work with config variables from the stream', async () => { - const inputs = await packagePolicyService._compilePackagePolicyInputs( - mockedRegistryInfo(), + const inputs = await _compilePackagePolicyInputs( { data_streams: [ { @@ -244,8 +235,7 @@ describe('Package policy service', () => { }); it('should work with a two level dataset name', async () => { - const inputs = await packagePolicyService._compilePackagePolicyInputs( - mockedRegistryInfo(), + const inputs = await _compilePackagePolicyInputs( { data_streams: [ { @@ -297,8 +287,7 @@ describe('Package policy service', () => { }); it('should work with config variables at the input level', async () => { - const inputs = await packagePolicyService._compilePackagePolicyInputs( - mockedRegistryInfo(), + const inputs = await _compilePackagePolicyInputs( { data_streams: [ { @@ -361,8 +350,7 @@ describe('Package policy service', () => { }); it('should work with config variables at the package level', async () => { - const inputs = await packagePolicyService._compilePackagePolicyInputs( - mockedRegistryInfo(), + const inputs = await _compilePackagePolicyInputs( { data_streams: [ { @@ -430,8 +418,7 @@ describe('Package policy service', () => { }); it('should work with an input with a template and no streams', async () => { - const inputs = await packagePolicyService._compilePackagePolicyInputs( - mockedRegistryInfo(), + const inputs = await _compilePackagePolicyInputs( { data_streams: [], policy_templates: [ @@ -473,8 +460,7 @@ describe('Package policy service', () => { }); it('should work with an input with a template and streams', async () => { - const inputs = await packagePolicyService._compilePackagePolicyInputs( - mockedRegistryInfo(), + const inputs = await _compilePackagePolicyInputs( { data_streams: [ { @@ -579,8 +565,7 @@ describe('Package policy service', () => { }); it('should work with a package without input', async () => { - const inputs = await packagePolicyService._compilePackagePolicyInputs( - mockedRegistryInfo(), + const inputs = await _compilePackagePolicyInputs( { policy_templates: [ { @@ -596,8 +581,7 @@ describe('Package policy service', () => { }); it('should work with a package with a empty inputs array', async () => { - const inputs = await packagePolicyService._compilePackagePolicyInputs( - mockedRegistryInfo(), + const inputs = await _compilePackagePolicyInputs( { policy_templates: [ { @@ -906,14 +890,16 @@ describe('Package policy service', () => { ...mockPackagePolicy, inputs: [], }; - - mockedFetchInfo.mockResolvedValue({ - elasticsearch: { - privileges: { - cluster: ['monitor'], + (getPackageInfo as jest.Mock).mockImplementation(async (params) => { + return Promise.resolve({ + ...(await mockedGetPackageInfo(params)), + elasticsearch: { + privileges: { + cluster: ['monitor'], + }, }, - }, - } as RegistryPackage); + } as PackageInfo); + }); savedObjectsClient.get.mockResolvedValue({ id: 'test', diff --git a/x-pack/plugins/fleet/server/services/package_policy.ts b/x-pack/plugins/fleet/server/services/package_policy.ts index 0a4ab8e2f26d56..1acbce4f22cd1d 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.ts @@ -41,7 +41,7 @@ import type { ListResult, UpgradePackagePolicyDryRunResponseItem, RegistryDataStream, - InstallablePackage, + PackagePolicyPackage, } from '../../common'; import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../constants'; import { @@ -62,7 +62,6 @@ import type { ExternalCallback } from '..'; import { agentPolicyService } from './agent_policy'; import { outputService } from './output'; -import * as Registry from './epm/registry'; import { getPackageInfo, getInstallation, ensureInstalledPackage } from './epm/packages'; import { getAssetsData } from './epm/packages/assets'; import { compileTemplate } from './epm/agent/agent'; @@ -71,7 +70,6 @@ import { appContextService } from '.'; import { removeOldAssets } from './epm/packages/cleanup'; import type { PackageUpdateEvent, UpdateEventType } from './upgrade_sender'; import { sendTelemetryEvents } from './upgrade_sender'; -import { getArchivePackage } from './epm/archive'; export type InputsOverride = Partial & { vars?: Array; @@ -88,7 +86,7 @@ export const DATA_STREAM_ALLOWED_INDEX_PRIVILEGES = new Set([ 'read_cross_cluster', ]); -class PackagePolicyService { +class PackagePolicyService implements PackagePolicyServiceInterface { public async create( soClient: SavedObjectsClientContract, esClient: ElasticsearchClient, @@ -164,21 +162,9 @@ class PackagePolicyService { } validatePackagePolicyOrThrow(packagePolicy, pkgInfo); - let installablePackage: InstallablePackage | undefined = - getArchivePackage(pkgInfo)?.packageInfo; + inputs = await _compilePackagePolicyInputs(pkgInfo, packagePolicy.vars || {}, inputs); - if (!installablePackage) { - installablePackage = await Registry.fetchInfo(pkgInfo.name, pkgInfo.version); - } - - inputs = await this._compilePackagePolicyInputs( - installablePackage, - pkgInfo, - packagePolicy.vars || {}, - inputs - ); - - elasticsearch = installablePackage.elasticsearch; + elasticsearch = pkgInfo.elasticsearch; } const isoDate = new Date().toISOString(); @@ -409,20 +395,8 @@ class PackagePolicyService { validatePackagePolicyOrThrow(packagePolicy, pkgInfo); - let installablePackage: InstallablePackage | undefined = - getArchivePackage(pkgInfo)?.packageInfo; - - if (!installablePackage) { - installablePackage = await Registry.fetchInfo(pkgInfo.name, pkgInfo.version); - } - - inputs = await this._compilePackagePolicyInputs( - installablePackage, - pkgInfo, - packagePolicy.vars || {}, - inputs - ); - elasticsearch = installablePackage.elasticsearch; + inputs = await _compilePackagePolicyInputs(pkgInfo, packagePolicy.vars || {}, inputs); + elasticsearch = pkgInfo.elasticsearch; } await soClient.update( @@ -526,8 +500,52 @@ class PackagePolicyService { return result; } - public async getUpgradePackagePolicyInfo(soClient: SavedObjectsClientContract, id: string) { - const packagePolicy = await this.get(soClient, id); + // TODO should move out, public only for unit tests + public async getUpgradePackagePolicyInfo( + soClient: SavedObjectsClientContract, + id: string, + packagePolicy?: PackagePolicy, + pkgVersion?: string + ): Promise<{ packagePolicy: PackagePolicy; packageInfo: PackageInfo }> { + if (!packagePolicy) { + packagePolicy = (await this.get(soClient, id)) ?? undefined; + } + if (!pkgVersion && packagePolicy) { + const installedPackage = await getInstallation({ + savedObjectsClient: soClient, + pkgName: packagePolicy.package!.name, + }); + if (!installedPackage) { + throw new IngestManagerError( + i18n.translate('xpack.fleet.packagePolicy.packageNotInstalledError', { + defaultMessage: 'Package {name} is not installed', + values: { + name: packagePolicy.package!.name, + }, + }) + ); + } + pkgVersion = installedPackage.version; + } + let packageInfo: PackageInfo | undefined; + if (packagePolicy) { + packageInfo = await getPackageInfo({ + savedObjectsClient: soClient, + pkgName: packagePolicy!.package!.name, + pkgVersion: pkgVersion ?? '', + }); + } + + this.validateUpgradePackagePolicy(id, packageInfo, packagePolicy); + + return { packagePolicy: packagePolicy!, packageInfo: packageInfo! }; + } + + private validateUpgradePackagePolicy( + id: string, + packageInfo?: PackageInfo, + packagePolicy?: PackagePolicy + ) { if (!packagePolicy) { throw new IngestManagerError( i18n.translate('xpack.fleet.packagePolicy.policyNotFoundError', { @@ -546,28 +564,6 @@ class PackagePolicyService { ); } - const installedPackage = await getInstallation({ - savedObjectsClient: soClient, - pkgName: packagePolicy.package.name, - }); - - if (!installedPackage) { - throw new IngestManagerError( - i18n.translate('xpack.fleet.packagePolicy.packageNotInstalledError', { - defaultMessage: 'Package {name} is not installed', - values: { - name: packagePolicy.package.name, - }, - }) - ); - } - - const packageInfo = await getPackageInfo({ - savedObjectsClient: soClient, - pkgName: packagePolicy.package.name, - pkgVersion: installedPackage?.version ?? '', - }); - const isInstalledVersionLessThanPolicyVersion = semverLt( packageInfo?.version ?? '', packagePolicy.package.version @@ -586,59 +582,29 @@ class PackagePolicyService { }) ); } - - return { - packagePolicy: packagePolicy as Required, - packageInfo, - }; } public async upgrade( soClient: SavedObjectsClientContract, esClient: ElasticsearchClient, ids: string[], - options?: { user?: AuthenticatedUser } + options?: { user?: AuthenticatedUser }, + packagePolicy?: PackagePolicy, + pkgVersion?: string ): Promise { const result: UpgradePackagePolicyResponse = []; for (const id of ids) { try { - const { packagePolicy, packageInfo } = await this.getUpgradePackagePolicyInfo(soClient, id); - - const updatePackagePolicy = updatePackageInputs( - { - ...omit(packagePolicy, 'id'), - inputs: packagePolicy.inputs, - package: { - ...packagePolicy.package, - version: packageInfo.version, - }, - }, - packageInfo, - packageToPackagePolicyInputs(packageInfo) as InputsOverride[] - ); - const registryPkgInfo = await Registry.fetchInfo(packageInfo.name, packageInfo.version); - updatePackagePolicy.inputs = await this._compilePackagePolicyInputs( - registryPkgInfo, - packageInfo, - updatePackagePolicy.vars || {}, - updatePackagePolicy.inputs as PackagePolicyInput[] - ); - updatePackagePolicy.elasticsearch = registryPkgInfo.elasticsearch; - - await this.update( + let packageInfo: PackageInfo; + ({ packagePolicy, packageInfo } = await this.getUpgradePackagePolicyInfo( soClient, - esClient, - id, - updatePackagePolicy, - options, - packagePolicy.package.version - ); - result.push({ id, - name: packagePolicy.name, - success: true, - }); + packagePolicy, + pkgVersion + )); + + await this.doUpgrade(soClient, esClient, id, packagePolicy!, result, packageInfo, options); } catch (error) { result.push({ id, @@ -651,67 +617,65 @@ class PackagePolicyService { return result; } - public async getUpgradeDryRunDiff( + private async doUpgrade( soClient: SavedObjectsClientContract, - id: string - ): Promise { - try { - const { packagePolicy, packageInfo } = await this.getUpgradePackagePolicyInfo(soClient, id); - - const updatedPackagePolicy = updatePackageInputs( - { - ...omit(packagePolicy, 'id'), - inputs: packagePolicy.inputs, - package: { - ...packagePolicy.package, - version: packageInfo.version, - }, + esClient: ElasticsearchClient, + id: string, + packagePolicy: PackagePolicy, + result: UpgradePackagePolicyResponse, + packageInfo: PackageInfo, + options?: { user?: AuthenticatedUser } + ) { + const updatePackagePolicy = updatePackageInputs( + { + ...omit(packagePolicy, 'id'), + inputs: packagePolicy.inputs, + package: { + ...packagePolicy.package!, + version: packageInfo.version, }, - packageInfo, - packageToPackagePolicyInputs(packageInfo) as InputsOverride[], - true - ); - const registryPkgInfo = await Registry.fetchInfo(packageInfo.name, packageInfo.version); - updatedPackagePolicy.inputs = await this._compilePackagePolicyInputs( - registryPkgInfo, - packageInfo, - updatedPackagePolicy.vars || {}, - updatedPackagePolicy.inputs as PackagePolicyInput[] - ); - updatedPackagePolicy.elasticsearch = registryPkgInfo.elasticsearch; + }, + packageInfo, + packageToPackagePolicyInputs(packageInfo) as InputsOverride[] + ); + updatePackagePolicy.inputs = await _compilePackagePolicyInputs( + packageInfo, + updatePackagePolicy.vars || {}, + updatePackagePolicy.inputs as PackagePolicyInput[] + ); + updatePackagePolicy.elasticsearch = packageInfo.elasticsearch; - const hasErrors = 'errors' in updatedPackagePolicy; + await this.update( + soClient, + esClient, + id, + updatePackagePolicy, + options, + packagePolicy.package!.version + ); + result.push({ + id, + name: packagePolicy.name, + success: true, + }); + } - if (packagePolicy.package.version !== packageInfo.version) { - const upgradeTelemetry: PackageUpdateEvent = { - packageName: packageInfo.name, - currentVersion: packagePolicy.package.version, - newVersion: packageInfo.version, - status: hasErrors ? 'failure' : 'success', - error: hasErrors ? updatedPackagePolicy.errors : undefined, - dryRun: true, - eventType: 'package-policy-upgrade' as UpdateEventType, - }; - sendTelemetryEvents( - appContextService.getLogger(), - appContextService.getTelemetryEventsSender(), - upgradeTelemetry - ); - appContextService - .getLogger() - .info( - `Package policy upgrade dry run ${ - hasErrors ? 'resulted in errors' : 'ran successfully' - }` - ); - appContextService.getLogger().debug(JSON.stringify(upgradeTelemetry)); - } + public async getUpgradeDryRunDiff( + soClient: SavedObjectsClientContract, + id: string, + packagePolicy?: PackagePolicy, + pkgVersion?: string + ): Promise { + try { + let packageInfo: PackageInfo; + ({ packagePolicy, packageInfo } = await this.getUpgradePackagePolicyInfo( + soClient, + id, + packagePolicy, + pkgVersion + )); - return { - name: updatedPackagePolicy.name, - diff: [packagePolicy, updatedPackagePolicy], - hasErrors, - }; + return this.calculateDiff(packagePolicy, packageInfo); } catch (error) { return { hasErrors: true, @@ -720,6 +684,76 @@ class PackagePolicyService { } } + private async calculateDiff( + packagePolicy: PackagePolicy, + packageInfo: PackageInfo + ): Promise { + const updatedPackagePolicy = updatePackageInputs( + { + ...omit(packagePolicy, 'id'), + inputs: packagePolicy.inputs, + package: { + ...packagePolicy.package!, + version: packageInfo.version, + }, + }, + packageInfo, + packageToPackagePolicyInputs(packageInfo) as InputsOverride[], + true + ); + updatedPackagePolicy.inputs = await _compilePackagePolicyInputs( + packageInfo, + updatedPackagePolicy.vars || {}, + updatedPackagePolicy.inputs as PackagePolicyInput[] + ); + updatedPackagePolicy.elasticsearch = packageInfo.elasticsearch; + + const hasErrors = 'errors' in updatedPackagePolicy; + + this.sendUpgradeTelemetry( + packagePolicy.package!, + packageInfo.version, + hasErrors, + updatedPackagePolicy.errors + ); + + return { + name: updatedPackagePolicy.name, + diff: [packagePolicy, updatedPackagePolicy], + hasErrors, + }; + } + + private sendUpgradeTelemetry( + packagePolicyPackage: PackagePolicyPackage, + latestVersion: string, + hasErrors: boolean, + errors?: Array<{ key: string | undefined; message: string }> + ) { + if (packagePolicyPackage.version !== latestVersion) { + const upgradeTelemetry: PackageUpdateEvent = { + packageName: packagePolicyPackage.name, + currentVersion: packagePolicyPackage.version, + newVersion: latestVersion, + status: hasErrors ? 'failure' : 'success', + error: hasErrors ? errors : undefined, + dryRun: true, + eventType: 'package-policy-upgrade' as UpdateEventType, + }; + sendTelemetryEvents( + appContextService.getLogger(), + appContextService.getTelemetryEventsSender(), + upgradeTelemetry + ); + appContextService + .getLogger() + .info( + `Package policy upgrade dry run ${hasErrors ? 'resulted in errors' : 'ran successfully'}` + ); + appContextService.getLogger().debug(JSON.stringify(upgradeTelemetry)); + } + } + public async enrichPolicyWithDefaultsFromPackage( soClient: SavedObjectsClientContract, newPolicy: NewPackagePolicy @@ -775,7 +809,7 @@ class PackagePolicyService { return newPackagePolicy; } - public async buildPackagePolicyFromPackageWithVersion( + private async buildPackagePolicyFromPackageWithVersion( soClient: SavedObjectsClientContract, pkgName: string, pkgVersion: string @@ -813,30 +847,6 @@ class PackagePolicyService { } } - public async _compilePackagePolicyInputs( - installablePackage: InstallablePackage, - pkgInfo: PackageInfo, - vars: PackagePolicy['vars'], - inputs: PackagePolicyInput[] - ): Promise { - const inputsPromises = inputs.map(async (input) => { - const compiledInput = await _compilePackagePolicyInput(pkgInfo, vars, input); - const compiledStreams = await _compilePackageStreams( - installablePackage, - pkgInfo, - vars, - input - ); - return { - ...input, - compiled_input: compiledInput, - streams: compiledStreams, - }; - }); - - return Promise.all(inputsPromises); - } - public async runExternalCallbacks( externalCallbackType: A, packagePolicy: A extends 'postPackagePolicyDelete' @@ -936,6 +946,24 @@ function assignStreamIdToInput(packagePolicyId: string, input: NewPackagePolicyI }; } +export async function _compilePackagePolicyInputs( + pkgInfo: PackageInfo, + vars: PackagePolicy['vars'], + inputs: PackagePolicyInput[] +): Promise { + const inputsPromises = inputs.map(async (input) => { + const compiledInput = await _compilePackagePolicyInput(pkgInfo, vars, input); + const compiledStreams = await _compilePackageStreams(pkgInfo, vars, input); + return { + ...input, + compiled_input: compiledInput, + streams: compiledStreams, + }; + }); + + return Promise.all(inputsPromises); +} + async function _compilePackagePolicyInput( pkgInfo: PackageInfo, vars: PackagePolicy['vars'], @@ -977,7 +1005,6 @@ async function _compilePackagePolicyInput( } async function _compilePackageStreams( - installablePackage: InstallablePackage, pkgInfo: PackageInfo, vars: PackagePolicy['vars'], input: PackagePolicyInput @@ -1133,8 +1160,104 @@ function _enforceFrozenVars( return resultVars; } -export type PackagePolicyServiceInterface = PackagePolicyService; -export const packagePolicyService = new PackagePolicyService(); +export interface PackagePolicyServiceInterface { + create( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + packagePolicy: NewPackagePolicy, + options?: { + spaceId?: string; + id?: string; + user?: AuthenticatedUser; + bumpRevision?: boolean; + force?: boolean; + skipEnsureInstalled?: boolean; + skipUniqueNameVerification?: boolean; + overwrite?: boolean; + } + ): Promise; + + bulkCreate( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + packagePolicies: NewPackagePolicy[], + agentPolicyId: string, + options?: { user?: AuthenticatedUser; bumpRevision?: boolean } + ): Promise; + + get(soClient: SavedObjectsClientContract, id: string): Promise; + + getByIDs(soClient: SavedObjectsClientContract, ids: string[]): Promise; + + list( + soClient: SavedObjectsClientContract, + options: ListWithKuery + ): Promise>; + + listIds( + soClient: SavedObjectsClientContract, + options: ListWithKuery + ): Promise>; + + update( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + id: string, + packagePolicyUpdate: UpdatePackagePolicy, + options?: { user?: AuthenticatedUser }, + currentVersion?: string + ): Promise; + + delete( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + ids: string[], + options?: { user?: AuthenticatedUser; skipUnassignFromAgentPolicies?: boolean; force?: boolean } + ): Promise; + + upgrade( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + ids: string[], + options?: { user?: AuthenticatedUser }, + packagePolicy?: PackagePolicy, + pkgVersion?: string + ): Promise; + + getUpgradeDryRunDiff( + soClient: SavedObjectsClientContract, + id: string, + packagePolicy?: PackagePolicy, + pkgVersion?: string + ): Promise; + + enrichPolicyWithDefaultsFromPackage( + soClient: SavedObjectsClientContract, + newPolicy: NewPackagePolicy + ): Promise; + + buildPackagePolicyFromPackage( + soClient: SavedObjectsClientContract, + pkgName: string + ): Promise; + + runExternalCallbacks( + externalCallbackType: A, + packagePolicy: A extends 'postPackagePolicyDelete' + ? DeletePackagePoliciesResponse + : NewPackagePolicy, + context: RequestHandlerContext, + request: KibanaRequest + ): Promise; + + runDeleteExternalCallbacks(deletedPackagePolicies: DeletePackagePoliciesResponse): Promise; + + getUpgradePackagePolicyInfo( + soClient: SavedObjectsClientContract, + id: string + ): Promise<{ packagePolicy: PackagePolicy; packageInfo: PackageInfo }>; +} +export const packagePolicyService: PackagePolicyServiceInterface = new PackagePolicyService(); export type { PackagePolicyService }; From 9b5ca19e49fb7e33540e21d972a994479c8b8f73 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Wed, 23 Feb 2022 17:40:45 +0100 Subject: [PATCH 015/137] [Discover] Unskip and improve runtime editor test (#126061) --- .../apps/discover/_runtime_fields_editor.ts | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/test/functional/apps/discover/_runtime_fields_editor.ts b/test/functional/apps/discover/_runtime_fields_editor.ts index 23325ef5aa084f..8406c755238547 100644 --- a/test/functional/apps/discover/_runtime_fields_editor.ts +++ b/test/functional/apps/discover/_runtime_fields_editor.ts @@ -29,10 +29,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await fieldEditor.enableValue(); await fieldEditor.typeScript("emit('abc')"); await fieldEditor.save(); + await PageObjects.header.waitUntilLoadingHasFinished(); }; - // FLAKY: https://github.com/elastic/kibana/issues/123372 - describe.skip('discover integration with runtime fields editor', function describeIndexTests() { + describe('discover integration with runtime fields editor', function describeIndexTests() { before(async function () { await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader']); await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); @@ -61,32 +61,35 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('allows creation of a new field', async function () { - await createRuntimeField('runtimefield'); - await PageObjects.header.waitUntilLoadingHasFinished(); + const field = '_runtimefield'; + await createRuntimeField(field); await retry.waitForWithTimeout('fieldNames to include runtimefield', 5000, async () => { const fieldNames = await PageObjects.discover.getAllFieldNames(); - return fieldNames.includes('runtimefield'); + return fieldNames.includes(field); }); }); it('allows editing of a newly created field', async function () { - await PageObjects.discover.editField('runtimefield'); - await fieldEditor.setName('runtimefield edited'); + const field = '_runtimefield-before-edit'; + await createRuntimeField(field); + const newFieldName = '_runtimefield-after-edit'; + await PageObjects.discover.editField(field); + await fieldEditor.setName(newFieldName, true); await fieldEditor.save(); await fieldEditor.confirmSave(); await PageObjects.header.waitUntilLoadingHasFinished(); await retry.waitForWithTimeout('fieldNames to include edits', 5000, async () => { const fieldNames = await PageObjects.discover.getAllFieldNames(); - return fieldNames.includes('runtimefield edited'); + return fieldNames.includes(newFieldName); }); }); it('allows creation of a new field and use it in a saved search', async function () { - await createRuntimeField('discover runtimefield'); - await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.discover.clickFieldListItemAdd('discover runtimefield'); - expect(await PageObjects.discover.getDocHeader()).to.have.string('discover runtimefield'); + const fieldName = '_runtimefield-saved-search'; + await createRuntimeField(fieldName); + await PageObjects.discover.clickFieldListItemAdd(fieldName); + expect(await PageObjects.discover.getDocHeader()).to.have.string(fieldName); expect(await PageObjects.discover.saveSearch('Saved Search with runtimefield')); await PageObjects.header.waitUntilLoadingHasFinished(); @@ -95,24 +98,25 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.discover.loadSavedSearch('Saved Search with runtimefield'); await PageObjects.header.waitUntilLoadingHasFinished(); - expect(await PageObjects.discover.getDocHeader()).to.have.string('discover runtimefield'); + expect(await PageObjects.discover.getDocHeader()).to.have.string(fieldName); }); it('deletes a runtime field', async function () { - await createRuntimeField('delete'); - await PageObjects.header.waitUntilLoadingHasFinished(); - - await PageObjects.discover.removeField('delete'); + const fieldName = '_runtimefield-to-delete'; + await createRuntimeField(fieldName); + await PageObjects.discover.removeField(fieldName); await fieldEditor.confirmDelete(); await PageObjects.header.waitUntilLoadingHasFinished(); await retry.waitForWithTimeout('fieldNames to include edits', 5000, async () => { const fieldNames = await PageObjects.discover.getAllFieldNames(); - return !fieldNames.includes('delete'); + return !fieldNames.includes(fieldName); }); }); it('doc view includes runtime fields', async function () { // navigate to doc view + const fieldName = '_runtimefield-doc-view'; + await createRuntimeField(fieldName); const table = await PageObjects.discover.getDocTable(); const useLegacyTable = await PageObjects.discover.useLegacyTable(); await table.clickRowToggle(); @@ -136,10 +140,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // Maybe loading has not completed throw new Error('test subject doc-hit is not yet displayed'); } - const runtimeFieldsRow = await testSubjects.exists( - 'tableDocViewRow-discover runtimefield' - ); - + const runtimeFieldsRow = await testSubjects.exists(`tableDocViewRow-${fieldName}-value`); return hasDocHit && runtimeFieldsRow; } ); From 4bffe7318764205579367919ae371bfe5ef2ea3e Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Wed, 23 Feb 2022 20:02:36 +0300 Subject: [PATCH 016/137] [MetricVis] Add possibility to apply color background for whole container in metric (#125217) * Add `colorFullBackground` arg which allow to colorize full container * Fix Checks * Fix condition * Fix CI * Fix snapshots * Fixed remarks * Fix full color background for auto scaling * Fix CI * Fix autoscale styles * Fix nit's * Update src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.ts Co-authored-by: Michael Marcialis * Fix some nit's Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Michael Marcialis --- .../metric_vis_function.test.ts.snap | 7 ++ .../metric_vis_function.test.ts | 95 +++++++++++++------ .../metric_vis_function.ts | 35 +++++++ .../common/types/expression_functions.ts | 1 + .../common/types/expression_renderers.ts | 1 + .../__stories__/metric_renderer.stories.tsx | 48 ++++++++++ .../metric_component.test.tsx.snap | 3 + .../public/components/metric.scss | 10 ++ .../components/metric_component.test.tsx | 1 + .../public/components/metric_component.tsx | 17 ++++ .../public/components/metric_value.test.tsx | 30 +++++- .../public/components/metric_value.tsx | 19 +++- .../public/components/with_auto_scale.tsx | 38 ++++++-- .../snapshots/baseline/combined_test3.json | 2 +- .../snapshots/baseline/final_output_test.json | 2 +- .../snapshots/baseline/metric_all_data.json | 2 +- .../snapshots/baseline/metric_empty_data.json | 2 +- .../baseline/metric_multi_metric_data.json | 2 +- .../baseline/metric_percentage_mode.json | 2 +- .../baseline/metric_single_metric_data.json | 2 +- .../snapshots/baseline/partial_test_2.json | 2 +- .../snapshots/baseline/step_output_test3.json | 2 +- 22 files changed, 273 insertions(+), 50 deletions(-) diff --git a/src/plugins/chart_expressions/expression_metric/common/expression_functions/__snapshots__/metric_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_metric/common/expression_functions/__snapshots__/metric_vis_function.test.ts.snap index d242dfb4cf42a5..44cc3fee09b1fa 100644 --- a/src/plugins/chart_expressions/expression_metric/common/expression_functions/__snapshots__/metric_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_metric/common/expression_functions/__snapshots__/metric_vis_function.test.ts.snap @@ -41,6 +41,7 @@ Object { }, "metric": Object { "autoScale": undefined, + "colorFullBackground": false, "labels": Object { "position": "bottom", "show": true, @@ -100,3 +101,9 @@ Object { }, } `; + +exports[`interpreter/functions#metric returns error if bucket and colorFullBackground specified 1`] = `"Full background coloring cannot be applied to visualizations that have a bucket specified."`; + +exports[`interpreter/functions#metric returns error if data includes several rows and colorFullBackground specified 1`] = `"Full background coloring cannot be applied to a visualization with multiple metrics."`; + +exports[`interpreter/functions#metric returns error if several metrics and colorFullBackground specified 1`] = `"Full background coloring cannot be applied to a visualization with multiple metrics."`; diff --git a/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.test.ts b/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.test.ts index 30b6954b7c88cf..2c59a9a875d855 100644 --- a/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.test.ts +++ b/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.test.ts @@ -19,36 +19,40 @@ describe('interpreter/functions#metric', () => { rows: [{ 'col-0-1': 0 }], columns: [{ id: 'col-0-1', name: 'Count', meta: { type: 'number' } }], }; - const args: MetricArguments = { - percentageMode: false, - colorMode: 'None', - palette: { - type: 'palette', - name: '', - params: { - colors: ['rgb(0, 0, 0, 0)', 'rgb(112, 38, 231)'], - stops: [0, 10000], - gradient: false, - rangeMin: 0, - rangeMax: 150, - range: 'number', - }, - }, - showLabels: true, - font: { spec: { fontSize: '60px' }, type: 'style', css: '' }, - labelFont: { spec: { fontSize: '24px' }, type: 'style', css: '' }, - labelPosition: LabelPosition.BOTTOM, - metric: [ - { - type: 'vis_dimension', - accessor: 0, - format: { - id: 'number', - params: {}, + let args: MetricArguments; + beforeEach(() => { + args = { + percentageMode: false, + colorMode: 'None', + palette: { + type: 'palette', + name: '', + params: { + colors: ['rgb(0, 0, 0, 0)', 'rgb(112, 38, 231)'], + stops: [0, 10000], + gradient: false, + rangeMin: 0, + rangeMax: 150, + range: 'number', }, }, - ], - }; + colorFullBackground: false, + showLabels: true, + labelFont: { spec: { fontSize: '24px' }, type: 'style', css: '' }, + labelPosition: LabelPosition.BOTTOM, + font: { spec: { fontSize: '60px' }, type: 'style', css: '' }, + metric: [ + { + type: 'vis_dimension', + accessor: 0, + format: { + id: 'number', + params: {}, + }, + }, + ], + }; + }); it('returns an object with the correct structure', () => { const actual = fn(context, args, undefined); @@ -71,4 +75,39 @@ describe('interpreter/functions#metric', () => { expect(loggedTable!).toMatchSnapshot(); }); + + it('returns error if bucket and colorFullBackground specified', () => { + args.colorFullBackground = true; + args.bucket = { + type: 'vis_dimension', + accessor: 0, + format: { + id: 'number', + params: {}, + }, + }; + + expect(() => fn(context, args, undefined)).toThrowErrorMatchingSnapshot(); + }); + + it('returns error if several metrics and colorFullBackground specified', () => { + args.colorFullBackground = true; + args.metric.push({ + type: 'vis_dimension', + accessor: 0, + format: { + id: 'number', + params: {}, + }, + }); + + expect(() => fn(context, args, undefined)).toThrowErrorMatchingSnapshot(); + }); + + it('returns error if data includes several rows and colorFullBackground specified', () => { + args.colorFullBackground = true; + context.rows.push({ 'col-0-1': 0 }); + + expect(() => fn(context, args, undefined)).toThrowErrorMatchingSnapshot(); + }); }); diff --git a/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.ts b/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.ts index ec799fa8ac8aad..596b59b418b422 100644 --- a/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.ts +++ b/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.ts @@ -36,6 +36,22 @@ const errors = { 'Invalid label position is specified. Supported label positions: {labelPosition}', values: { labelPosition: Object.values(LabelPosition).join(', ') }, }), + severalMetricsAndColorFullBackgroundSpecifiedError: () => + i18n.translate( + 'expressionMetricVis.function.errors.severalMetricsAndColorFullBackgroundSpecified', + { + defaultMessage: + 'Full background coloring cannot be applied to a visualization with multiple metrics.', + } + ), + splitByBucketAndColorFullBackgroundSpecifiedError: () => + i18n.translate( + 'expressionMetricVis.function.errors.splitByBucketAndColorFullBackgroundSpecified', + { + defaultMessage: + 'Full background coloring cannot be applied to visualizations that have a bucket specified.', + } + ), }; export const metricVisFunction = (): MetricVisExpressionFunctionDefinition => ({ @@ -61,6 +77,13 @@ export const metricVisFunction = (): MetricVisExpressionFunctionDefinition => ({ defaultMessage: 'Which part of metric to color', }), }, + colorFullBackground: { + types: ['boolean'], + default: false, + help: i18n.translate('expressionMetricVis.function.colorFullBackground.help', { + defaultMessage: 'Applies the selected background color to the full visualization container', + }), + }, palette: { types: ['palette'], help: i18n.translate('expressionMetricVis.function.palette.help', { @@ -123,6 +146,17 @@ export const metricVisFunction = (): MetricVisExpressionFunctionDefinition => ({ throw new Error('Palette must be provided when using percentageMode'); } + // currently we can allow colorize full container only for one metric + if (args.colorFullBackground) { + if (args.bucket) { + throw new Error(errors.splitByBucketAndColorFullBackgroundSpecifiedError()); + } + + if (args.metric.length > 1 || input.rows.length > 1) { + throw new Error(errors.severalMetricsAndColorFullBackgroundSpecifiedError()); + } + } + validateOptions(args.colorMode, ColorMode, errors.invalidColorModeError); validateOptions(args.labelPosition, LabelPosition, errors.invalidLabelPositionError); @@ -165,6 +199,7 @@ export const metricVisFunction = (): MetricVisExpressionFunctionDefinition => ({ ...args.labelFont, }, }, + colorFullBackground: args.colorFullBackground, style: { bgColor: args.colorMode === ColorMode.Background, labelColor: args.colorMode === ColorMode.Labels, diff --git a/src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts index acce56ef2878d5..ff8650eaa62dd5 100644 --- a/src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts @@ -27,6 +27,7 @@ export interface MetricArguments { labelPosition: LabelPositionType; metric: ExpressionValueVisDimension[]; bucket?: ExpressionValueVisDimension; + colorFullBackground: boolean; autoScale?: boolean; } diff --git a/src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts index 3804d015d58034..86baf3fb47f8f1 100644 --- a/src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts @@ -36,6 +36,7 @@ export interface MetricVisParam { palette?: CustomPaletteState; labels: LabelsConfig; style: MetricStyle; + colorFullBackground: boolean; autoScale?: boolean; } diff --git a/src/plugins/chart_expressions/expression_metric/public/__stories__/metric_renderer.stories.tsx b/src/plugins/chart_expressions/expression_metric/public/__stories__/metric_renderer.stories.tsx index 2dd8f4d19cfaa3..039e15d2a67b4a 100644 --- a/src/plugins/chart_expressions/expression_metric/public/__stories__/metric_renderer.stories.tsx +++ b/src/plugins/chart_expressions/expression_metric/public/__stories__/metric_renderer.stories.tsx @@ -64,6 +64,7 @@ const config: MetricVisRenderConfig = { position: LabelPosition.BOTTOM, }, percentageMode: false, + colorFullBackground: false, style, }, dimensions: { @@ -295,4 +296,51 @@ storiesOf('renderers/visMetric', module) {...containerSize} /> ); + }) + .add('With colorizing full container', () => { + return ( + + ); }); diff --git a/src/plugins/chart_expressions/expression_metric/public/components/__snapshots__/metric_component.test.tsx.snap b/src/plugins/chart_expressions/expression_metric/public/components/__snapshots__/metric_component.test.tsx.snap index 8aff94230d5443..684f42d527c19b 100644 --- a/src/plugins/chart_expressions/expression_metric/public/components/__snapshots__/metric_component.test.tsx.snap +++ b/src/plugins/chart_expressions/expression_metric/public/components/__snapshots__/metric_component.test.tsx.snap @@ -3,6 +3,7 @@ exports[`MetricVisComponent should render correct structure for multi-value metrics 1`] = ` Array [ , { }); }; + private isAutoScaleWithColorizingContainer = () => { + return this.props.visParams.metric.autoScale && this.props.visParams.metric.colorFullBackground; + }; + private renderMetric = (metric: MetricOptions, index: number) => { const MetricComponent = this.props.visParams.metric.autoScale ? AutoScaleMetricVisValue @@ -117,12 +121,25 @@ class MetricVisComponent extends Component { return ( this.filterBucket(index) : undefined } + autoScale={this.props.visParams.metric.autoScale} + colorFullBackground={this.props.visParams.metric.colorFullBackground} labelConfig={this.props.visParams.metric.labels} /> ); diff --git a/src/plugins/chart_expressions/expression_metric/public/components/metric_value.test.tsx b/src/plugins/chart_expressions/expression_metric/public/components/metric_value.test.tsx index 872dea382e1c09..f86f70341891c0 100644 --- a/src/plugins/chart_expressions/expression_metric/public/components/metric_value.test.tsx +++ b/src/plugins/chart_expressions/expression_metric/public/components/metric_value.test.tsx @@ -38,6 +38,7 @@ describe('MetricVisValue', () => { style={font} metric={baseMetric} onFilter={() => {}} + colorFullBackground={false} labelConfig={labelConfig} /> ); @@ -46,7 +47,12 @@ describe('MetricVisValue', () => { it('should not be wrapped in button without having a click listener', () => { const component = shallow( - + ); expect(component.find('button').exists()).toBe(false); }); @@ -58,6 +64,7 @@ describe('MetricVisValue', () => { style={font} metric={baseMetric} onFilter={onFilter} + colorFullBackground={false} labelConfig={labelConfig} /> ); @@ -67,7 +74,12 @@ describe('MetricVisValue', () => { it('should not add -isfilterable class if onFilter is not provided', () => { const component = shallow( - + ); component.simulate('click'); expect(component.find('.mtrVis__container-isfilterable')).toHaveLength(0); @@ -80,10 +92,24 @@ describe('MetricVisValue', () => { style={font} metric={baseMetric} onFilter={onFilter} + colorFullBackground={false} labelConfig={labelConfig} /> ); component.simulate('click'); expect(onFilter).toHaveBeenCalled(); }); + + it('should add correct class name if colorFullBackground is true', () => { + const component = shallow( + {}} + colorFullBackground={true} + labelConfig={labelConfig} + /> + ); + expect(component.find('.mtrVis__container-isfull').exists()).toBe(true); + }); }); diff --git a/src/plugins/chart_expressions/expression_metric/public/components/metric_value.tsx b/src/plugins/chart_expressions/expression_metric/public/components/metric_value.tsx index 7ca16dcaf862d5..35c754d8b0b244 100644 --- a/src/plugins/chart_expressions/expression_metric/public/components/metric_value.tsx +++ b/src/plugins/chart_expressions/expression_metric/public/components/metric_value.tsx @@ -15,18 +15,33 @@ interface MetricVisValueProps { onFilter?: () => void; style: MetricStyle; labelConfig: MetricVisParam['labels']; + colorFullBackground: boolean; + autoScale?: boolean; } -export const MetricVisValue = ({ style, metric, onFilter, labelConfig }: MetricVisValueProps) => { +export const MetricVisValue = ({ + style, + metric, + onFilter, + labelConfig, + colorFullBackground, + autoScale, +}: MetricVisValueProps) => { const containerClassName = classNames('mtrVis__container', { // eslint-disable-next-line @typescript-eslint/naming-convention 'mtrVis__container--light': metric.lightText, // eslint-disable-next-line @typescript-eslint/naming-convention 'mtrVis__container-isfilterable': onFilter, + // eslint-disable-next-line @typescript-eslint/naming-convention + 'mtrVis__container-isfull': !autoScale && colorFullBackground, }); + // for autoScale true we should add background to upper level so that correct colorize full container const metricComponent = ( -
+
( - WrappedComponent: ComponentType, - autoScaleParams?: AutoScaleParams -) { - return (props: T) => { +function hasAutoscaleProps(props: T): props is T & AutoScaleProps { + if ((props as T & AutoScaleProps).autoScaleParams) { + return true; + } + return false; +} + +function getWrappedComponentProps(props: T) { + if (hasAutoscaleProps(props)) { + const { autoScaleParams, ...rest } = props; + return rest; + } + + return props; +} + +export function withAutoScale(WrappedComponent: ComponentType) { + return (props: T & AutoScaleProps) => { // An initial scale of 0 means we always redraw // at least once, which is sub-optimal, but it // prevents an annoying flicker. + const { autoScaleParams } = props; + const restProps = getWrappedComponentProps(props); const [scale, setScale] = useState(0); const parentRef = useRef(null); const childrenRef = useRef(null); @@ -68,7 +88,7 @@ export function withAutoScale( setScale(newScale); } }), - [parentDimensions, setScale, scale] + [parentDimensions, setScale, scale, autoScaleParams] ); useEffect(() => { @@ -76,14 +96,14 @@ export function withAutoScale( }, [scaleFn]); return ( -
+
- +
); diff --git a/test/interpreter_functional/snapshots/baseline/combined_test3.json b/test/interpreter_functional/snapshots/baseline/combined_test3.json index b4ecd60f8ac59a..aefe875ff248c8 100644 --- a/test/interpreter_functional/snapshots/baseline/combined_test3.json +++ b/test/interpreter_functional/snapshots/baseline/combined_test3.json @@ -1 +1 @@ -{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/final_output_test.json b/test/interpreter_functional/snapshots/baseline/final_output_test.json index b4ecd60f8ac59a..aefe875ff248c8 100644 --- a/test/interpreter_functional/snapshots/baseline/final_output_test.json +++ b/test/interpreter_functional/snapshots/baseline/final_output_test.json @@ -1 +1 @@ -{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_all_data.json b/test/interpreter_functional/snapshots/baseline/metric_all_data.json index ec04c29dfdcd7b..c146b8ca6e39da 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_all_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_all_data.json @@ -1 +1 @@ -{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":2,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":2,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_empty_data.json b/test/interpreter_functional/snapshots/baseline/metric_empty_data.json index a866540900e953..84ea37086d00cb 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_empty_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_empty_data.json @@ -1 +1 @@ -{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json b/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json index dbb63cd00e0708..de55a313fde430 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json @@ -1 +1 @@ -{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json b/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json index b43986250d4c7b..aacb5a1d38eafe 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json +++ b/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json @@ -1 +1 @@ -{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":{"colors":["rgb(0,0,0,0)","rgb(100, 100, 100)"],"continuity":"none","gradient":false,"range":"number","rangeMax":10000,"rangeMin":0,"stops":[0,10000]},"percentageMode":true,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":{"colors":["rgb(0,0,0,0)","rgb(100, 100, 100)"],"continuity":"none","gradient":false,"range":"number","rangeMax":10000,"rangeMin":0,"stops":[0,10000]},"percentageMode":true,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json b/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json index d65dd9dc4a7758..91d78e9942f1aa 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json @@ -1 +1 @@ -{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/partial_test_2.json b/test/interpreter_functional/snapshots/baseline/partial_test_2.json index b4ecd60f8ac59a..aefe875ff248c8 100644 --- a/test/interpreter_functional/snapshots/baseline/partial_test_2.json +++ b/test/interpreter_functional/snapshots/baseline/partial_test_2.json @@ -1 +1 @@ -{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/step_output_test3.json b/test/interpreter_functional/snapshots/baseline/step_output_test3.json index b4ecd60f8ac59a..aefe875ff248c8 100644 --- a/test/interpreter_functional/snapshots/baseline/step_output_test3.json +++ b/test/interpreter_functional/snapshots/baseline/step_output_test3.json @@ -1 +1 @@ -{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file From 20dc80fc36c828923367e4fbe14e9cd3982e1eee Mon Sep 17 00:00:00 2001 From: Abdul Wahab Zahid Date: Wed, 23 Feb 2022 18:03:29 +0100 Subject: [PATCH 017/137] [Uptime][Monitor Management] Show form validation errors only after user interaction (#126116)(uptime/issues/456) * Expose `onBlur` and `onFieldBlur` on fleet components. * Only pass down validators if either field is interacted with or form submit attempt has been made. uptime/issues/456 --- .../browser/advanced_fields.test.tsx | 17 +- .../fleet_package/browser/advanced_fields.tsx | 290 ++++--- .../fleet_package/browser/simple_fields.tsx | 12 +- .../browser/source_field.test.tsx | 14 +- .../fleet_package/browser/source_field.tsx | 20 +- .../browser/throttling_fields.test.tsx | 46 +- .../browser/throttling_fields.tsx | 7 +- .../fleet_package/combo_box.test.tsx | 14 + .../components/fleet_package/combo_box.tsx | 4 +- .../fleet_package/common/common_fields.tsx | 15 +- .../fleet_package/common/enabled.tsx | 4 +- .../common/simple_fields_wrapper.tsx | 28 +- .../fleet_package/custom_fields.test.tsx | 33 +- .../fleet_package/custom_fields.tsx | 37 +- .../fleet_package/header_field.test.tsx | 19 + .../components/fleet_package/header_field.tsx | 3 + .../http/advanced_fields.test.tsx | 34 +- .../fleet_package/http/advanced_fields.tsx | 783 +++++++++--------- .../fleet_package/http/simple_fields.tsx | 21 +- .../fleet_package/icmp/simple_fields.tsx | 13 +- .../index_response_body_field.test.tsx | 20 +- .../index_response_body_field.tsx | 5 +- .../fleet_package/key_value_field.test.tsx | 22 + .../fleet_package/key_value_field.tsx | 4 + .../fleet_package/request_body_field.tsx | 25 +- .../fleet_package/schedule_field.test.tsx | 23 + .../fleet_package/schedule_field.tsx | 6 +- .../tcp/advanced_fields.test.tsx | 15 +- .../fleet_package/tcp/advanced_fields.tsx | 6 +- .../fleet_package/tcp/simple_fields.tsx | 12 +- .../monitor_config/locations.test.tsx | 36 +- .../monitor_config/locations.tsx | 4 +- .../monitor_advanced_fields.tsx | 138 +-- .../monitor_config/monitor_config.test.tsx | 61 ++ .../monitor_config/monitor_config.tsx | 8 +- .../monitor_config/monitor_fields.test.tsx | 94 +++ .../monitor_config/monitor_fields.tsx | 34 +- .../monitor_config/monitor_name_location.tsx | 5 +- 38 files changed, 1264 insertions(+), 668 deletions(-) create mode 100644 x-pack/plugins/uptime/public/components/monitor_management/monitor_config/monitor_config.test.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor_management/monitor_config/monitor_fields.test.tsx diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/advanced_fields.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/browser/advanced_fields.test.tsx index a4212c362ee31f..884a81020cedf4 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/browser/advanced_fields.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/advanced_fields.test.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import { fireEvent } from '@testing-library/react'; import React from 'react'; import userEvent from '@testing-library/user-event'; import { render } from '../../../lib/helper/rtl_helpers'; @@ -38,17 +39,21 @@ describe('', () => { defaultSimpleFields = defaultBrowserSimpleFields, validate = defaultValidation, children, + onFieldBlur, }: { defaultValues?: BrowserAdvancedFieldsType; defaultSimpleFields?: BrowserSimpleFields; validate?: Validation; children?: React.ReactNode; + onFieldBlur?: (field: ConfigKey) => void; }) => { return ( - {children} + + {children} + @@ -72,6 +77,16 @@ describe('', () => { userEvent.selectOptions(screenshots, ['off']); expect(screenshots.value).toEqual('off'); }); + + it('calls onFieldBlur after change', () => { + const onFieldBlur = jest.fn(); + const { getByLabelText } = render(); + + const screenshots = getByLabelText('Screenshot options') as HTMLInputElement; + userEvent.selectOptions(screenshots, ['off']); + fireEvent.blur(screenshots); + expect(onFieldBlur).toHaveBeenCalledWith(ConfigKey.SCREENSHOTS); + }); }); it('only displayed filter options when zip url is truthy', () => { diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/advanced_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/browser/advanced_fields.tsx index f838474f5219b7..5db5aabd791ee9 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/browser/advanced_fields.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/advanced_fields.tsx @@ -29,198 +29,212 @@ interface Props { validate: Validation; children?: React.ReactNode; minColumnWidth?: string; + onFieldBlur?: (field: ConfigKey) => void; } -export const BrowserAdvancedFields = memo(({ validate, children, minColumnWidth }) => { - const { fields, setFields } = useBrowserAdvancedFieldsContext(); - const { fields: simpleFields } = useBrowserSimpleFieldsContext(); +export const BrowserAdvancedFields = memo( + ({ validate, children, minColumnWidth, onFieldBlur }) => { + const { fields, setFields } = useBrowserAdvancedFieldsContext(); + const { fields: simpleFields } = useBrowserSimpleFieldsContext(); - const handleInputChange = useCallback( - ({ value, configKey }: { value: unknown; configKey: ConfigKey }) => { - setFields((prevFields) => ({ ...prevFields, [configKey]: value })); - }, - [setFields] - ); + const handleInputChange = useCallback( + ({ value, configKey }: { value: unknown; configKey: ConfigKey }) => { + setFields((prevFields) => ({ ...prevFields, [configKey]: value })); + }, + [setFields] + ); - return ( - - - {simpleFields[ConfigKey.SOURCE_ZIP_URL] && ( + return ( + + + {simpleFields[ConfigKey.SOURCE_ZIP_URL] && ( + + + + } + description={ + + } + > + + + } + labelAppend={} + helpText={ + + } + > + + handleInputChange({ + value: event.target.value, + configKey: ConfigKey.JOURNEY_FILTERS_MATCH, + }) + } + onBlur={() => onFieldBlur?.(ConfigKey.JOURNEY_FILTERS_MATCH)} + data-test-subj="syntheticsBrowserJourneyFiltersMatch" + /> + + + } + labelAppend={} + helpText={ + + } + > + + handleInputChange({ value, configKey: ConfigKey.JOURNEY_FILTERS_TAGS }) + } + onBlur={() => onFieldBlur?.(ConfigKey.JOURNEY_FILTERS_TAGS)} + data-test-subj="syntheticsBrowserJourneyFiltersTags" + /> + + + )} } description={ } > + + + + } + data-test-subj="syntheticsBrowserIgnoreHttpsErrors" + > + + } + onChange={(event) => + handleInputChange({ + value: event.target.checked, + configKey: ConfigKey.IGNORE_HTTPS_ERRORS, + }) + } + onBlur={() => onFieldBlur?.(ConfigKey.IGNORE_HTTPS_ERRORS)} + /> + + } labelAppend={} helpText={ } > - handleInputChange({ value: event.target.value, - configKey: ConfigKey.JOURNEY_FILTERS_MATCH, + configKey: ConfigKey.SCREENSHOTS, }) } - data-test-subj="syntheticsBrowserJourneyFiltersMatch" + onBlur={() => onFieldBlur?.(ConfigKey.SCREENSHOTS)} + data-test-subj="syntheticsBrowserScreenshots" /> } labelAppend={} helpText={ } > - handleInputChange({ value, configKey: ConfigKey.JOURNEY_FILTERS_TAGS }) + handleInputChange({ value, configKey: ConfigKey.SYNTHETICS_ARGS }) } - data-test-subj="syntheticsBrowserJourneyFiltersTags" + onBlur={() => onFieldBlur?.(ConfigKey.SYNTHETICS_ARGS)} + data-test-subj="syntheticsBrowserSyntheticsArgs" /> - )} - - - - } - description={ - - } - > - - - - - } - data-test-subj="syntheticsBrowserIgnoreHttpsErrors" - > - - } - onChange={(event) => - handleInputChange({ - value: event.target.checked, - configKey: ConfigKey.IGNORE_HTTPS_ERRORS, - }) - } - /> - - - - } - labelAppend={} - helpText={ - - } - > - - handleInputChange({ - value: event.target.value, - configKey: ConfigKey.SCREENSHOTS, - }) - } - data-test-subj="syntheticsBrowserScreenshots" - /> - - - } - labelAppend={} - helpText={ - - } - > - handleInputChange({ value, configKey: ConfigKey.SYNTHETICS_ARGS })} - data-test-subj="syntheticsBrowserSyntheticsArgs" - /> - - - - {children} - - ); -}); + + {children} + + ); + } +); const requestMethodOptions = Object.values(ScreenshotOption).map((option) => ({ value: option, diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/simple_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/browser/simple_fields.tsx index 14fec078f458a6..ddf8f5699b5f90 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/browser/simple_fields.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/simple_fields.tsx @@ -17,9 +17,10 @@ import { SimpleFieldsWrapper } from '../common/simple_fields_wrapper'; interface Props { validate: Validation; + onFieldBlur: (field: ConfigKey) => void; // To propagate blurred state up to parents } -export const BrowserSimpleFields = memo(({ validate }) => { +export const BrowserSimpleFields = memo(({ validate, onFieldBlur }) => { const { fields, setFields, defaultValues } = useBrowserSimpleFieldsContext(); const handleInputChange = useCallback( ({ value, configKey }: { value: unknown; configKey: ConfigKey }) => { @@ -61,7 +62,12 @@ export const BrowserSimpleFields = memo(({ validate }) => { ); return ( - + (({ validate }) => { configKey: ConfigKey.SCHEDULE, }) } + onBlur={() => onFieldBlur(ConfigKey.SCHEDULE)} number={fields[ConfigKey.SCHEDULE].number} unit={fields[ConfigKey.SCHEDULE].unit} /> @@ -99,6 +106,7 @@ export const BrowserSimpleFields = memo(({ validate }) => { > ({ zipUrl: defaultValues[ConfigKey.SOURCE_ZIP_URL], diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/source_field.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/browser/source_field.test.tsx index 3d1d50abb487f3..7bff465bae1bd1 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/browser/source_field.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/source_field.test.tsx @@ -8,6 +8,7 @@ import 'jest-canvas-mock'; import React from 'react'; import { fireEvent, screen, waitFor } from '@testing-library/react'; +import { ConfigKey } from '../../../../common/runtime_types'; import { render } from '../../../lib/helper/rtl_helpers'; import { IPolicyConfigContextProvider } from '../contexts/policy_config_context'; import { SourceField, defaultValues } from './source_field'; @@ -42,6 +43,7 @@ jest.mock('../../../../../../../src/plugins/kibana_react/public', () => { }); const onChange = jest.fn(); +const onBlur = jest.fn(); describe('', () => { const WrappedComponent = ({ @@ -50,7 +52,7 @@ describe('', () => { return ( - + ); @@ -72,6 +74,16 @@ describe('', () => { }); }); + it('calls onBlur', () => { + render(); + + const zipUrlField = screen.getByTestId('syntheticsBrowserZipUrl'); + fireEvent.click(zipUrlField); + fireEvent.blur(zipUrlField); + + expect(onBlur).toBeCalledWith(ConfigKey.SOURCE_ZIP_URL); + }); + it('shows ZipUrl source type by default', async () => { render(); diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/source_field.tsx b/x-pack/plugins/uptime/public/components/fleet_package/browser/source_field.tsx index 3861537a72c116..89f34618009232 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/browser/source_field.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/source_field.tsx @@ -24,7 +24,7 @@ import { OptionalLabel } from '../optional_label'; import { CodeEditor } from '../code_editor'; import { ScriptRecorderFields } from './script_recorder_fields'; import { ZipUrlTLSFields } from './zip_url_tls_fields'; -import { MonacoEditorLangId } from '../types'; +import { ConfigKey, MonacoEditorLangId } from '../types'; enum SourceType { INLINE = 'syntheticsBrowserInlineConfig', @@ -46,6 +46,7 @@ interface SourceConfig { interface Props { onChange: (sourceConfig: SourceConfig) => void; + onFieldBlur: (field: ConfigKey) => void; defaultConfig?: SourceConfig; } @@ -71,7 +72,7 @@ const getDefaultTab = (defaultConfig: SourceConfig, isZipUrlSourceEnabled = true return isZipUrlSourceEnabled ? SourceType.ZIP : SourceType.INLINE; }; -export const SourceField = ({ onChange, defaultConfig = defaultValues }: Props) => { +export const SourceField = ({ onChange, onFieldBlur, defaultConfig = defaultValues }: Props) => { const { isZipUrlSourceEnabled } = usePolicyConfigContext(); const [sourceType, setSourceType] = useState( getDefaultTab(defaultConfig, isZipUrlSourceEnabled) @@ -118,6 +119,7 @@ export const SourceField = ({ onChange, defaultConfig = defaultValues }: Props) onChange={({ target: { value } }) => setConfig((prevConfig) => ({ ...prevConfig, zipUrl: value })) } + onBlur={() => onFieldBlur(ConfigKey.SOURCE_ZIP_URL)} value={config.zipUrl} data-test-subj="syntheticsBrowserZipUrl" /> @@ -142,6 +144,7 @@ export const SourceField = ({ onChange, defaultConfig = defaultValues }: Props) onChange={({ target: { value } }) => setConfig((prevConfig) => ({ ...prevConfig, proxyUrl: value })) } + onBlur={() => onFieldBlur(ConfigKey.SOURCE_ZIP_PROXY_URL)} value={config.proxyUrl} data-test-subj="syntheticsBrowserZipUrlProxy" /> @@ -165,6 +168,7 @@ export const SourceField = ({ onChange, defaultConfig = defaultValues }: Props) onChange={({ target: { value } }) => setConfig((prevConfig) => ({ ...prevConfig, folder: value })) } + onBlur={() => onFieldBlur(ConfigKey.SOURCE_ZIP_FOLDER)} value={config.folder} data-test-subj="syntheticsBrowserZipUrlFolder" /> @@ -193,7 +197,10 @@ export const SourceField = ({ onChange, defaultConfig = defaultValues }: Props) )} id="jsonParamsEditor" languageId={MonacoEditorLangId.JSON} - onChange={(code) => setConfig((prevConfig) => ({ ...prevConfig, params: code }))} + onChange={(code) => { + setConfig((prevConfig) => ({ ...prevConfig, params: code })); + onFieldBlur(ConfigKey.PARAMS); + }} value={config.params} data-test-subj="syntheticsBrowserZipUrlParams" /> @@ -217,6 +224,7 @@ export const SourceField = ({ onChange, defaultConfig = defaultValues }: Props) onChange={({ target: { value } }) => setConfig((prevConfig) => ({ ...prevConfig, username: value })) } + onBlur={() => onFieldBlur(ConfigKey.SOURCE_ZIP_USERNAME)} value={config.username} data-test-subj="syntheticsBrowserZipUrlUsername" /> @@ -240,6 +248,7 @@ export const SourceField = ({ onChange, defaultConfig = defaultValues }: Props) onChange={({ target: { value } }) => setConfig((prevConfig) => ({ ...prevConfig, password: value })) } + onBlur={() => onFieldBlur(ConfigKey.SOURCE_ZIP_PASSWORD)} value={config.password} data-test-subj="syntheticsBrowserZipUrlPassword" /> @@ -281,7 +290,10 @@ export const SourceField = ({ onChange, defaultConfig = defaultValues }: Props) )} id="javascript" languageId={MonacoEditorLangId.JAVASCRIPT} - onChange={(code) => setConfig((prevConfig) => ({ ...prevConfig, inlineScript: code }))} + onChange={(code) => { + setConfig((prevConfig) => ({ ...prevConfig, inlineScript: code })); + onFieldBlur(ConfigKey.SOURCE_INLINE); + }} value={config.inlineScript} /> diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/throttling_fields.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/browser/throttling_fields.test.tsx index 8021253c80e997..d675616a769150 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/browser/throttling_fields.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/throttling_fields.test.tsx @@ -5,11 +5,18 @@ * 2.0. */ +import { fireEvent } from '@testing-library/react'; import React from 'react'; import userEvent from '@testing-library/user-event'; import { render } from '../../../lib/helper/rtl_helpers'; import { ThrottlingFields } from './throttling_fields'; -import { DataStream, BrowserAdvancedFields, BrowserSimpleFields, Validation } from '../types'; +import { + DataStream, + BrowserAdvancedFields, + BrowserSimpleFields, + Validation, + ConfigKey, +} from '../types'; import { BrowserAdvancedFieldsContextProvider, BrowserSimpleFieldsContextProvider, @@ -31,16 +38,18 @@ describe('', () => { defaultValues = defaultConfig, defaultSimpleFields = defaultBrowserSimpleFields, validate = defaultValidation, + onFieldBlur, }: { defaultValues?: BrowserAdvancedFields; defaultSimpleFields?: BrowserSimpleFields; validate?: Validation; + onFieldBlur?: (field: ConfigKey) => void; }) => { return ( - + @@ -97,6 +106,39 @@ describe('', () => { }); }); + describe('calls onBlur on fields', () => { + const onFieldBlur = jest.fn(); + + afterEach(() => { + jest.resetAllMocks(); + }); + + it('for the enable switch', () => { + const { getByTestId } = render(); + + const enableSwitch = getByTestId('syntheticsBrowserIsThrottlingEnabled'); + fireEvent.focus(enableSwitch); + fireEvent.blur(enableSwitch); + expect(onFieldBlur).toHaveBeenCalledWith(ConfigKey.IS_THROTTLING_ENABLED); + }); + + it('for throttling inputs', () => { + const { getByLabelText } = render(); + + const downloadSpeed = getByLabelText('Download Speed') as HTMLInputElement; + const uploadSpeed = getByLabelText('Upload Speed') as HTMLInputElement; + const latency = getByLabelText('Latency') as HTMLInputElement; + + fireEvent.blur(downloadSpeed); + fireEvent.blur(uploadSpeed); + fireEvent.blur(latency); + + expect(onFieldBlur).toHaveBeenCalledWith(ConfigKey.DOWNLOAD_SPEED); + expect(onFieldBlur).toHaveBeenCalledWith(ConfigKey.UPLOAD_SPEED); + expect(onFieldBlur).toHaveBeenCalledWith(ConfigKey.LATENCY); + }); + }); + describe('validates changing fields', () => { it('disallows negative/zero download speeds', () => { const { getByLabelText, queryByText } = render(); diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/throttling_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/browser/throttling_fields.tsx index d5ec96ebb5a6f5..97f39e7823d5a0 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/browser/throttling_fields.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/throttling_fields.tsx @@ -17,6 +17,7 @@ import { Validation, ConfigKey } from '../types'; interface Props { validate: Validation; minColumnWidth?: string; + onFieldBlur?: (field: ConfigKey) => void; } type ThrottlingConfigs = @@ -25,7 +26,7 @@ type ThrottlingConfigs = | ConfigKey.UPLOAD_SPEED | ConfigKey.LATENCY; -export const ThrottlingFields = memo(({ validate, minColumnWidth }) => { +export const ThrottlingFields = memo(({ validate, minColumnWidth, onFieldBlur }) => { const { fields, setFields } = useBrowserAdvancedFieldsContext(); const handleInputChange = useCallback( @@ -64,6 +65,7 @@ export const ThrottlingFields = memo(({ validate, minColumnWidth }) => { configKey: ConfigKey.DOWNLOAD_SPEED, }); }} + onBlur={() => onFieldBlur?.(ConfigKey.DOWNLOAD_SPEED)} data-test-subj="syntheticsBrowserDownloadSpeed" append={ @@ -98,6 +100,7 @@ export const ThrottlingFields = memo(({ validate, minColumnWidth }) => { configKey: ConfigKey.UPLOAD_SPEED, }) } + onBlur={() => onFieldBlur?.(ConfigKey.UPLOAD_SPEED)} data-test-subj="syntheticsBrowserUploadSpeed" append={ @@ -131,6 +134,7 @@ export const ThrottlingFields = memo(({ validate, minColumnWidth }) => { configKey: ConfigKey.LATENCY, }) } + onBlur={() => onFieldBlur?.(ConfigKey.LATENCY)} data-test-subj="syntheticsBrowserLatency" append={ @@ -177,6 +181,7 @@ export const ThrottlingFields = memo(({ validate, minColumnWidth }) => { configKey: ConfigKey.IS_THROTTLING_ENABLED, }) } + onBlur={() => onFieldBlur?.(ConfigKey.IS_THROTTLING_ENABLED)} /> {throttlingInputs} diff --git a/x-pack/plugins/uptime/public/components/fleet_package/combo_box.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/combo_box.test.tsx index 932bce9328d4c2..7d615b2ecea1d1 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/combo_box.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/combo_box.test.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import { fireEvent } from '@testing-library/react'; import React from 'react'; import { render } from '../../lib/helper/rtl_helpers'; import { ComboBox } from './combo_box'; @@ -20,4 +21,17 @@ describe('', () => { expect(getByTestId('syntheticsFleetComboBox')).toBeInTheDocument(); }); + + it('calls onBlur', () => { + const onBlur = jest.fn(); + const { getByTestId } = render( + + ); + + const combobox = getByTestId('syntheticsFleetComboBox'); + fireEvent.focus(combobox); + fireEvent.blur(combobox); + + expect(onBlur).toHaveBeenCalledTimes(1); + }); }); diff --git a/x-pack/plugins/uptime/public/components/fleet_package/combo_box.tsx b/x-pack/plugins/uptime/public/components/fleet_package/combo_box.tsx index 7a1df79b0a59ac..16a31e8e5d623d 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/combo_box.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/combo_box.tsx @@ -10,10 +10,11 @@ import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; export interface Props { onChange: (value: string[]) => void; + onBlur?: () => void; selectedOptions: string[]; } -export const ComboBox = ({ onChange, selectedOptions, ...props }: Props) => { +export const ComboBox = ({ onChange, onBlur, selectedOptions, ...props }: Props) => { const [formattedSelectedOptions, setSelectedOptions] = useState< Array> >(selectedOptions.map((option) => ({ label: option, key: option }))); @@ -64,6 +65,7 @@ export const ComboBox = ({ onChange, selectedOptions, ...props }: Props) => { selectedOptions={formattedSelectedOptions} onCreateOption={onCreateOption} onChange={onOptionsChange} + onBlur={() => onBlur?.()} onSearchChange={onSearchChange} isInvalid={isInvalid} {...props} diff --git a/x-pack/plugins/uptime/public/components/fleet_package/common/common_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/common/common_fields.tsx index b869f1729a80e7..78569e8ba65a46 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/common/common_fields.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/common/common_fields.tsx @@ -5,14 +5,13 @@ * 2.0. */ -import React, { useEffect } from 'react'; +import { EuiFieldNumber, EuiFieldText, EuiFormRow } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiFormRow, EuiFieldText, EuiFieldNumber } from '@elastic/eui'; -import { Validation, DataStream } from '../types'; -import { ConfigKey, CommonFields as CommonFieldsType } from '../types'; +import React, { useEffect } from 'react'; import { ComboBox } from '../combo_box'; -import { OptionalLabel } from '../optional_label'; import { usePolicyConfigContext } from '../contexts'; +import { OptionalLabel } from '../optional_label'; +import { CommonFields as CommonFieldsType, ConfigKey, DataStream, Validation } from '../types'; interface Props { validate: Validation; @@ -24,9 +23,10 @@ interface Props { value: string | string[] | null; configKey: ConfigKey; }) => void; + onFieldBlur?: (field: ConfigKey) => void; } -export function CommonFields({ fields, onChange, validate }: Props) { +export function CommonFields({ fields, onChange, onFieldBlur, validate }: Props) { const { monitorType } = usePolicyConfigContext(); const isBrowser = monitorType === DataStream.BROWSER; @@ -65,6 +65,7 @@ export function CommonFields({ fields, onChange, validate }: Props) { configKey: ConfigKey.APM_SERVICE_NAME, }) } + onBlur={() => onFieldBlur?.(ConfigKey.APM_SERVICE_NAME)} data-test-subj="syntheticsAPMServiceName" /> @@ -106,6 +107,7 @@ export function CommonFields({ fields, onChange, validate }: Props) { configKey: ConfigKey.TIMEOUT, }) } + onBlur={() => onFieldBlur?.(ConfigKey.TIMEOUT)} step={'any'} /> @@ -128,6 +130,7 @@ export function CommonFields({ fields, onChange, validate }: Props) { onChange({ value, configKey: ConfigKey.TAGS })} + onBlur={() => onFieldBlur?.(ConfigKey.TAGS)} data-test-subj="syntheticsTags" /> diff --git a/x-pack/plugins/uptime/public/components/fleet_package/common/enabled.tsx b/x-pack/plugins/uptime/public/components/fleet_package/common/enabled.tsx index 09a8e9aec3719f..29f947984b3408 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/common/enabled.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/common/enabled.tsx @@ -13,9 +13,10 @@ import { ConfigKey, CommonFields } from '../types'; interface Props { fields: CommonFields; onChange: ({ value, configKey }: { value: boolean; configKey: ConfigKey }) => void; + onBlur?: () => void; } -export function Enabled({ fields, onChange }: Props) { +export function Enabled({ fields, onChange, onBlur }: Props) { return ( <> onBlur?.()} /> diff --git a/x-pack/plugins/uptime/public/components/fleet_package/common/simple_fields_wrapper.tsx b/x-pack/plugins/uptime/public/components/fleet_package/common/simple_fields_wrapper.tsx index 989763ab922752..73d49769248e32 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/common/simple_fields_wrapper.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/common/simple_fields_wrapper.tsx @@ -6,23 +6,39 @@ */ import React from 'react'; -import { ConfigKey, Validation, CommonFields as CommonFieldsType } from '../types'; -import { CommonFields } from '../common/common_fields'; -import { Enabled } from '../common/enabled'; +import { CommonFields } from './common_fields'; +import { Enabled } from './enabled'; +import { CommonFields as CommonFieldsType, ConfigKey, Validation } from '../types'; interface Props { validate: Validation; onInputChange: ({ value, configKey }: { value: unknown; configKey: ConfigKey }) => void; + onFieldBlur?: (field: ConfigKey) => void; children: React.ReactNode; fields: CommonFieldsType; } -export const SimpleFieldsWrapper = ({ validate, onInputChange, children, fields }: Props) => { +export const SimpleFieldsWrapper = ({ + validate, + onInputChange, + onFieldBlur, + children, + fields, +}: Props) => { return ( <> - + onFieldBlur?.(ConfigKey.ENABLED)} + /> {children} - + ); }; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.test.tsx index c38c93bf11d488..9a22163b9c90d5 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.test.tsx @@ -57,10 +57,13 @@ const defaultHTTPConfig = defaultConfig[DataStream.HTTP]; const defaultTCPConfig = defaultConfig[DataStream.TCP]; describe('', () => { + let onFieldBlurMock: jest.Mock | undefined; + const WrappedComponent = ({ validate = defaultValidation, isEditable = false, dataStreams = [DataStream.HTTP, DataStream.TCP, DataStream.ICMP, DataStream.BROWSER], + onFieldBlur = onFieldBlurMock, }) => { return ( @@ -69,7 +72,11 @@ describe('', () => { - + @@ -79,8 +86,15 @@ describe('', () => { ); }; + beforeEach(() => { + onFieldBlurMock = undefined; + jest.resetAllMocks(); + }); + it('renders CustomFields', async () => { - const { getByText, getByLabelText, queryByLabelText } = render(); + const { getByText, getByLabelText, queryByLabelText } = render( + + ); const monitorType = getByLabelText('Monitor Type') as HTMLInputElement; const url = getByLabelText('URL') as HTMLInputElement; const proxyUrl = getByLabelText('Proxy URL') as HTMLInputElement; @@ -366,4 +380,19 @@ describe('', () => { expect(enabled).not.toBeChecked(); }); }); + + it('calls onFieldBlur on fields', () => { + onFieldBlurMock = jest.fn(); + const { queryByLabelText } = render( + + ); + + const monitorTypeSelect = queryByLabelText('Monitor Type') as HTMLInputElement; + fireEvent.click(monitorTypeSelect); + fireEvent.blur(monitorTypeSelect); + expect(onFieldBlurMock).toHaveBeenCalledWith(ConfigKey.MONITOR_TYPE); + }); }); diff --git a/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.tsx index 638f91fb32d21c..f1e4210f206d45 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.tsx @@ -37,6 +37,7 @@ interface Props { children?: React.ReactNode; appendAdvancedFields?: React.ReactNode; minColumnWidth?: string; + onFieldBlur?: (field: ConfigKey) => void; } const dataStreamToString = [ @@ -55,7 +56,7 @@ const dataStreamToString = [ ]; export const CustomFields = memo( - ({ validate, dataStreams = [], children, appendAdvancedFields, minColumnWidth }) => { + ({ validate, dataStreams = [], children, appendAdvancedFields, minColumnWidth, onFieldBlur }) => { const { monitorType, setMonitorType, isTLSEnabled, setIsTLSEnabled, isEditable } = usePolicyConfigContext(); @@ -71,13 +72,24 @@ export const CustomFields = memo( const renderSimpleFields = (type: DataStream) => { switch (type) { case DataStream.HTTP: - return ; + return ( + onFieldBlur?.(field)} /> + ); case DataStream.ICMP: - return ; + return ( + onFieldBlur?.(field)} /> + ); case DataStream.TCP: - return ; + return ( + onFieldBlur?.(field)} /> + ); case DataStream.BROWSER: - return ; + return ( + onFieldBlur?.(field)} + /> + ); default: return null; } @@ -132,6 +144,7 @@ export const CustomFields = memo( options={dataStreamOptions} value={monitorType} onChange={(event) => setMonitorType(event.target.value as DataStream)} + onBlur={() => onFieldBlur?.(ConfigKey.MONITOR_TYPE)} data-test-subj="syntheticsMonitorTypeField" /> @@ -204,17 +217,25 @@ export const CustomFields = memo( )} {isHTTP && ( - + {appendAdvancedFields} )} {isTCP && ( - + {appendAdvancedFields} )} {isBrowser && ( - + {appendAdvancedFields} )} diff --git a/x-pack/plugins/uptime/public/components/fleet_package/header_field.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/header_field.test.tsx index 6d9e578fe53f5b..668669dab6a263 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/header_field.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/header_field.test.tsx @@ -13,8 +13,13 @@ import { Mode } from './types'; describe('', () => { const onChange = jest.fn(); + const onBlur = jest.fn(); const defaultValue = {}; + afterEach(() => { + jest.resetAllMocks(); + }); + it('renders HeaderField', () => { const { getByText, getByTestId } = render( @@ -28,6 +33,20 @@ describe('', () => { expect(value.value).toEqual('header'); }); + it('calls onBlur', () => { + const { getByTestId } = render( + + ); + + const key = getByTestId('keyValuePairsKey0') as HTMLInputElement; + const value = getByTestId('keyValuePairsValue0') as HTMLInputElement; + + fireEvent.blur(key); + fireEvent.blur(value); + + expect(onBlur).toHaveBeenCalledTimes(2); + }); + it('formats headers and handles onChange', async () => { const { getByTestId, getByText } = render( diff --git a/x-pack/plugins/uptime/public/components/fleet_package/header_field.tsx b/x-pack/plugins/uptime/public/components/fleet_package/header_field.tsx index b77b7239b8e301..c80ed6cd300f77 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/header_field.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/header_field.tsx @@ -15,6 +15,7 @@ interface Props { contentMode?: Mode; defaultValue: Record; onChange: (value: Record) => void; + onBlur?: () => void; 'data-test-subj'?: string; } @@ -22,6 +23,7 @@ export const HeaderField = ({ contentMode, defaultValue, onChange, + onBlur, 'data-test-subj': dataTestSubj, }: Props) => { const defaultValueKeys = Object.keys(defaultValue).filter((key) => key !== 'Content-Type'); // Content-Type is a secret header we hide from the user @@ -61,6 +63,7 @@ export const HeaderField = ({ } defaultPairs={headers} onChange={setHeaders} + onBlur={() => onBlur?.()} data-test-subj={dataTestSubj} /> ); diff --git a/x-pack/plugins/uptime/public/components/fleet_package/http/advanced_fields.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/http/advanced_fields.test.tsx index f8cb0d3369c80d..b84a659753a35e 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/http/advanced_fields.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/http/advanced_fields.test.tsx @@ -5,22 +5,22 @@ * 2.0. */ -import React from 'react'; import { fireEvent } from '@testing-library/react'; +import React from 'react'; import { render } from '../../../lib/helper/rtl_helpers'; -import { HTTPAdvancedFields } from './advanced_fields'; +import { + defaultHTTPAdvancedFields as defaultConfig, + HTTPAdvancedFieldsContextProvider, +} from '../contexts'; import { ConfigKey, DataStream, - HTTPMethod, HTTPAdvancedFields as HTTPAdvancedFieldsType, + HTTPMethod, Validation, } from '../types'; -import { - HTTPAdvancedFieldsContextProvider, - defaultHTTPAdvancedFields as defaultConfig, -} from '../contexts'; import { validate as centralValidation } from '../validation'; +import { HTTPAdvancedFields } from './advanced_fields'; jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({ htmlIdGenerator: () => () => `id-${Math.random()}`, @@ -46,6 +46,8 @@ jest.mock('../../../../../../../src/plugins/kibana_react/public', () => { const defaultValidation = centralValidation[DataStream.HTTP]; describe('', () => { + const onFieldBlur = jest.fn(); + const WrappedComponent = ({ defaultValues, validate = defaultValidation, @@ -57,7 +59,9 @@ describe('', () => { }) => { return ( - {children} + + {children} + ); }; @@ -129,6 +133,20 @@ describe('', () => { expect(indexResponseHeaders.checked).toBe(false); }); + it('calls onBlur', () => { + const { getByLabelText } = render(); + + const username = getByLabelText('Username') as HTMLInputElement; + const requestMethod = getByLabelText('Request method') as HTMLInputElement; + const indexResponseBody = getByLabelText('Index response body') as HTMLInputElement; + + [username, requestMethod, indexResponseBody].forEach((field) => fireEvent.blur(field)); + + expect(onFieldBlur).toHaveBeenCalledWith(ConfigKey.USERNAME); + expect(onFieldBlur).toHaveBeenCalledWith(ConfigKey.REQUEST_METHOD_CHECK); + expect(onFieldBlur).toHaveBeenCalledWith(ConfigKey.RESPONSE_BODY_INDEX); + }); + it('renders upstream fields', () => { const upstreamFieldsText = 'Monitor Advanced field section'; const { getByText } = render({upstreamFieldsText}); diff --git a/x-pack/plugins/uptime/public/components/fleet_package/http/advanced_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/http/advanced_fields.tsx index e4dd68f50f52c1..c9396ac69fbac5 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/http/advanced_fields.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/http/advanced_fields.tsx @@ -34,442 +34,457 @@ interface Props { validate: Validation; children?: React.ReactNode; minColumnWidth?: string; + onFieldBlur?: (field: ConfigKey) => void; } -export const HTTPAdvancedFields = memo(({ validate, children, minColumnWidth }) => { - const { fields, setFields } = useHTTPAdvancedFieldsContext(); - const handleInputChange = useCallback( - ({ value, configKey }: { value: unknown; configKey: ConfigKey }) => { - setFields((prevFields) => ({ ...prevFields, [configKey]: value })); - }, - [setFields] - ); +export const HTTPAdvancedFields = memo( + ({ validate, children, minColumnWidth, onFieldBlur }) => { + const { fields, setFields } = useHTTPAdvancedFieldsContext(); + const handleInputChange = useCallback( + ({ value, configKey }: { value: unknown; configKey: ConfigKey }) => { + setFields((prevFields) => ({ ...prevFields, [configKey]: value })); + }, + [setFields] + ); - return ( - - } - data-test-subj="syntheticsHTTPAdvancedFieldsAccordion" - > - - - - - } - description={ + return ( + } - data-test-subj="httpAdvancedFieldsSection" + data-test-subj="syntheticsHTTPAdvancedFieldsAccordion" > - - + + + + } - labelAppend={} - helpText={ + description={ } + data-test-subj="httpAdvancedFieldsSection" > - - handleInputChange({ - value: event.target.value, - configKey: ConfigKey.USERNAME, - }) + + } - data-test-subj="syntheticsUsername" - /> - - - } - labelAppend={} - helpText={ - - } - > - - handleInputChange({ - value: event.target.value, - configKey: ConfigKey.PASSWORD, - }) + labelAppend={} + helpText={ + } - data-test-subj="syntheticsPassword" - /> - - - } - labelAppend={} - helpText={ - + + handleInputChange({ + value: event.target.value, + configKey: ConfigKey.USERNAME, + }) + } + onBlur={() => onFieldBlur?.(ConfigKey.USERNAME)} + data-test-subj="syntheticsUsername" /> - } - > - - handleInputChange({ - value: event.target.value, - configKey: ConfigKey.PROXY_URL, - }) + + } - data-test-subj="syntheticsProxyUrl" - /> - - - } - helpText={ - - } - > - - handleInputChange({ - value: event.target.value, - configKey: ConfigKey.REQUEST_METHOD_CHECK, - }) + labelAppend={} + helpText={ + } - data-test-subj="syntheticsRequestMethod" - /> - - - } - labelAppend={} - isInvalid={!!validate[ConfigKey.REQUEST_HEADERS_CHECK]?.(fields)} - error={ - - } - helpText={ - - } - > - + > + handleInputChange({ - value, - configKey: ConfigKey.REQUEST_HEADERS_CHECK, - }), - [handleInputChange] - )} - data-test-subj="syntheticsRequestHeaders" - /> - - onFieldBlur?.(ConfigKey.PASSWORD)} + data-test-subj="syntheticsPassword" /> - } - labelAppend={} - helpText={ - - } - fullWidth - > - + + + } + labelAppend={} + helpText={ + + } + > + handleInputChange({ - value, - configKey: ConfigKey.REQUEST_BODY_CHECK, - }), - [handleInputChange] - )} - /> - - - - - onFieldBlur?.(ConfigKey.PROXY_URL)} + data-test-subj="syntheticsProxyUrl" /> - - } - description={ - - } - > - - + + - http.response.body.headers - - } - data-test-subj="syntheticsIndexResponseHeaders" - > - + } + > + + handleInputChange({ + value: event.target.value, + configKey: ConfigKey.REQUEST_METHOD_CHECK, + }) + } + onBlur={() => onFieldBlur?.(ConfigKey.REQUEST_METHOD_CHECK)} + data-test-subj="syntheticsRequestMethod" + /> + + } - onChange={(event) => - handleInputChange({ - value: event.target.checked, - configKey: ConfigKey.RESPONSE_HEADERS_INDEX, - }) + labelAppend={} + isInvalid={!!validate[ConfigKey.REQUEST_HEADERS_CHECK]?.(fields)} + error={ + } - /> - - + helpText={ - http.response.body.contents - - } - > - - handleInputChange({ value: policy, configKey: ConfigKey.RESPONSE_BODY_INDEX }), - [handleInputChange] - )} - /> - - - - + + handleInputChange({ + value, + configKey: ConfigKey.REQUEST_HEADERS_CHECK, + }), + [handleInputChange] + )} + onBlur={() => onFieldBlur?.(ConfigKey.REQUEST_HEADERS_CHECK)} + data-test-subj="syntheticsRequestHeaders" /> - - } - description={ - - } - > - + + } + labelAppend={} + helpText={ + + } + fullWidth + > + + handleInputChange({ + value, + configKey: ConfigKey.REQUEST_BODY_CHECK, + }), + [handleInputChange] + )} + onBlur={() => onFieldBlur?.(ConfigKey.REQUEST_BODY_CHECK)} /> + + + + + + } - labelAppend={} - isInvalid={!!validate[ConfigKey.RESPONSE_STATUS_CHECK]?.(fields)} - error={ + description={ } - helpText={i18n.translate( - 'xpack.uptime.createPackagePolicy.stepConfigure.httpAdvancedOptions.responseChecks.responseStatusCheck.helpText', - { - defaultMessage: - 'A list of expected status codes. Press enter to add a new code. 4xx and 5xx codes are considered down by default. Other codes are considered up.', - } - )} > - - handleInputChange({ - value, - configKey: ConfigKey.RESPONSE_STATUS_CHECK, - }) + + + + http.response.body.headers + } - data-test-subj="syntheticsResponseStatusCheck" - /> - - + + } + onChange={(event) => + handleInputChange({ + value: event.target.checked, + configKey: ConfigKey.RESPONSE_HEADERS_INDEX, + }) + } + onBlur={() => onFieldBlur?.(ConfigKey.RESPONSE_HEADERS_INDEX)} /> + + + + http.response.body.contents + + } + > + + handleInputChange({ value: policy, configKey: ConfigKey.RESPONSE_BODY_INDEX }), + [handleInputChange] + )} + onBlur={() => onFieldBlur?.(ConfigKey.RESPONSE_BODY_INDEX)} + /> + + + + + } - labelAppend={} - isInvalid={!!validate[ConfigKey.RESPONSE_HEADERS_CHECK]?.(fields)} - error={[ + description={ , - ]} - helpText={ - } > - + + } + labelAppend={} + isInvalid={!!validate[ConfigKey.RESPONSE_STATUS_CHECK]?.(fields)} + error={ + + } + helpText={i18n.translate( + 'xpack.uptime.createPackagePolicy.stepConfigure.httpAdvancedOptions.responseChecks.responseStatusCheck.helpText', + { + defaultMessage: + 'A list of expected status codes. Press enter to add a new code. 4xx and 5xx codes are considered down by default. Other codes are considered up.', + } + )} + > + handleInputChange({ value, - configKey: ConfigKey.RESPONSE_HEADERS_CHECK, - }), - [handleInputChange] - )} - data-test-subj="syntheticsResponseHeaders" - /> - - onFieldBlur?.(ConfigKey.RESPONSE_STATUS_CHECK)} + data-test-subj="syntheticsResponseStatusCheck" /> - } - labelAppend={} - helpText={i18n.translate( - 'xpack.uptime.createPackagePolicy.stepConfigure.httpAdvancedOptions.responseBodyCheckPositive.helpText', - { - defaultMessage: - 'A list of regular expressions to match the body output. Press enter to add a new expression. Only a single expression needs to match.', + + } - )} - > - - handleInputChange({ - value, - configKey: ConfigKey.RESPONSE_BODY_CHECK_POSITIVE, - }), - [handleInputChange] + labelAppend={} + isInvalid={!!validate[ConfigKey.RESPONSE_HEADERS_CHECK]?.(fields)} + error={[ + , + ]} + helpText={ + + } + > + + handleInputChange({ + value, + configKey: ConfigKey.RESPONSE_HEADERS_CHECK, + }), + [handleInputChange] + )} + onBlur={() => onFieldBlur?.(ConfigKey.RESPONSE_HEADERS_CHECK)} + data-test-subj="syntheticsResponseHeaders" + /> + + + } + labelAppend={} + helpText={i18n.translate( + 'xpack.uptime.createPackagePolicy.stepConfigure.httpAdvancedOptions.responseBodyCheckPositive.helpText', + { + defaultMessage: + 'A list of regular expressions to match the body output. Press enter to add a new expression. Only a single expression needs to match.', + } )} - data-test-subj="syntheticsResponseBodyCheckPositive" - /> - - + + handleInputChange({ + value, + configKey: ConfigKey.RESPONSE_BODY_CHECK_POSITIVE, + }), + [handleInputChange] + )} + onBlur={() => onFieldBlur?.(ConfigKey.RESPONSE_BODY_CHECK_POSITIVE)} + data-test-subj="syntheticsResponseBodyCheckPositive" /> - } - labelAppend={} - helpText={i18n.translate( - 'xpack.uptime.createPackagePolicy.stepConfigure.httpAdvancedOptions.responseBodyCheckNegative.helpText', - { - defaultMessage: - 'A list of regular expressions to match the the body output negatively. Press enter to add a new expression. Return match failed if single expression matches.', + + } - )} - > - - handleInputChange({ - value, - configKey: ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE, - }), - [handleInputChange] + labelAppend={} + helpText={i18n.translate( + 'xpack.uptime.createPackagePolicy.stepConfigure.httpAdvancedOptions.responseBodyCheckNegative.helpText', + { + defaultMessage: + 'A list of regular expressions to match the the body output negatively. Press enter to add a new expression. Return match failed if single expression matches.', + } )} - data-test-subj="syntheticsResponseBodyCheckNegative" - /> - - - {children} - - ); -}); + > + + handleInputChange({ + value, + configKey: ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE, + }), + [handleInputChange] + )} + onBlur={() => onFieldBlur?.(ConfigKey.RESPONSE_BODY_CHECK_NEGATIVE)} + data-test-subj="syntheticsResponseBodyCheckNegative" + /> + + + {children} + + ); + } +); const requestMethodOptions = Object.values(HTTPMethod).map((method) => ({ value: method, diff --git a/x-pack/plugins/uptime/public/components/fleet_package/http/simple_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/http/simple_fields.tsx index b43a115c5d8bf9..56245856f5aed0 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/http/simple_fields.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/http/simple_fields.tsx @@ -5,20 +5,21 @@ * 2.0. */ -import React, { memo, useCallback } from 'react'; +import { EuiFieldNumber, EuiFieldText, EuiFormRow } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiFormRow, EuiFieldText, EuiFieldNumber } from '@elastic/eui'; -import { ConfigKey, Validation } from '../types'; +import React, { memo, useCallback } from 'react'; +import { SimpleFieldsWrapper } from '../common/simple_fields_wrapper'; import { useHTTPSimpleFieldsContext } from '../contexts'; import { OptionalLabel } from '../optional_label'; import { ScheduleField } from '../schedule_field'; -import { SimpleFieldsWrapper } from '../common/simple_fields_wrapper'; +import { ConfigKey, Validation } from '../types'; interface Props { validate: Validation; + onFieldBlur: (field: ConfigKey) => void; // To propagate blurred state up to parents } -export const HTTPSimpleFields = memo(({ validate }) => { +export const HTTPSimpleFields = memo(({ validate, onFieldBlur }) => { const { fields, setFields } = useHTTPSimpleFieldsContext(); const handleInputChange = useCallback( ({ value, configKey }: { value: unknown; configKey: ConfigKey }) => { @@ -28,7 +29,12 @@ export const HTTPSimpleFields = memo(({ validate }) => { ); return ( - + (({ validate }) => { onChange={(event) => handleInputChange({ value: event.target.value, configKey: ConfigKey.URLS }) } + onBlur={() => onFieldBlur(ConfigKey.URLS)} data-test-subj="syntheticsUrlField" /> @@ -75,6 +82,7 @@ export const HTTPSimpleFields = memo(({ validate }) => { configKey: ConfigKey.SCHEDULE, }) } + onBlur={() => onFieldBlur(ConfigKey.SCHEDULE)} number={fields[ConfigKey.SCHEDULE].number} unit={fields[ConfigKey.SCHEDULE].unit} /> @@ -110,6 +118,7 @@ export const HTTPSimpleFields = memo(({ validate }) => { configKey: ConfigKey.MAX_REDIRECTS, }) } + onBlur={() => onFieldBlur(ConfigKey.MAX_REDIRECTS)} /> diff --git a/x-pack/plugins/uptime/public/components/fleet_package/icmp/simple_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/icmp/simple_fields.tsx index 2e2fe96e9a9f37..76058d5cc32486 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/icmp/simple_fields.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/icmp/simple_fields.tsx @@ -16,9 +16,10 @@ import { SimpleFieldsWrapper } from '../common/simple_fields_wrapper'; interface Props { validate: Validation; + onFieldBlur: (field: ConfigKey) => void; // To propagate blurred state up to parents } -export const ICMPSimpleFields = memo(({ validate }) => { +export const ICMPSimpleFields = memo(({ validate, onFieldBlur }) => { const { fields, setFields } = useICMPSimpleFieldsContext(); const handleInputChange = useCallback( ({ value, configKey }: { value: unknown; configKey: ConfigKey }) => { @@ -28,7 +29,12 @@ export const ICMPSimpleFields = memo(({ validate }) => { ); return ( - + (({ validate }) => { configKey: ConfigKey.HOSTS, }) } + onBlur={() => onFieldBlur(ConfigKey.HOSTS)} data-test-subj="syntheticsICMPHostField" /> @@ -78,6 +85,7 @@ export const ICMPSimpleFields = memo(({ validate }) => { configKey: ConfigKey.SCHEDULE, }) } + onBlur={() => onFieldBlur(ConfigKey.SCHEDULE)} number={fields[ConfigKey.SCHEDULE].number} unit={fields[ConfigKey.SCHEDULE].unit} /> @@ -113,6 +121,7 @@ export const ICMPSimpleFields = memo(({ validate }) => { configKey: ConfigKey.WAIT, }) } + onBlur={() => onFieldBlur(ConfigKey.WAIT)} step={'any'} /> diff --git a/x-pack/plugins/uptime/public/components/fleet_package/index_response_body_field.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/index_response_body_field.test.tsx index 53a96c5ec1c733..d69f66d688e352 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/index_response_body_field.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/index_response_body_field.test.tsx @@ -14,10 +14,17 @@ import { ResponseBodyIndexPolicy } from './types'; describe('', () => { const defaultDefaultValue = ResponseBodyIndexPolicy.ON_ERROR; const onChange = jest.fn(); + const onBlur = jest.fn(); const WrappedComponent = ({ defaultValue = defaultDefaultValue }) => { - return ; + return ( + + ); }; + afterEach(() => { + jest.resetAllMocks(); + }); + it('renders ResponseBodyIndexField', () => { const { getByText, getByTestId } = render(); const select = getByTestId('indexResponseBodyFieldSelect') as HTMLInputElement; @@ -41,6 +48,17 @@ describe('', () => { }); }); + it('calls onBlur', async () => { + const { getByTestId } = render(); + const select = getByTestId('indexResponseBodyFieldSelect') as HTMLInputElement; + const newPolicy = ResponseBodyIndexPolicy.ALWAYS; + + fireEvent.change(select, { target: { value: newPolicy } }); + fireEvent.blur(select); + + expect(onBlur).toHaveBeenCalledTimes(1); + }); + it('handles checkbox change', async () => { const { getByTestId, getByLabelText } = render(); const checkbox = getByLabelText('Index response body') as HTMLInputElement; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/index_response_body_field.tsx b/x-pack/plugins/uptime/public/components/fleet_package/index_response_body_field.tsx index 990b6fcf8eb568..b8b350b84d1040 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/index_response_body_field.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/index_response_body_field.tsx @@ -15,9 +15,10 @@ import { ResponseBodyIndexPolicy } from './types'; interface Props { defaultValue: ResponseBodyIndexPolicy; onChange: (responseBodyIndexPolicy: ResponseBodyIndexPolicy) => void; + onBlur?: () => void; } -export const ResponseBodyIndexField = ({ defaultValue, onChange }: Props) => { +export const ResponseBodyIndexField = ({ defaultValue, onChange, onBlur }: Props) => { const [policy, setPolicy] = useState( defaultValue !== ResponseBodyIndexPolicy.NEVER ? defaultValue : ResponseBodyIndexPolicy.ON_ERROR ); @@ -52,6 +53,7 @@ export const ResponseBodyIndexField = ({ defaultValue, onChange }: Props) => { const checkedEvent = event.target.checked; setChecked(checkedEvent); }} + onBlur={() => onBlur?.()} /> {checked && ( @@ -69,6 +71,7 @@ export const ResponseBodyIndexField = ({ defaultValue, onChange }: Props) => { onChange={(event) => { setPolicy(event.target.value as ResponseBodyIndexPolicy); }} + onBlur={() => onBlur?.()} /> )} diff --git a/x-pack/plugins/uptime/public/components/fleet_package/key_value_field.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/key_value_field.test.tsx index b0143ab9767221..bffe4fd761908b 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/key_value_field.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/key_value_field.test.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import userEvent from '@testing-library/user-event'; import React from 'react'; import { fireEvent, waitFor } from '@testing-library/react'; import { render } from '../../lib/helper/rtl_helpers'; @@ -12,6 +13,7 @@ import { KeyValuePairsField, Pair } from './key_value_field'; describe('', () => { const onChange = jest.fn(); + const onBlur = jest.fn(); const defaultDefaultValue = [['', '']] as Pair[]; const WrappedComponent = ({ defaultValue = defaultDefaultValue, @@ -21,11 +23,16 @@ describe('', () => { ); }; + afterEach(() => { + jest.resetAllMocks(); + }); + it('renders KeyValuePairsField', () => { const { getByText } = render(); expect(getByText('Key')).toBeInTheDocument(); @@ -34,6 +41,21 @@ describe('', () => { expect(getByText('Add pair')).toBeInTheDocument(); }); + it('calls onBlur', () => { + const { getByText, getByTestId } = render(); + const addPair = getByText('Add pair'); + fireEvent.click(addPair); + + const keyInput = getByTestId('keyValuePairsKey0') as HTMLInputElement; + const valueInput = getByTestId('keyValuePairsValue0') as HTMLInputElement; + + userEvent.type(keyInput, 'some-key'); + userEvent.type(valueInput, 'some-value'); + fireEvent.blur(valueInput); + + expect(onBlur).toHaveBeenCalledTimes(2); + }); + it('handles adding and editing a new row', async () => { const { getByTestId, queryByTestId, getByText } = render( diff --git a/x-pack/plugins/uptime/public/components/fleet_package/key_value_field.tsx b/x-pack/plugins/uptime/public/components/fleet_package/key_value_field.tsx index 8165577bfa530e..c4a00461cda351 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/key_value_field.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/key_value_field.tsx @@ -50,6 +50,7 @@ interface Props { addPairControlLabel: string | React.ReactElement; defaultPairs: Pair[]; onChange: (pairs: Pair[]) => void; + onBlur?: () => void; 'data-test-subj'?: string; } @@ -57,6 +58,7 @@ export const KeyValuePairsField = ({ addPairControlLabel, defaultPairs, onChange, + onBlur, 'data-test-subj': dataTestSubj, }: Props) => { const [pairs, setPairs] = useState(defaultPairs); @@ -167,6 +169,7 @@ export const KeyValuePairsField = ({ data-test-subj={`keyValuePairsKey${index}`} value={key} onChange={(event) => handleOnChange(event, index, true)} + onBlur={() => onBlur?.()} /> } endControl={ @@ -177,6 +180,7 @@ export const KeyValuePairsField = ({ data-test-subj={`keyValuePairsValue${index}`} value={value} onChange={(event) => handleOnChange(event, index, false)} + onBlur={() => onBlur?.()} /> } delimiter=":" diff --git a/x-pack/plugins/uptime/public/components/fleet_package/request_body_field.tsx b/x-pack/plugins/uptime/public/components/fleet_package/request_body_field.tsx index 6c2b6a3383b26e..a81a4b076ad70e 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/request_body_field.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/request_body_field.tsx @@ -15,6 +15,7 @@ import { CodeEditor } from './code_editor'; interface Props { onChange: (requestBody: { type: Mode; value: string }) => void; + onBlur?: () => void; type: Mode; value: string; } @@ -25,7 +26,7 @@ enum ResponseBodyType { } // TO DO: Look into whether or not code editor reports errors, in order to prevent form submission on an error -export const RequestBodyField = ({ onChange, type, value }: Props) => { +export const RequestBodyField = ({ onChange, onBlur, type, value }: Props) => { const [values, setValues] = useState>({ [ResponseBodyType.FORM]: type === Mode.FORM ? value : '', [ResponseBodyType.CODE]: type !== Mode.FORM ? value : '', @@ -93,9 +94,10 @@ export const RequestBodyField = ({ onChange, type, value }: Props) => { )} id={Mode.PLAINTEXT} languageId={MonacoEditorLangId.PLAINTEXT} - onChange={(code) => - setValues((prevValues) => ({ ...prevValues, [ResponseBodyType.CODE]: code })) - } + onChange={(code) => { + setValues((prevValues) => ({ ...prevValues, [ResponseBodyType.CODE]: code })); + onBlur?.(); + }} value={values[ResponseBodyType.CODE]} /> ), @@ -114,9 +116,10 @@ export const RequestBodyField = ({ onChange, type, value }: Props) => { )} id={Mode.JSON} languageId={MonacoEditorLangId.JSON} - onChange={(code) => - setValues((prevValues) => ({ ...prevValues, [ResponseBodyType.CODE]: code })) - } + onChange={(code) => { + setValues((prevValues) => ({ ...prevValues, [ResponseBodyType.CODE]: code })); + onBlur?.(); + }} value={values[ResponseBodyType.CODE]} /> ), @@ -135,9 +138,10 @@ export const RequestBodyField = ({ onChange, type, value }: Props) => { )} id={Mode.XML} languageId={MonacoEditorLangId.XML} - onChange={(code) => - setValues((prevValues) => ({ ...prevValues, [ResponseBodyType.CODE]: code })) - } + onChange={(code) => { + setValues((prevValues) => ({ ...prevValues, [ResponseBodyType.CODE]: code })); + onBlur?.(); + }} value={values[ResponseBodyType.CODE]} /> ), @@ -156,6 +160,7 @@ export const RequestBodyField = ({ onChange, type, value }: Props) => { } defaultPairs={defaultFormPairs} onChange={onChangeFormFields} + onBlur={() => onBlur?.()} /> ), }, diff --git a/x-pack/plugins/uptime/public/components/fleet_package/schedule_field.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/schedule_field.test.tsx index c2f99aaee21607..77f987da0df0df 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/schedule_field.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/schedule_field.test.tsx @@ -17,6 +17,7 @@ import { ScheduleUnit } from './types'; describe('', () => { const number = '1'; const unit = ScheduleUnit.MINUTES; + const onBlur = jest.fn(); const WrappedComponent = ({ allowedScheduleUnits, }: Omit) => { @@ -31,11 +32,16 @@ describe('', () => { number={config.number} unit={config.unit} onChange={(value) => setConfig(value)} + onBlur={onBlur} /> ); }; + afterEach(() => { + jest.resetAllMocks(); + }); + it('shows all options by default (allowedScheduleUnits is not provided)', () => { const { getByText } = render(); expect(getByText('Minutes')).toBeInTheDocument(); @@ -110,4 +116,21 @@ describe('', () => { expect(getByText('Seconds')).toBeInTheDocument(); }); }); + + it('calls onBlur when changed', () => { + const { getByTestId } = render( + + ); + const input = getByTestId('scheduleFieldInput') as HTMLInputElement; + const select = getByTestId('scheduleFieldSelect') as HTMLInputElement; + + userEvent.clear(input); + userEvent.type(input, '2'); + + userEvent.selectOptions(select, ScheduleUnit.MINUTES); + + userEvent.click(input); + + expect(onBlur).toHaveBeenCalledTimes(2); + }); }); diff --git a/x-pack/plugins/uptime/public/components/fleet_package/schedule_field.tsx b/x-pack/plugins/uptime/public/components/fleet_package/schedule_field.tsx index 4042821834f3fe..d24095a7824c82 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/schedule_field.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/schedule_field.tsx @@ -14,10 +14,11 @@ import { ConfigKey, MonitorFields, ScheduleUnit } from './types'; interface Props { number: string; onChange: (schedule: MonitorFields[ConfigKey.SCHEDULE]) => void; + onBlur: () => void; unit: ScheduleUnit; } -export const ScheduleField = ({ number, onChange, unit }: Props) => { +export const ScheduleField = ({ number, onChange, onBlur, unit }: Props) => { const { allowedScheduleUnits } = usePolicyConfigContext(); const options = !allowedScheduleUnits?.length ? allOptions @@ -51,6 +52,8 @@ export const ScheduleField = ({ number, onChange, unit }: Props) => { const updatedNumber = `${Math.ceil(+event.target.value)}`; onChange({ number: updatedNumber, unit }); } + + onBlur(); }} /> @@ -70,6 +73,7 @@ export const ScheduleField = ({ number, onChange, unit }: Props) => { const updatedUnit = event.target.value; onChange({ number, unit: updatedUnit as ScheduleUnit }); }} + onBlur={() => onBlur()} /> diff --git a/x-pack/plugins/uptime/public/components/fleet_package/tcp/advanced_fields.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/tcp/advanced_fields.test.tsx index 3aad8e30d58f7f..e070d071587669 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/tcp/advanced_fields.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/tcp/advanced_fields.test.tsx @@ -24,13 +24,15 @@ describe('', () => { const WrappedComponent = ({ defaultValues = defaultConfig, children, + onFieldBlur, }: { defaultValues?: TCPAdvancedFieldsType; children?: React.ReactNode; + onFieldBlur?: (field: ConfigKey) => void; }) => { return ( - {children} + {children} ); }; @@ -59,6 +61,17 @@ describe('', () => { expect(requestPayload.value).toEqual('success'); }); + it('calls onBlur on fields', () => { + const onFieldBlur = jest.fn(); + const { getByLabelText } = render(); + + const requestPayload = getByLabelText('Request payload') as HTMLInputElement; + + fireEvent.change(requestPayload, { target: { value: 'success' } }); + fireEvent.blur(requestPayload); + expect(onFieldBlur).toHaveBeenCalledWith(ConfigKey.REQUEST_SEND_CHECK); + }); + it('shows resolve hostnames locally field when proxy url is filled for tcp monitors', () => { const { getByLabelText, queryByLabelText } = render(); diff --git a/x-pack/plugins/uptime/public/components/fleet_package/tcp/advanced_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/tcp/advanced_fields.tsx index ab185b34085bc6..7598f1ec0734cb 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/tcp/advanced_fields.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/tcp/advanced_fields.tsx @@ -19,9 +19,10 @@ import { OptionalLabel } from '../optional_label'; interface Props { children?: React.ReactNode; minColumnWidth?: string; + onFieldBlur?: (field: ConfigKey) => void; } -export const TCPAdvancedFields = memo(({ children, minColumnWidth }) => { +export const TCPAdvancedFields = memo(({ children, minColumnWidth, onFieldBlur }) => { const { fields, setFields } = useTCPAdvancedFieldsContext(); const handleInputChange = useCallback( @@ -79,6 +80,7 @@ export const TCPAdvancedFields = memo(({ children, minColumnWidth }) => { configKey: ConfigKey.PROXY_URL, }) } + onBlur={() => onFieldBlur?.(ConfigKey.PROXY_URL)} data-test-subj="syntheticsProxyUrl" /> @@ -127,6 +129,7 @@ export const TCPAdvancedFields = memo(({ children, minColumnWidth }) => { }), [handleInputChange] )} + onBlur={() => onFieldBlur?.(ConfigKey.REQUEST_SEND_CHECK)} data-test-subj="syntheticsTCPRequestSendCheck" /> @@ -173,6 +176,7 @@ export const TCPAdvancedFields = memo(({ children, minColumnWidth }) => { }), [handleInputChange] )} + onBlur={() => onFieldBlur?.(ConfigKey.RESPONSE_RECEIVE_CHECK)} data-test-subj="syntheticsTCPResponseReceiveCheck" /> diff --git a/x-pack/plugins/uptime/public/components/fleet_package/tcp/simple_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/tcp/simple_fields.tsx index 89b89de88a2e1f..4eb4ed05e073b7 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/tcp/simple_fields.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/tcp/simple_fields.tsx @@ -15,9 +15,10 @@ import { SimpleFieldsWrapper } from '../common/simple_fields_wrapper'; interface Props { validate: Validation; + onFieldBlur: (field: ConfigKey) => void; // To propagate blurred state up to parents } -export const TCPSimpleFields = memo(({ validate }) => { +export const TCPSimpleFields = memo(({ validate, onFieldBlur }) => { const { fields, setFields } = useTCPSimpleFieldsContext(); const handleInputChange = useCallback( ({ value, configKey }: { value: unknown; configKey: ConfigKey }) => { @@ -27,7 +28,12 @@ export const TCPSimpleFields = memo(({ validate }) => { ); return ( - + (({ validate }) => { configKey: ConfigKey.HOSTS, }) } + onBlur={() => onFieldBlur(ConfigKey.HOSTS)} data-test-subj="syntheticsTCPHostField" /> @@ -78,6 +85,7 @@ export const TCPSimpleFields = memo(({ validate }) => { configKey: ConfigKey.SCHEDULE, }) } + onBlur={() => onFieldBlur(ConfigKey.SCHEDULE)} number={fields[ConfigKey.SCHEDULE].number} unit={fields[ConfigKey.SCHEDULE].unit} /> diff --git a/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/locations.test.tsx b/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/locations.test.tsx index ccc3e7b619c685..7cf8f0a8204a1b 100644 --- a/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/locations.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/locations.test.tsx @@ -6,11 +6,11 @@ */ import React from 'react'; -import { screen } from '@testing-library/react'; +import { fireEvent, screen } from '@testing-library/react'; import { render } from '../../../lib/helper/rtl_helpers'; import { ServiceLocations } from './locations'; -describe('', () => { +describe('', () => { const setLocations = jest.fn(); const location = { label: 'US Central', @@ -21,6 +21,7 @@ describe('', () => { }, url: 'url', }; + const locationTestSubId = `syntheticsServiceLocation--${location.id}`; const state = { monitorManagementList: { locations: [location], @@ -62,4 +63,35 @@ describe('', () => { expect(screen.getByText('At least one service location must be specified')).toBeInTheDocument(); }); + + it('checks unchecks location', () => { + const { getByTestId } = render( + , + { state } + ); + + const checkbox = getByTestId(locationTestSubId) as HTMLInputElement; + expect(checkbox.checked).toEqual(false); + fireEvent.click(checkbox); + + expect(setLocations).toHaveBeenCalled(); + }); + + it('calls onBlur', () => { + const onBlur = jest.fn(); + const { getByTestId } = render( + , + { state } + ); + + const checkbox = getByTestId(locationTestSubId) as HTMLInputElement; + fireEvent.click(checkbox); + fireEvent.blur(checkbox); + expect(onBlur).toHaveBeenCalledTimes(1); + }); }); diff --git a/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/locations.tsx b/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/locations.tsx index 1877cc4ade126b..49a232312aa272 100644 --- a/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/locations.tsx +++ b/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/locations.tsx @@ -16,9 +16,10 @@ interface Props { selectedLocations: ServiceLocation[]; setLocations: React.Dispatch>; isInvalid: boolean; + onBlur?: () => void; } -export const ServiceLocations = ({ selectedLocations, setLocations, isInvalid }: Props) => { +export const ServiceLocations = ({ selectedLocations, setLocations, isInvalid, onBlur }: Props) => { const [error, setError] = useState(null); const [checkboxIdToSelectedMap, setCheckboxIdToSelectedMap] = useState>( {} @@ -58,6 +59,7 @@ export const ServiceLocations = ({ selectedLocations, setLocations, isInvalid }: }))} idToSelectedMap={checkboxIdToSelectedMap} onChange={(id) => onLocationChange(id)} + onBlur={() => onBlur?.()} /> ); diff --git a/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/monitor_advanced_fields.tsx b/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/monitor_advanced_fields.tsx index 21ef7d12dcd595..3b416436f9cc44 100644 --- a/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/monitor_advanced_fields.tsx +++ b/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/monitor_advanced_fields.tsx @@ -4,88 +4,92 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { memo } from 'react'; +import { EuiFieldText, EuiFormRow, EuiLink, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiFormRow, EuiSpacer, EuiLink, EuiFieldText } from '@elastic/eui'; -import type { Validation } from '../../../../common/types'; +import React, { memo } from 'react'; +import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; import { ConfigKey } from '../../../../common/runtime_types'; +import type { Validation } from '../../../../common/types'; import { DescribedFormGroupWithWrap } from '../../fleet_package/common/described_form_group_with_wrap'; import { usePolicyConfigContext } from '../../fleet_package/contexts'; -import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; interface Props { validate: Validation; minColumnWidth?: string; + onFieldBlur?: (field: ConfigKey) => void; } -export const MonitorManagementAdvancedFields = memo(({ validate, minColumnWidth }) => { - const { namespace, setNamespace } = usePolicyConfigContext(); +export const MonitorManagementAdvancedFields = memo( + ({ validate, minColumnWidth, onFieldBlur }) => { + const { namespace, setNamespace } = usePolicyConfigContext(); - const namespaceErrorMsg = validate[ConfigKey.NAMESPACE]?.({ - [ConfigKey.NAMESPACE]: namespace, - }); - const isNamespaceInvalid = !!namespaceErrorMsg; - const { services } = useKibana(); + const namespaceErrorMsg = validate[ConfigKey.NAMESPACE]?.({ + [ConfigKey.NAMESPACE]: namespace, + }); + const isNamespaceInvalid = !!namespaceErrorMsg; + const { services } = useKibana(); - return ( - - - - } - description={ - - } - data-test-subj="monitorAdvancedFieldsSection" - > - - + return ( + + + } - helpText={ + description={ - - - ), - }} + id="xpack.uptime.monitorManagement.monitorAdvancedOptions.dataStreamConfiguration.description" + defaultMessage="Configure additional Data Stream options." /> } + data-test-subj="monitorAdvancedFieldsSection" > - setNamespace(event.target.value)} - required={true} + + - - - ); -}); + error={namespaceErrorMsg} + label={ + + } + helpText={ + + + + ), + }} + /> + } + > + setNamespace(event.target.value)} + required={true} + isInvalid={isNamespaceInvalid} + fullWidth={true} + name="namespace" + onBlur={() => onFieldBlur?.(ConfigKey.NAMESPACE)} + /> + + + ); + } +); diff --git a/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/monitor_config.test.tsx b/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/monitor_config.test.tsx new file mode 100644 index 00000000000000..833ab9b23e452e --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/monitor_config.test.tsx @@ -0,0 +1,61 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { fireEvent } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import React from 'react'; +import { render } from '../../../lib/helper/rtl_helpers'; +import { + BrowserContextProvider, + HTTPContextProvider, + ICMPSimpleFieldsContextProvider, + PolicyConfigContextProvider, + TCPContextProvider, + TLSFieldsContextProvider, +} from '../../fleet_package/contexts'; +import { MonitorConfig } from './monitor_config'; + +describe('', () => { + const WrappedComponent = ({ isEditable = true, isEdit = false }) => { + return ( + + + + + + + + + + + + + + ); + }; + + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('renders MonitorConfig', async () => { + const { getByLabelText } = render(); + const monitorName = getByLabelText('Monitor name') as HTMLInputElement; + expect(monitorName).toBeInTheDocument(); + }); + + it('only shows validation errors when field is interacted with', async () => { + const { getByLabelText, queryByText } = render(); + const monitorName = getByLabelText('Monitor name') as HTMLInputElement; + expect(monitorName).toBeInTheDocument(); + + userEvent.clear(monitorName); + expect(queryByText('Monitor name is required')).toBeNull(); + fireEvent.blur(monitorName); + expect(queryByText('Monitor name is required')).not.toBeNull(); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/monitor_config.tsx b/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/monitor_config.tsx index c12e3a3f49939b..c2e828fe332115 100644 --- a/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/monitor_config.tsx +++ b/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/monitor_config.tsx @@ -44,10 +44,15 @@ export const MonitorConfig = ({ isEdit = false }: { isEdit: boolean }) => { defaultConfig: defaultConfig[monitorType], }); + const [hasBeenSubmitted, setHasBeenSubmitted] = useState(false); const [testRun, setTestRun] = useState(); const [isTestRunInProgress, setIsTestRunInProgress] = useState(false); const [isFlyoutOpen, setIsFlyoutOpen] = useState(false); + const handleFormSubmit = () => { + setHasBeenSubmitted(true); + }; + const handleTestNow = () => { if (config) { setTestRun({ id: uuidv4(), monitor: config as MonitorFieldsType }); @@ -90,7 +95,7 @@ export const MonitorConfig = ({ isEdit = false }: { isEdit: boolean }) => { return ( <> - + {flyout} @@ -100,6 +105,7 @@ export const MonitorConfig = ({ isEdit = false }: { isEdit: boolean }) => { onTestNow={handleTestNow} testRun={testRun} isTestRunInProgress={isTestRunInProgress} + onSave={handleFormSubmit} /> ); diff --git a/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/monitor_fields.test.tsx b/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/monitor_fields.test.tsx new file mode 100644 index 00000000000000..7320410ae52eb8 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/monitor_fields.test.tsx @@ -0,0 +1,94 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { fireEvent } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import React from 'react'; +import { ConfigKey, DataStream, HTTPFields } from '../../../../common/runtime_types'; +import { render } from '../../../lib/helper/rtl_helpers'; +import { + BrowserContextProvider, + HTTPContextProvider, + ICMPSimpleFieldsContextProvider, + PolicyConfigContextProvider, + TCPContextProvider, + TLSFieldsContextProvider, +} from '../../fleet_package/contexts'; +import { defaultConfig } from '../../fleet_package/synthetics_policy_create_extension'; +import { MonitorFields } from './monitor_fields'; + +const defaultHTTPConfig = defaultConfig[DataStream.HTTP] as HTTPFields; + +describe('', () => { + const WrappedComponent = ({ + isEditable = true, + isFormSubmitted = false, + defaultSimpleHttpFields = defaultHTTPConfig, + }: { + isEditable?: boolean; + isFormSubmitted?: boolean; + defaultSimpleHttpFields?: HTTPFields; + }) => { + return ( + + + + + + + + + + + + + + ); + }; + + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('renders MonitorFields', async () => { + const { getByLabelText } = render(); + const monitorName = getByLabelText('URL') as HTMLInputElement; + expect(monitorName).toBeInTheDocument(); + }); + + it('only shows validation errors when field has been interacted with', async () => { + const { getByLabelText, queryByText } = render(); + const monitorName = getByLabelText('Monitor name') as HTMLInputElement; + expect(monitorName).toBeInTheDocument(); + + userEvent.clear(monitorName); + expect(queryByText('Monitor name is required')).toBeNull(); + fireEvent.blur(monitorName); + expect(queryByText('Monitor name is required')).not.toBeNull(); + }); + + it('shows all validations errors when form is submitted', async () => { + const httpInvalidValues = { ...defaultHTTPConfig, [ConfigKey.NAME]: '', [ConfigKey.URLS]: '' }; + const { queryByText } = render( + + ); + + expect(queryByText('Monitor name is required')).not.toBeNull(); + expect(queryByText('URL is required')).not.toBeNull(); + }); + + it('does not show validation errors initially', async () => { + const httpInvalidValues = { ...defaultHTTPConfig, [ConfigKey.NAME]: '', [ConfigKey.URLS]: '' }; + const { queryByText } = render( + + ); + + expect(queryByText('Monitor name is required')).toBeNull(); + expect(queryByText('URL is required')).toBeNull(); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/monitor_fields.tsx b/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/monitor_fields.tsx index 32783460aed090..655bb5cecac85c 100644 --- a/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/monitor_fields.tsx +++ b/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/monitor_fields.tsx @@ -5,9 +5,9 @@ * 2.0. */ -import React from 'react'; +import React, { useMemo, useState } from 'react'; import { EuiForm } from '@elastic/eui'; -import { DataStream } from '../../../../common/runtime_types'; +import { ConfigKey, DataStream } from '../../../../common/runtime_types'; import { usePolicyConfigContext } from '../../fleet_package/contexts'; import { CustomFields } from '../../fleet_package/custom_fields'; @@ -17,22 +17,44 @@ import { MonitorManagementAdvancedFields } from './monitor_advanced_fields'; const MIN_COLUMN_WRAP_WIDTH = '360px'; -export const MonitorFields = () => { +export const MonitorFields = ({ isFormSubmitted = false }: { isFormSubmitted?: boolean }) => { const { monitorType } = usePolicyConfigContext(); + + const [touchedFieldsHash, setTouchedFieldsHash] = useState>({}); + + const fieldValidation = useMemo(() => { + const validatorsHash = { ...validate[monitorType] }; + if (!isFormSubmitted) { + Object.keys(validatorsHash).map((key) => { + if (!touchedFieldsHash[key]) { + validatorsHash[key as ConfigKey] = undefined; + } + }); + } + + return validatorsHash; + }, [isFormSubmitted, monitorType, touchedFieldsHash]); + + const handleFieldBlur = (field: ConfigKey) => { + setTouchedFieldsHash((hash) => ({ ...hash, [field]: true })); + }; + return ( } + onFieldBlur={handleFieldBlur} > - + ); diff --git a/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/monitor_name_location.tsx b/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/monitor_name_location.tsx index 7ba80f411c6f13..8e8df803c7d75a 100644 --- a/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/monitor_name_location.tsx +++ b/x-pack/plugins/uptime/public/components/monitor_management/monitor_config/monitor_name_location.tsx @@ -17,9 +17,10 @@ import { useMonitorName } from './use_monitor_name'; interface Props { validate: Validation; + onFieldBlur?: (field: ConfigKey) => void; } -export const MonitorNameAndLocation = ({ validate }: Props) => { +export const MonitorNameAndLocation = ({ validate, onFieldBlur }: Props) => { const { name, setName, locations = [], setLocations } = usePolicyConfigContext(); const isNameInvalid = !!validate[ConfigKey.NAME]?.({ [ConfigKey.NAME]: name }); const isLocationsInvalid = !!validate[ConfigKey.LOCATIONS]?.({ @@ -64,6 +65,7 @@ export const MonitorNameAndLocation = ({ validate }: Props) => { fullWidth={true} name="name" onChange={(event) => setLocalName(event.target.value)} + onBlur={() => onFieldBlur?.(ConfigKey.NAME)} data-test-subj="monitorManagementMonitorName" /> @@ -71,6 +73,7 @@ export const MonitorNameAndLocation = ({ validate }: Props) => { setLocations={setLocations} selectedLocations={locations} isInvalid={isLocationsInvalid} + onBlur={() => onFieldBlur?.(ConfigKey.LOCATIONS)} /> ); From 3030e6e499ede92b7d03cb5e8a0f30f76c6620d7 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Wed, 23 Feb 2022 19:06:16 +0200 Subject: [PATCH 018/137] [i18n] [main] Integrate 8.1.0 Translations (#126259) --- .../translations/translations/ja-JP.json | 500 +++++++++++++++++- 1 file changed, 493 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 881984cc719eb1..8bd17596508973 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -146,6 +146,7 @@ "xpack.lens.chartSwitch.experimentalLabel": "テクニカルプレビュー", "xpack.lens.chartSwitch.noResults": "{term}の結果が見つかりませんでした。", "xpack.lens.chartTitle.unsaved": "保存されていないビジュアライゼーション", + "xpack.lens.chartWarnings.number": "{warningsCount} {warningsCount, plural, other {警告}}", "xpack.lens.configPanel.addLayerButton": "レイヤーを追加", "xpack.lens.configPanel.color.tooltip.auto": "カスタム色を指定しない場合、Lensは自動的に色を選択します。", "xpack.lens.configPanel.color.tooltip.custom": "[自動]モードに戻すには、カスタム色をオフにしてください。", @@ -263,6 +264,7 @@ "xpack.lens.dynamicColoring.customPaletteAriaLabel": "色を反転", "xpack.lens.editorFrame.buildExpressionError": "グラフの準備中に予期しないエラーが発生しました", "xpack.lens.editorFrame.colorIndicatorLabel": "このディメンションの色:{hex}", + "xpack.lens.editorFrame.configurationFailureMoreErrors": " +{errors} {errors, plural, other {エラー}}", "xpack.lens.editorFrame.dataFailure": "データの読み込み中にエラーが発生しました。", "xpack.lens.editorFrame.dataViewNotFound": "データビューが見つかりません", "xpack.lens.editorFrame.dataViewReconfigure": "データビュー管理ページで再作成", @@ -274,6 +276,7 @@ "xpack.lens.editorFrame.expressionFailureMessage": "リクエストエラー:{type}, {reason}", "xpack.lens.editorFrame.expressionFailureMessageWithContext": "リクエストエラー:{type}、{context}の{reason}", "xpack.lens.editorFrame.expressionMissingDatasource": "ビジュアライゼーションのデータソースが見つかりませんでした", + "xpack.lens.editorFrame.expressionMissingDataView": "{count, plural, other {個のデータビュー}}が見つかりませんでした:{ids}", "xpack.lens.editorFrame.expressionMissingVisualizationType": "ビジュアライゼーションタイプが見つかりません。", "xpack.lens.editorFrame.goToForums": "リクエストとフィードバック", "xpack.lens.editorFrame.invisibleIndicatorLabel": "このディメンションは現在グラフに表示されません", @@ -346,10 +349,12 @@ "xpack.lens.formulaDocumentation.weekOverWeek": "週単位", "xpack.lens.formulaDocumentationHeading": "仕組み", "xpack.lens.formulaEnableWordWrapLabel": "単語の折り返しを有効にする", + "xpack.lens.formulaErrorCount": "{count} {count, plural, other {# 件のエラー}}", "xpack.lens.formulaExampleMarkdown": "例", "xpack.lens.formulaFrequentlyUsedHeading": "一般的な式", "xpack.lens.formulaPlaceholderText": "関数を演算と組み合わせて式を入力します。例:", "xpack.lens.formulaSearchPlaceholder": "検索関数", + "xpack.lens.formulaWarningCount": "{count} {count, plural, other {件の警告}}", "xpack.lens.functions.counterRate.args.byHelpText": "カウンターレート計算を分割する列", "xpack.lens.functions.counterRate.args.inputColumnIdHelpText": "カウンターレートを計算する列", "xpack.lens.functions.counterRate.args.outputColumnIdHelpText": "結果のカウンターレートを格納する列", @@ -490,6 +495,7 @@ "xpack.lens.indexPattern.formulaExpressionNotHandled": "式{operation}の演算には次のパラメーターがありません:{params}", "xpack.lens.indexPattern.formulaExpressionParseError": "式{expression}を解析できません", "xpack.lens.indexPattern.formulaExpressionWrongType": "式の演算{operation}のパラメーターの型が正しくありません:{params}", + "xpack.lens.indexPattern.formulaFieldNotFound": "{variablesLength, plural, other {フィールド}} {variablesList}が見つかりません", "xpack.lens.indexPattern.formulaFieldNotRequired": "演算{operation}ではどのフィールドも引数として使用できません", "xpack.lens.indexPattern.formulaFieldValue": "フィールド", "xpack.lens.indexPattern.formulaFilterableHelpText": "指定されたフィルターは式全体に適用されます。", @@ -542,6 +548,7 @@ "xpack.lens.indexPattern.min": "最低", "xpack.lens.indexPattern.min.description": "集約されたドキュメントから抽出された数値の最小値を返す単一値メトリック集約。", "xpack.lens.indexPattern.minOf": "{name} の最低値", + "xpack.lens.indexPattern.missingDataView": "{count, plural, other {個のデータビュー}}({count, plural, other {個のID}}: {indexpatterns})が見つかりません", "xpack.lens.indexPattern.missingFieldLabel": "見つからないフィールド", "xpack.lens.indexPattern.missingReferenceError": "\"{dimensionLabel}\"は完全に構成されていません", "xpack.lens.indexPattern.moveToWorkspace": "{field}をワークスペースに追加", @@ -559,10 +566,12 @@ "xpack.lens.indexPattern.movingAverage.windowLimitations": "ウィンドウには現在の値が含まれません。", "xpack.lens.indexPattern.movingAverageOf": "{name} の移動平均", "xpack.lens.indexPattern.multipleDateHistogramsError": "\"{dimensionLabel}\"は唯一の日付ヒストグラムではありません。時間シフトを使用するときには、1つの日付ヒストグラムのみを使用していることを確認してください。", + "xpack.lens.indexPattern.multipleTermsOf": "上位の値{name} + {count} {count, plural, other {個のその他の値}}", "xpack.lens.indexPattern.noDataViewDescription": "データビューを作成するか、別のデータソースに切り替えてください", "xpack.lens.indexPattern.noDataViewsLabel": "データビューがありません", "xpack.lens.indexPattern.numberFormatLabel": "数字", "xpack.lens.indexPattern.ofDocumentsLabel": "ドキュメント", + "xpack.lens.indexPattern.operationsNotFound": "{operationLength, plural, other {個の演算}} {operationsList}が見つかりました", "xpack.lens.indexPattern.otherDocsLabel": "その他", "xpack.lens.indexPattern.overall_metric": "メトリック:数値", "xpack.lens.indexPattern.overallAverageOf": "{name}の全体平均値", @@ -578,7 +587,6 @@ "xpack.lens.indexPattern.percentile.errorMessage": "パーセンタイルは1~99の範囲の整数でなければなりません。", "xpack.lens.indexPattern.percentile.percentileValue": "パーセンタイル", "xpack.lens.indexPattern.percentile.signature": "フィールド:文字列、[percentile]:数値", - "xpack.lens.indexPattern.percentileOf": "{name}の{percentile, selectordinal, other {#}}パーセンタイル", "xpack.lens.indexPattern.pinnedTopValuesLabel": "{field}のフィルター", "xpack.lens.indexPattern.precisionErrorWarning": "データのインデックス方法のため、このビジュアライゼーションの{name}は近似される場合があります。{topValues}の数を増やすか、正確な結果を得るには{topValues}ではなく{filters}を使用してください。この制限の詳細については、{link}。", "xpack.lens.indexPattern.precisionErrorWarning.filters": "フィルター", @@ -629,6 +637,7 @@ "xpack.lens.indexPattern.terms.addaFilter": "フィールドの追加", "xpack.lens.indexPattern.terms.addField": "フィールドの追加", "xpack.lens.indexPattern.terms.advancedSettings": "高度な設定", + "xpack.lens.indexPattern.terms.chooseFields": "{count, plural, other {個のフィールド}}", "xpack.lens.indexPattern.terms.deleteButtonAriaLabel": "削除", "xpack.lens.indexPattern.terms.deleteButtonDisabled": "この関数には定義された1つのフィールドの最小値が必須です", "xpack.lens.indexPattern.terms.deleteButtonLabel": "削除", @@ -681,6 +690,7 @@ "xpack.lens.indexPatterns.addFieldButton": "フィールドをデータビューに追加", "xpack.lens.indexPatterns.clearFiltersLabel": "名前とタイプフィルターを消去", "xpack.lens.indexPatterns.fieldFiltersLabel": "タイプでフィルタリング", + "xpack.lens.indexPatterns.fieldSearchLiveRegion": "{availableFields}使用可能な{availableFields, plural, other {フィールド}}。{emptyFields}空の{emptyFields, plural, other {フィールド}}。 {metaFields}メタ{metaFields, plural, other {フィールド}}。", "xpack.lens.indexPatterns.filterByNameLabel": "検索フィールド名", "xpack.lens.indexPatterns.manageFieldButton": "データビューフィールドを管理", "xpack.lens.indexPatterns.noAvailableDataLabel": "データを含むフィールドはありません。", @@ -971,7 +981,9 @@ "xpack.lens.xyVisualization.barHorizontalFullLabel": "横棒", "xpack.lens.xyVisualization.barHorizontalLabel": "H.棒", "xpack.lens.xyVisualization.barLabel": "縦棒", + "xpack.lens.xyVisualization.dataFailureSplitLong": "{layers, plural, other {レイヤー}} {layersList} には {axis} のフィールドが{layers, plural, other {必要です}}。", "xpack.lens.xyVisualization.dataFailureSplitShort": "{axis} がありません。", + "xpack.lens.xyVisualization.dataFailureYLong": "{layers, plural, other {レイヤー}} {layersList} には {axis} のフィールドが{layers, plural, other {必要です}}。", "xpack.lens.xyVisualization.dataFailureYShort": "{axis} がありません。", "xpack.lens.xyVisualization.dataTypeFailureXLong": "{axis}のデータ型が一致しません。日付と数値間隔型を混合することはできません。", "xpack.lens.xyVisualization.dataTypeFailureXOrdinalLong": "{axis}のデータ型が一致しません。別の関数を使用してください。", @@ -1029,6 +1041,7 @@ "advancedSettings.form.cancelButtonLabel": "変更をキャンセル", "advancedSettings.form.clearNoSearchResultText": "(検索結果を消去)", "advancedSettings.form.clearSearchResultText": "(検索結果を消去)", + "advancedSettings.form.countOfSettingsChanged": "{unsavedCount}保存されていない {unsavedCount, plural, other {個の設定} }{hiddenCount, plural, other {, # 非表示} }", "advancedSettings.form.noSearchResultText": "{queryText} {clearSearch}の設定が見つかりません", "advancedSettings.form.requiresPageReloadToastButtonLabel": "ページを再読み込み", "advancedSettings.form.requiresPageReloadToastDescription": "設定を有効にするためにページの再読み込みが必要です。", @@ -1040,6 +1053,8 @@ "advancedSettings.searchBar.unableToParseQueryErrorMessage": "クエリをパースできません", "advancedSettings.searchBarAriaLabel": "高度な設定を検索", "advancedSettings.voiceAnnouncement.ariaLabel": "詳細設定結果情報", + "advancedSettings.voiceAnnouncement.noSearchResultScreenReaderMessage": "{sectionLenght, plural, other {# セクション}}に{optionLenght, plural, other {# オプション}}があります。", + "advancedSettings.voiceAnnouncement.searchResultScreenReaderMessage": "{query} を検索しました。{sectionLenght, plural, other {# セクション}}に{optionLenght, plural, other {# オプション}}があります。", "alerts.documentationTitle": "ドキュメンテーションを表示", "alerts.noPermissionsMessage": "アラートを表示するには、Kibanaスペースでアラート機能の権限が必要です。詳細については、Kibana管理者に連絡してください。", "alerts.noPermissionsTitle": "Kibana機能権限が必要です", @@ -1164,6 +1179,7 @@ "console.settingsPage.refreshingDataDescription": "コンソールは、Elasticsearchをクエリして自動入力候補を更新します。帯域幅コストを削減するには、更新頻度を下げることをお勧めします。", "console.settingsPage.refreshingDataLabel": "更新頻度", "console.settingsPage.refreshInterval.everyHourTimeInterval": "毎時", + "console.settingsPage.refreshInterval.everyNMinutesTimeInterval": "{value} {value, plural, other {分}}ごと", "console.settingsPage.refreshInterval.onceTimeInterval": "コンソールの読み込み時に1回", "console.settingsPage.saveButtonLabel": "保存", "console.settingsPage.savingRequestsToHistoryMessage": "履歴へのリクエストの保存を無効にしてください", @@ -1315,6 +1331,7 @@ "core.euiColumnSelector.searchcolumns": "列を検索", "core.euiColumnSelector.selectAll": "すべて表示", "core.euiColumnSorting.button": "フィールドの並べ替え", + "core.euiColumnSorting.buttonActive": "{numberOfSortedFields, plural, other {# 個のフィールド}}が並べ替えられました", "core.euiColumnSorting.clearAll": "並び替えを消去", "core.euiColumnSorting.emptySorting": "現在並び替えられているフィールドはありません", "core.euiColumnSorting.pickFields": "並び替え基準でフィールドの選択", @@ -1519,6 +1536,7 @@ "core.notifications.unableUpdateUISettingNotificationMessageTitle": "UI 設定を更新できません", "core.savedObjects.deprecations.unknownTypes.manualSteps.1": "無効なプラグインを有効にしてから、Kibanaを再起動してください。", "core.savedObjects.deprecations.unknownTypes.manualSteps.2": "無効なプラグインがないか、有効にしても問題が解決しない場合は、ドキュメントを削除してください。", + "core.savedObjects.deprecations.unknownTypes.message": "Kibanaシステムインデックスで不明な型の{objectCount, plural, other {# 個のオブジェクト}}が{objectCount, plural, other {}}見つかりました。不明なsavedObject型のアップグレードはサポートされていません。今後アップグレードが成功することを保証するには、プラグインを再有効化するか、これらのドキュメントをKibanaインデックスから削除してください", "core.savedObjects.deprecations.unknownTypes.title": "Kibanaシステムインデックスに不明な型の保存されたオブジェクトがあります", "core.status.greenTitle": "緑", "core.status.redTitle": "赤", @@ -1942,6 +1960,7 @@ "data.inspector.table.rawCSVButtonLabel": "CSV", "data.inspector.table.rawCSVButtonTooltip": "日付をタイムスタンプとしてなど、提供されたデータをそのままダウンロードします", "data.inspector.table.tableLabel": "テーブル{index}", + "data.inspector.table.tablesDescription": "合計で{tablesCount, plural, other {# 個のテーブル} }があります", "data.inspector.table.tableSelectorLabel": "選択済み:", "data.kueryAutocomplete.andOperatorDescription": "{bothArguments} が true であることを条件とする", "data.kueryAutocomplete.andOperatorDescription.bothArgumentsText": "両方の引数", @@ -2618,8 +2637,13 @@ "data.search.searchSource.requestTimeDescription": "ブラウザから Elasticsearch にリクエストが送信され返されるまでの所要時間です。リクエストがキューで待機していた時間は含まれません。", "data.search.searchSource.requestTimeLabel": "リクエスト時間", "data.search.searchSource.requestTimeValue": "{requestTime}ms", + "data.search.timeBuckets.dayLabel": "{amount, plural, other {# 日}}", + "data.search.timeBuckets.hourLabel": "{amount, plural, other {# 時間}}", "data.search.timeBuckets.infinityLabel": "1年を超える", + "data.search.timeBuckets.millisecondLabel": "{amount, plural, other {# ミリ秒}}", + "data.search.timeBuckets.minuteLabel": "{amount, plural, other {# 分}}", "data.search.timeBuckets.monthLabel": "1か月", + "data.search.timeBuckets.secondLabel": "{amount, plural, other {# 秒}}", "data.search.timeBuckets.yearLabel": "1年", "data.search.timeoutContactAdmin": "クエリがタイムアウトしました。実行時間を延長するには、システム管理者に問い合わせてください。", "data.search.timeoutIncreaseSetting": "クエリがタイムアウトしました。検索タイムアウト詳細設定で実行時間を延長します。", @@ -2876,6 +2900,7 @@ "discover.hideChart": "グラフを非表示", "discover.histogramOfFoundDocumentsAriaLabel": "検出されたドキュメントのヒストグラム", "discover.hitCountSpinnerAriaLabel": "読み込み中の最終一致件数", + "discover.hitsPluralTitle": "{formattedHits} {hits, plural, other {一致}}", "discover.howToSeeOtherMatchingDocumentsDescription": "これらは検索条件に一致した初めの {sampleSize} 件のドキュメントです。他の結果を表示するには検索条件を絞ってください。", "discover.howToSeeOtherMatchingDocumentsDescriptionGrid": "これらは検索条件に一致した初めの {sampleSize} 件のドキュメントです。他の結果を表示するには検索条件を絞ってください。", "discover.inspectorRequestDataTitleChart": "グラフデータ", @@ -2924,6 +2949,7 @@ "discover.openOptionsPopover.documentExplorerText": "ドキュメントエクスプローラー", "discover.openOptionsPopover.gotToSettings": "Discover設定を表示", "discover.openOptionsPopover.tryDocumentExplorer": "ドキュメントエクスプローラーを試す", + "discover.partialHits": "≥{formattedHits} {hits, plural, other {一致}}", "discover.reloadSavedSearchButton": "検索をリセット", "discover.removeColumnLabel": "列を削除", "discover.rootBreadcrumb": "Discover", @@ -2963,6 +2989,7 @@ "discover.uninitializedRefreshButtonText": "データを更新", "discover.uninitializedText": "クエリを作成、フィルターを追加、または[更新]をクリックして、現在のクエリの結果を取得します。", "discover.uninitializedTitle": "検索開始", + "discover.utils.formatHit.moreFields": "および{count} more {count, plural, other {個のフィールド}}", "discover.valueIsNotConfiguredDataViewIDWarningTitle": "{stateVal}は設定されたデータビューIDではありません", "discover.viewModes.document.label": "ドキュメント", "discover.viewModes.fieldStatistics.betaTitle": "ベータ", @@ -3067,8 +3094,10 @@ "esUi.cronEditor.month.september": "9 月", "esUi.cronEditor.textEveryLabel": "毎", "esUi.forms.comboBoxField.placeHolderText": "入力してエンターキーを押してください", + "esUi.forms.fieldValidation.indexNameInvalidCharactersError": "インデックス名には無効な{characterListLength, plural, other {文字}} { characterList }が含まれています。", "esUi.forms.fieldValidation.indexNameSpacesError": "インデックス名にはスペースを使用できません。", "esUi.forms.fieldValidation.indexNameStartsWithDotError": "インデックス名の始めにピリオド(.)は使用できません。", + "esUi.forms.fieldValidation.indexPatternInvalidCharactersError": "インデックスパターンには無効な{characterListLength, plural, other {文字}} { characterList }が含まれています。", "esUi.forms.fieldValidation.indexPatternSpacesError": "インデックスパターンにはスペースを使用できません。", "esUi.formWizard.backButtonLabel": "戻る", "esUi.formWizard.nextButtonLabel": "次へ", @@ -4249,10 +4278,15 @@ "indexPatternEditor.rollupIndexPattern.warning.title": "ベータ機能", "indexPatternEditor.rollupLabel": "ロールアップ", "indexPatternEditor.saved": "Saved '{indexPatternTitle}'", + "indexPatternEditor.status.matchAnyLabel.matchAnyDetail": "インデックスパターンは、{sourceCount, plural, other {# 個のソース} }と一致します。", "indexPatternEditor.status.noSystemIndicesLabel": "データストリーム、インデックス、またはインデックスエイリアスがインデックスパターンと一致しません。", "indexPatternEditor.status.noSystemIndicesWithPromptLabel": "データストリーム、インデックス、またはインデックスエイリアスがインデックスパターンと一致しません。", + "indexPatternEditor.status.notMatchLabel.allIndicesLabel": "{indicesLength, plural, other {# ソース} }", "indexPatternEditor.status.notMatchLabel.notMatchDetail": "入力したインデックスパターンはデータストリーム、インデックス、またはインデックスエイリアスと一致しません。{strongIndices}と一致できます。", "indexPatternEditor.status.notMatchLabel.notMatchNoIndicesDetail": "入力したインデックスパターンはデータストリーム、インデックス、またはインデックスエイリアスと一致しません。", + "indexPatternEditor.status.partialMatchLabel.partialMatchDetail": "インデックスパターンはどのデータストリーム、インデックス、インデックスエイリアスとも一致しませんが、{strongIndices} {matchedIndicesLength, plural, other {が} }類似しています。", + "indexPatternEditor.status.partialMatchLabel.strongIndicesLabel": "{matchedIndicesLength, plural, other {# ソース} }", + "indexPatternEditor.status.successLabel.successDetail": "インデックスパターンは、{sourceCount} {sourceCount, plural, other {ソース} }と一致します。", "indexPatternEditor.title": "データビューを作成", "indexPatternEditor.typeSelect.betaLabel": "ベータ", "indexPatternEditor.typeSelect.rollup": "ロールアップ", @@ -4480,6 +4514,7 @@ "indexPatternManagement.editIndexPattern.list.histogramSummary": "{aggName} (間隔:{interval})", "indexPatternManagement.editIndexPattern.list.rollupIndexPatternListName": "ロールアップ", "indexPatternManagement.editIndexPattern.mappingConflictHeader": "マッピングの矛盾", + "indexPatternManagement.editIndexPattern.mappingConflictLabel": "{conflictFieldsLength, plural, other {# 個のフィールド}}が、このパターンと一致するインデックスの間で異なるタイプ(文字列、整数など)に定義されています。これらの矛盾したフィールドはKibanaの一部で使用できますが、Kibanaがタイプを把握しなければならない機能には使用できません。この問題を修正するにはデータのレンダリングが必要です。", "indexPatternManagement.editIndexPattern.scripted.addFieldButton": "スクリプトフィールドを追加", "indexPatternManagement.editIndexPattern.scripted.deleteField.cancelButton": "キャンセル", "indexPatternManagement.editIndexPattern.scripted.deleteField.deleteButton": "削除", @@ -4650,6 +4685,7 @@ "inspector.requests.requestTabLabel": "リクエスト", "inspector.requests.requestTimeLabel": "{requestTime}ms", "inspector.requests.requestTooltipDescription": "リクエストの合計所要時間です。", + "inspector.requests.requestWasMadeDescription": "{requestsCount, plural, other {# 件のリクエスト} } が行われました{failedRequests}", "inspector.requests.requestWasMadeDescription.requestHadFailureText": "、{failedCount} 件に失敗がありました", "inspector.requests.responseTabLabel": "応答", "inspector.requests.searchSessionId": "セッション ID を検索:{searchSessionId}", @@ -4938,15 +4974,19 @@ "savedObjectsManagement.importSummary.createdOutcomeLabel": "作成済み", "savedObjectsManagement.importSummary.errorCountHeader": "{errorCount}件のエラー", "savedObjectsManagement.importSummary.errorOutcomeLabel": "{errorMessage}", + "savedObjectsManagement.importSummary.headerLabel": "{importCount, plural, other {# 個のオブジェクト}}がインポートされました", "savedObjectsManagement.importSummary.overwrittenCountHeader": "{overwrittenCount} overwritten", "savedObjectsManagement.importSummary.overwrittenOutcomeLabel": "上書き", "savedObjectsManagement.importSummary.warnings.defaultButtonLabel": "Go", "savedObjectsManagement.managementSectionLabel": "保存されたオブジェクト", "savedObjectsManagement.objects.savedObjectsDescription": "保存されたオブジェクトをインポート、エクスポート、管理します。", "savedObjectsManagement.objects.savedObjectsTitle": "保存されたオブジェクト", + "savedObjectsManagement.objectsTable.deleteConfirmModal.cannotDeleteCallout.content": "{objectCount, plural, other {# 個のオブジェクト}}が非表示であるため削除できません。{objectCount, plural, other {オブジェクト}}が表の要約から除外されました。", "savedObjectsManagement.objectsTable.deleteConfirmModal.cannotDeleteCallout.title": "一部のオブジェクトを削除できません", "savedObjectsManagement.objectsTable.deleteConfirmModal.sharedObjectsCallout.content": "共有オブジェクトは属しているすべてのスペースから削除されます。", + "savedObjectsManagement.objectsTable.deleteConfirmModal.sharedObjectsCallout.title": "{sharedObjectsCount, plural, other {# 個の保存されたオブジェクトが共有されます}}", "savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.cancelButtonLabel": "キャンセル", + "savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.deleteButtonLabel": "{objectsCount, plural, other {# 個のオブジェクト}}を削除", "savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.idColumnName": "Id", "savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.titleColumnName": "タイトル", "savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.typeColumnName": "型", @@ -4960,6 +5000,7 @@ "savedObjectsManagement.objectsTable.exportObjectsConfirmModal.exportOptionsLabel": "オプション", "savedObjectsManagement.objectsTable.exportObjectsConfirmModal.includeReferencesDeepLabel": "関連オブジェクトを含める", "savedObjectsManagement.objectsTable.exportObjectsConfirmModalDescription": "エクスポートするタイプを選択してください", + "savedObjectsManagement.objectsTable.exportObjectsConfirmModalTitle": "{filteredItemCount, plural, other {# 個のオブジェクト}}をエクスポート", "savedObjectsManagement.objectsTable.flyout.errorCalloutTitle": "申し訳ございません、エラーが発生しました", "savedObjectsManagement.objectsTable.flyout.import.cancelButtonLabel": "キャンセル", "savedObjectsManagement.objectsTable.flyout.import.confirmButtonLabel": "インポート", @@ -4979,6 +5020,7 @@ "savedObjectsManagement.objectsTable.flyout.renderConflicts.columnSampleOfAffectedObjectsDescription": "影響されるオブジェクトのサンプル", "savedObjectsManagement.objectsTable.flyout.renderConflicts.columnSampleOfAffectedObjectsName": "影響されるオブジェクトのサンプル", "savedObjectsManagement.objectsTable.flyout.selectFileToImportFormRowLabel": "インポートするファイルを選択してください", + "savedObjectsManagement.objectsTable.header.exportButtonLabel": "{filteredCount, plural, other {# 個のオブジェクト}}をエクスポート", "savedObjectsManagement.objectsTable.header.importButtonLabel": "インポート", "savedObjectsManagement.objectsTable.header.refreshButtonLabel": "更新", "savedObjectsManagement.objectsTable.header.savedObjectsTitle": "保存されたオブジェクト", @@ -5032,6 +5074,7 @@ "savedObjectsManagement.objectsTable.table.deleteButtonTitle": "保存されたオブジェクトを削除できません", "savedObjectsManagement.objectsTable.table.exportButtonLabel": "エクスポート", "savedObjectsManagement.objectsTable.table.exportPopoverButtonLabel": "エクスポート", + "savedObjectsManagement.objectsTable.table.tooManyResultsLabel": "表示中:{totalItemCount, plural, other {# オブジェクト}}の{limit}", "savedObjectsManagement.objectsTable.table.typeFilterName": "型", "savedObjectsManagement.objectsTable.unableFindSavedObjectNotificationMessage": "保存されたオブジェクトが見つかりません", "savedObjectsManagement.objectsTable.unableFindSavedObjectsNotificationMessage": "保存されたオブジェクトが見つかりません", @@ -6565,6 +6608,7 @@ "visualizations.newVisWizard.learnMoreText": "詳細について", "visualizations.newVisWizard.newVisTypeTitle": "新規 {visTypeName}", "visualizations.newVisWizard.readDocumentationLink": "ドキュメンテーションを表示", + "visualizations.newVisWizard.resultsFound": "{resultCount, plural, other {個のタイプ}} が見つかりました", "visualizations.newVisWizard.searchSelection.notFoundLabel": "一致インデックスまたは保存した検索が見つかりません。", "visualizations.newVisWizard.searchSelection.savedObjectType.dataView": "データビュー", "visualizations.newVisWizard.searchSelection.savedObjectType.search": "保存検索", @@ -6665,6 +6709,7 @@ "xpack.actions.disabledActionTypeError": "アクションタイプ \"{actionType}\" は、Kibana 構成 xpack.actions.enabledActionTypes では有効化されません", "xpack.actions.featureRegistry.actionsFeatureName": "アクションとコネクター", "xpack.actions.savedObjects.goToConnectorsButtonText": "コネクターに移動", + "xpack.actions.savedObjects.onImportText": "{connectorsWithSecretsLength} {connectorsWithSecretsLength, plural, other {コネクターには}}更新が必要な機密情報があります。", "xpack.actions.serverSideErrors.expirerdLicenseErrorMessage": "{licenseType} ライセンスの期限が切れたのでアクションタイプ {actionTypeId} は無効です。", "xpack.actions.serverSideErrors.invalidLicenseErrorMessage": "{licenseType} ライセンスでサポートされないのでアクションタイプ {actionTypeId} は無効です。ライセンスをアップグレードしてください。", "xpack.actions.serverSideErrors.predefinedActionDeleteDisabled": "あらかじめ構成されたアクション{id}は削除できません。", @@ -6689,6 +6734,7 @@ "xpack.alerting.ruleTypeRegistry.register.invalidMinimumTimeoutRuleTypeError": "ルールタイプ\"{id}\"の最低間隔が無効です:{errorMessage}。", "xpack.alerting.ruleTypeRegistry.register.invalidTimeoutRuleTypeError": "ルールタイプ\"{id}\"のタイムアウトが無効です:{errorMessage}。", "xpack.alerting.savedObjects.goToRulesButtonText": "ルールに移動", + "xpack.alerting.savedObjects.onImportText": "インポート後に、{rulesSavedObjectsLength} {rulesSavedObjectsLength, plural, other {ルール}}を有効にする必要があります。", "xpack.alerting.serverSideErrors.expirerdLicenseErrorMessage": "{licenseType}ライセンスの期限が切れたため、ルールタイプ{ruleTypeId}は無効です。", "xpack.alerting.serverSideErrors.invalidLicenseErrorMessage": "{licenseType}ライセンスが必要であるため、ルール{ruleTypeId}は無効です。アップグレードオプションを表示するには、[ライセンス管理]に移動してください。", "xpack.alerting.serverSideErrors.unavailableLicenseErrorMessage": "現時点でライセンス情報を入手できないため、ルール{ruleTypeId}は無効です。", @@ -6984,6 +7030,7 @@ "xpack.apm.errorGroupDetails.occurrencesChartLabel": "オカレンス", "xpack.apm.errorGroupDetails.relatedTransactionSample": "関連トランザクションサンプル", "xpack.apm.errorGroupDetails.unhandledLabel": "未対応", + "xpack.apm.errorGroupDetails.viewOccurrencesInDiscoverButtonLabel": "Discover で {occurrencesCount} {occurrencesCount, plural, other {件の発生}} を表示", "xpack.apm.errorRate": "失敗したトランザクション率", "xpack.apm.errorRate.chart.errorRate": "失敗したトランザクション率(平均)", "xpack.apm.errorRate.chart.errorRate.previousPeriodLabel": "前の期間", @@ -7165,6 +7212,7 @@ "xpack.apm.instancesLatencyDistributionChartLegend.previousPeriod": "前の期間", "xpack.apm.instancesLatencyDistributionChartTitle": "インスタンスのレイテンシ分布", "xpack.apm.instancesLatencyDistributionChartTooltipClickToFilterDescription": "クリックすると、インスタンスでフィルタリングします", + "xpack.apm.instancesLatencyDistributionChartTooltipInstancesTitle": "{instancesCount} {instancesCount, plural, other {個のインスタンス}}", "xpack.apm.instancesLatencyDistributionChartTooltipLatencyLabel": "レイテンシ", "xpack.apm.instancesLatencyDistributionChartTooltipThroughputLabel": "スループット", "xpack.apm.invalidLicense.licenseManagementLink": "ライセンスを更新", @@ -7251,12 +7299,15 @@ "xpack.apm.serviceIcons.container": "コンテナー", "xpack.apm.serviceIcons.serverless": "サーバーレス", "xpack.apm.serviceIcons.service": "サービス", - "xpack.apm.serviceIcons.serviceDetails.cloud.availabilityZoneLabel": "{zones, plural, =other {可用性ゾーン}} ", + "xpack.apm.serviceIcons.serviceDetails.cloud.availabilityZoneLabel": "{zones, plural, other {可用性ゾーン}} ", "xpack.apm.serviceIcons.serviceDetails.cloud.betaLabel": "ベータ", "xpack.apm.serviceIcons.serviceDetails.cloud.betaTooltip": "AWS Lambdaサポートは一般公開されていません。不具合を報告して支援してください。", - "xpack.apm.serviceIcons.serviceDetails.cloud.machineTypesLabel": "{machineTypes, plural, =other {コンピュータータイプ}} ", + "xpack.apm.serviceIcons.serviceDetails.cloud.faasTriggerTypeLabel": "{triggerTypes, plural, other {トリガータイプ}} ", + "xpack.apm.serviceIcons.serviceDetails.cloud.functionNameLabel": "{functionNames, plural, other {関数名}} ", + "xpack.apm.serviceIcons.serviceDetails.cloud.machineTypesLabel": "{machineTypes, plural, other {コンピュータータイプ} }\n ", "xpack.apm.serviceIcons.serviceDetails.cloud.projectIdLabel": "プロジェクト ID", "xpack.apm.serviceIcons.serviceDetails.cloud.providerLabel": "クラウドプロバイダー", + "xpack.apm.serviceIcons.serviceDetails.cloud.regionLabel": "{regions, plural, other {リージョン}} ", "xpack.apm.serviceIcons.serviceDetails.cloud.serviceNameLabel": "クラウドサービス", "xpack.apm.serviceIcons.serviceDetails.container.containerizedLabel": "コンテナー化", "xpack.apm.serviceIcons.serviceDetails.container.noLabel": "いいえ", @@ -7371,6 +7422,7 @@ "xpack.apm.serviceProfiling.valueTypeLabel.unknown": "その他", "xpack.apm.serviceProfiling.valueTypeLabel.wallTime": "Wall", "xpack.apm.servicesTable.environmentColumnLabel": "環境", + "xpack.apm.servicesTable.environmentCount": "{environmentCount, plural, other {# 個の環境}}", "xpack.apm.servicesTable.healthColumnLabel": "ヘルス", "xpack.apm.servicesTable.latencyAvgColumnLabel": "レイテンシ(平均)", "xpack.apm.servicesTable.metricsExplanationLabel": "これらのメトリックは何か。", @@ -7556,6 +7608,7 @@ "xpack.apm.settingsLinkLabel": "設定", "xpack.apm.setupInstructionsButtonLabel": "セットアップの手順", "xpack.apm.stacktraceTab.causedByFramesToogleButtonLabel": "作成元", + "xpack.apm.stacktraceTab.libraryFramesToogleButtonLabel": "{count, plural, other {# ライブラリフレーム}}", "xpack.apm.stacktraceTab.localVariablesToogleButtonLabel": "ローカル変数", "xpack.apm.stacktraceTab.noStacktraceAvailableLabel": "利用可能なスタックトレースがありません。", "xpack.apm.timeComparison.label": "比較", @@ -7602,6 +7655,7 @@ "xpack.apm.transactionDetails.distribution.panelTitle": "レイテンシ分布", "xpack.apm.transactionDetails.distribution.selectionText": "選択:{formattedSelection}", "xpack.apm.transactionDetails.emptySelectionText": "クリックおよびドラッグして範囲を選択", + "xpack.apm.transactionDetails.errorCount": "{errorCount, number} {errorCount, plural, other {エラー}}", "xpack.apm.transactionDetails.noTraceParentButtonTooltip": "トレースの親が見つかりませんでした", "xpack.apm.transactionDetails.percentOfTraceLabelExplanation": "{parentType, select, transaction {トランザクション} trace {トレース} }の割合が100%を超えています。これは、この{childType, select, span {スパン} transaction {トランザクション} }がルートトランザクションよりも時間がかかるためです。", "xpack.apm.transactionDetails.requestMethodLabel": "リクエストメソッド", @@ -7807,6 +7861,7 @@ "xpack.apm.tutorial.windowsServerInstructions.textPost": "注:システムでスクリプトの実行が無効な場合、スクリプトを実行するために現在のセッションの実行ポリシーの設定が必要となります。例:{command}。", "xpack.apm.tutorial.windowsServerInstructions.textPre": "1.[ダウンロードページ]({downloadPageLink})から APM Server Windows zip ファイルをダウンロードします。\n2.zip ファイルの内容を {zipFileExtractFolder} に抽出します。\n3.「{apmServerDirectory} ディレクトリの名前を「APM-Server」に変更します。\n4.管理者としてPowerShellプロンプトを開きます(PowerShellアイコンを右クリックして「管理者として実行」を選択します)。Windows XPをご使用の場合、PowerShellのダウンロードとインストールが必要な場合があります。\n5.PowerShell プロンプトで次のコマンドを実行し、APM Server を Windows サービスとしてインストールします。", "xpack.apm.unitLabel": "単位を選択", + "xpack.apm.unsavedChanges": "{unsavedChangesCount, plural, other {# 未保存変更}} ", "xpack.apm.ux.overview.agent.description": "APMエージェントを使用して、APMデータを収集します。多数の一般的な言語では、エージェントを使用することで処理が簡単になっています。", "xpack.apm.views.dependencies.title": "依存関係", "xpack.apm.views.dependenciesInventory.title": "依存関係", @@ -8819,6 +8874,10 @@ "xpack.canvas.units.quickRange.last90Days": "過去90日間", "xpack.canvas.units.quickRange.today": "今日", "xpack.canvas.units.quickRange.yesterday": "昨日", + "xpack.canvas.units.time.days": "{days, plural, other {# 日}}", + "xpack.canvas.units.time.hours": "{hours, plural, other {# 時間}}", + "xpack.canvas.units.time.minutes": "{minutes, plural, other {# 分}}", + "xpack.canvas.units.time.seconds": "{seconds, plural, other {# 秒}}", "xpack.canvas.useCloneWorkpad.clonedWorkpadName": "{workpadName} のコピー", "xpack.canvas.varConfig.addButtonLabel": "変数の追加", "xpack.canvas.varConfig.addTooltipLabel": "変数の追加", @@ -8884,6 +8943,10 @@ "xpack.canvas.workpadFilters.timeFilter.to": "終了:", "xpack.canvas.workpadFilters.timeFilter.typeLabel": "時間", "xpack.canvas.workpadHeader.addElementModalCloseButtonLabel": "閉じる", + "xpack.canvas.workpadHeader.cycleIntervalDaysText": "{days} {days, plural, other {# 日}}ごと", + "xpack.canvas.workpadHeader.cycleIntervalHoursText": "{hours} {hours, plural, other {時間}}ごと", + "xpack.canvas.workpadHeader.cycleIntervalMinutesText": "{minutes} {minutes, plural, other {分}}ごと", + "xpack.canvas.workpadHeader.cycleIntervalSecondsText": "{seconds} {seconds, plural, other {秒}}ごと", "xpack.canvas.workpadHeader.fullscreenButtonAriaLabel": "全画面表示", "xpack.canvas.workpadHeader.fullscreenTooltip": "全画面モードを開始します", "xpack.canvas.workpadHeader.hideEditControlTooltip": "編集コントロールを非表示にします", @@ -9017,8 +9080,11 @@ "xpack.cases.caseTable.searchAriaLabel": "ケースの検索", "xpack.cases.caseTable.searchPlaceholder": "例:ケース名", "xpack.cases.caseTable.select": "選択してください", + "xpack.cases.caseTable.selectedCasesTitle": "{totalRules} {totalRules, plural, other {ケース}} を選択しました", + "xpack.cases.caseTable.showingCasesTitle": "{totalRules} {totalRules, plural, other {ケース}} を表示中", "xpack.cases.caseTable.snIncident": "外部インシデント", "xpack.cases.caseTable.status": "ステータス", + "xpack.cases.caseTable.unit": "{totalCount, plural, other {ケース}}", "xpack.cases.caseTable.upToDate": " は最新です", "xpack.cases.caseView.actionLabel.addDescription": "説明を追加しました", "xpack.cases.caseView.actionLabel.addedField": "追加しました", @@ -9073,6 +9139,7 @@ "xpack.cases.caseView.fieldChanged": "変更されたコネクターフィールド", "xpack.cases.caseView.fieldRequiredError": "必須フィールド", "xpack.cases.caseView.generatedAlertCommentLabelTitle": "から追加されました", + "xpack.cases.caseView.generatedAlertCountCommentLabelTitle": "{totalCount} {totalCount, plural, other {アラート}}", "xpack.cases.caseView.isolatedHost": "ホストで分離リクエストが送信されました", "xpack.cases.caseView.lockedIncidentDesc": "更新は必要ありません", "xpack.cases.caseView.lockedIncidentTitle": "{ thirdParty }インシデントは最新です", @@ -9096,6 +9163,7 @@ "xpack.cases.caseView.noTags": "現在、このケースにタグは割り当てられていません。", "xpack.cases.caseView.openCase": "ケースを開く", "xpack.cases.caseView.optional": "オプション", + "xpack.cases.caseView.otherEndpoints": " および{endpoints} {endpoints, plural, other {その他}}", "xpack.cases.caseView.particpantsLabel": "参加者", "xpack.cases.caseView.pushNamedIncident": "{ thirdParty }インシデントとしてプッシュ", "xpack.cases.caseView.pushThirdPartyIncident": "外部インシデントとしてプッシュ", @@ -9163,6 +9231,7 @@ "xpack.cases.configureCases.warningTitle": "警告", "xpack.cases.configureCasesButton": "外部接続を編集", "xpack.cases.confirmDeleteCase.confirmQuestion": "{quantity, plural, =1 {このケース} other {これらのケース}}を削除すると、関連するすべてのケースデータが完全に削除され、外部インシデント管理システムにデータをプッシュできなくなります。続行していいですか?", + "xpack.cases.confirmDeleteCase.deleteCase": "{quantity, plural, other {ケース}}を削除", "xpack.cases.confirmDeleteCase.deleteTitle": "「{caseTitle}」を削除", "xpack.cases.confirmDeleteCase.selectedCases": "\"{quantity, plural, =1 {{title}} other {選択した{quantity}個のケース}}\"を削除", "xpack.cases.connecors.get.missingCaseConnectorErrorMessage": "オブジェクトタイプ「{id}」は登録されていません。", @@ -9286,6 +9355,7 @@ "xpack.crossClusterReplication.addBreadcrumbTitle": "追加", "xpack.crossClusterReplication.addFollowerButtonLabel": "フォロワーインデックスを作成", "xpack.crossClusterReplication.app.checkPermissionsFatalErrorTitle": "クラスター横断レプリケーションアプリ", + "xpack.crossClusterReplication.app.deniedPermissionDescription": "クラスター横断レプリケーションを使用するには、{clusterPrivilegesCount, plural, other {次のクラスター特権}}が必要です:{clusterPrivileges}。", "xpack.crossClusterReplication.app.deniedPermissionTitle": "クラスター特権が足りません", "xpack.crossClusterReplication.app.permissionCheckErrorTitle": "パーミッションの確認中にエラーが発生", "xpack.crossClusterReplication.app.permissionCheckTitle": "パーミッションを確認中…", @@ -9294,6 +9364,7 @@ "xpack.crossClusterReplication.autoFollowPattern.addAction.successNotificationTitle": "自動フォローパターン「{name}」が追加されました", "xpack.crossClusterReplication.autoFollowPattern.addTitle": "自動フォローパターンの追加", "xpack.crossClusterReplication.autoFollowPattern.editTitle": "自動フォローパターンの編集", + "xpack.crossClusterReplication.autoFollowPattern.leaderIndexPatternValidation.illegalCharacters": "インデックスパターンから{characterListLength, plural, other {文字}} {characterList} を削除してください。", "xpack.crossClusterReplication.autoFollowPattern.leaderIndexPatternValidation.isEmpty": "リーダーインデックスパターンが最低 1 つ必要です。", "xpack.crossClusterReplication.autoFollowPattern.leaderIndexPatternValidation.noEmptySpace": "インデックスパターンにスペースは使用できません。", "xpack.crossClusterReplication.autoFollowPattern.nameValidation.errorComma": "名前にコンマは使用できません。", @@ -9305,6 +9376,7 @@ "xpack.crossClusterReplication.autoFollowPattern.pauseAction.successMultipleNotificationTitle": "{count} 自動フォローパターンが一時停止しました", "xpack.crossClusterReplication.autoFollowPattern.pauseAction.successSingleNotificationTitle": "「{name}」 自動フォローパターンが一時停止しました", "xpack.crossClusterReplication.autoFollowPattern.prefixValidation.beginsWithPeriod": "接頭辞はピリオドで始めることはできません。", + "xpack.crossClusterReplication.autoFollowPattern.prefixValidation.illegalCharacters": "接頭辞から{characterListLength, plural, other {文字}}の {characterList} を削除してください。", "xpack.crossClusterReplication.autoFollowPattern.prefixValidation.noEmptySpace": "接頭辞にスペースは使用できません。", "xpack.crossClusterReplication.autoFollowPattern.removeAction.errorMultipleNotificationTitle": "{count} 自動フォローパターンの削除中にエラーが発生", "xpack.crossClusterReplication.autoFollowPattern.removeAction.errorSingleNotificationTitle": "「「{name}」自動フォローパターンの削除中にエラーが発生", @@ -9314,8 +9386,10 @@ "xpack.crossClusterReplication.autoFollowPattern.resumeAction.errorSingleNotificationTitle": "「「{name}」自動フォローパターンの再開エラー", "xpack.crossClusterReplication.autoFollowPattern.resumeAction.successMultipleNotificationTitle": "{count} 自動フォローパターンが再開しました", "xpack.crossClusterReplication.autoFollowPattern.resumeAction.successSingleNotificationTitle": "「{name}」 自動フォローパターンが再開しました", + "xpack.crossClusterReplication.autoFollowPattern.suffixValidation.illegalCharacters": "接尾辞から{characterListLength, plural, other {文字}} の {characterList} 削除してください。", "xpack.crossClusterReplication.autoFollowPattern.suffixValidation.noEmptySpace": "接尾辞にスペースは使用できません。", "xpack.crossClusterReplication.autoFollowPattern.updateAction.successNotificationTitle": "自動フォローパターン「{name}」が更新されました", + "xpack.crossClusterReplication.autoFollowPatternActionMenu.buttonLabel": "{patterns, plural, other {パターン}}を管理します", "xpack.crossClusterReplication.autoFollowPatternActionMenu.panelTitle": "パターンオプション", "xpack.crossClusterReplication.autoFollowPatternCreateForm.loadingRemoteClustersMessage": "リモートクラスターを読み込み中…", "xpack.crossClusterReplication.autoFollowPatternCreateForm.saveButtonLabel": "作成", @@ -9403,6 +9477,7 @@ "xpack.crossClusterReplication.deleteAutoFollowPattern.confirmModal.deleteMultipleTitle": "{count} 個の自動フォローパターンを削除しますか?", "xpack.crossClusterReplication.deleteAutoFollowPattern.confirmModal.deleteSingleTitle": "自動フォローパターン「{name}」を削除しますか?", "xpack.crossClusterReplication.deleteAutoFollowPattern.confirmModal.multipleDeletionDescription": "これらの自動フォローパターンを削除しようとしています:", + "xpack.crossClusterReplication.deleteAutoFollowPatternButtonLabel": "{total, plural, other {パターン}}を削除", "xpack.crossClusterReplication.deprecations.enabled.manualStepOneMessage": "kibana.yml構成ファイルを開きます。", "xpack.crossClusterReplication.deprecations.enabled.manualStepTwoMessage": "「xpack.ccr.enabled」設定を「xpack.ccr.ui.enabled」に変更します。", "xpack.crossClusterReplication.deprecations.enabledMessage": "ユーザーがクラスター横断レプリケーションUIにアクセスすることを禁止するには、「xpack.ccr.enabled」ではなく、「xpack.ccr.ui.enabled」設定を使用します。", @@ -9412,9 +9487,12 @@ "xpack.crossClusterReplication.followerIndex.addAction.successNotificationTitle": "フォロワーインデックス「{name}」が追加されました", "xpack.crossClusterReplication.followerIndex.addTitle": "フォロワーインデックスの追加", "xpack.crossClusterReplication.followerIndex.advancedSettingsForm.showSwitchLabel": "高度な設定をカスタマイズ", + "xpack.crossClusterReplication.followerIndex.contextMenu.buttonLabel": "フォロワー{followerIndicesLength, plural, other {インデックス}}の管理", "xpack.crossClusterReplication.followerIndex.contextMenu.editLabel": "フォロワーインデックスを編集", "xpack.crossClusterReplication.followerIndex.contextMenu.pauseLabel": "複製を中止", "xpack.crossClusterReplication.followerIndex.contextMenu.resumeLabel": "複製を再開", + "xpack.crossClusterReplication.followerIndex.contextMenu.title": "フォロワー{followerIndicesLength, plural, other {インデックス}}オプション", + "xpack.crossClusterReplication.followerIndex.contextMenu.unfollowLabel": "不明なリーダー{followerIndicesLength, plural, other {インデックス}}", "xpack.crossClusterReplication.followerIndex.editTitle": "フォロワーインデックスを編集", "xpack.crossClusterReplication.followerIndex.indexNameValidation.noEmptySpace": "名前にスペースは使用できません。", "xpack.crossClusterReplication.followerIndex.leaderIndexValidation.noEmptySpace": "リーダーインデックスではスペースを使用できません。", @@ -9549,6 +9627,7 @@ "xpack.crossClusterReplication.followerIndexList.table.statusColumnTitle": "ステータス", "xpack.crossClusterReplication.homeBreadcrumbTitle": "クラスター横断レプリケーション", "xpack.crossClusterReplication.indexMgmtBadge.followerLabel": "フォロワー", + "xpack.crossClusterReplication.pauseAutoFollowPatternsLabel": "{total, plural, other {複製}}を一時停止", "xpack.crossClusterReplication.pauseFollowerIndex.confirmModal.cancelButtonText": "キャンセル", "xpack.crossClusterReplication.pauseFollowerIndex.confirmModal.confirmButtonText": "複製を中止", "xpack.crossClusterReplication.pauseFollowerIndex.confirmModal.multiplePauseDescription": "これらのフォロワーインデックスの複製が一時停止されます:", @@ -9569,6 +9648,7 @@ "xpack.crossClusterReplication.remoteClustersFormField.remoteClusterNotFoundTitle": "リモートクラスター「{name}」が見つかりませんでした", "xpack.crossClusterReplication.remoteClustersFormField.validRemoteClusterRequired": "接続されたリモートクラスターが必要です。", "xpack.crossClusterReplication.remoteClustersFormField.viewRemoteClusterButtonLabel": "リモートクラスターを編集します", + "xpack.crossClusterReplication.resumeAutoFollowPatternsLabel": "{total, plural, other {複製}}を再開", "xpack.crossClusterReplication.resumeFollowerIndex.confirmModal.cancelButtonText": "キャンセル", "xpack.crossClusterReplication.resumeFollowerIndex.confirmModal.confirmButtonText": "複製を再開", "xpack.crossClusterReplication.resumeFollowerIndex.confirmModal.multipleResumeDescription": "これらのフォロワーインデックスの複製が再開されます:", @@ -9756,6 +9836,7 @@ "xpack.dataVisualizer.dataGrid.showDistributionsTooltip": "分布を表示", "xpack.dataVisualizer.dataGrid.typeColumnName": "型", "xpack.dataVisualizer.dataGridChart.histogramNotAvailable": "グラフはサポートされていません。", + "xpack.dataVisualizer.dataGridChart.singleCategoryLegend": "{cardinality, plural, other {# カテゴリ}}", "xpack.dataVisualizer.dataGridChart.topCategoriesLegend": "上位 {maxChartColumns}/{cardinality} カテゴリ", "xpack.dataVisualizer.description": "CSV、NDJSON、またはログファイルをインポートします。", "xpack.dataVisualizer.fieldNameSelect": "フィールド名", @@ -9791,6 +9872,7 @@ "xpack.dataVisualizer.file.analysisSummary.hasHeaderRowTitle": "ヘッダー行があります", "xpack.dataVisualizer.file.analysisSummary.summaryTitle": "まとめ", "xpack.dataVisualizer.file.analysisSummary.timeFieldTitle": "時間フィールド", + "xpack.dataVisualizer.file.analysisSummary.timeFormatTitle": "時間 {timestampFormats, plural, other {フォーマット}}", "xpack.dataVisualizer.file.bottomBar.backButtonLabel": "戻る", "xpack.dataVisualizer.file.bottomBar.cancelButtonLabel": "キャンセル", "xpack.dataVisualizer.file.bottomBar.missingImportPrivilegesMessage": "データインポートを有効にするには、ingest_adminロールが必要です", @@ -9824,6 +9906,7 @@ "xpack.dataVisualizer.file.explanationFlyout.content": "分析結果を生成した論理ステップ。", "xpack.dataVisualizer.file.explanationFlyout.title": "分析説明", "xpack.dataVisualizer.file.fileContents.fileContentsTitle": "ファイルコンテンツ", + "xpack.dataVisualizer.file.fileContents.firstLinesDescription": "初めの {numberOfLines, plural, other {# 行}}", "xpack.dataVisualizer.file.fileErrorCallouts.applyOverridesDescription": "ファイル形式やタイムスタンプ形式などこのデータに関する何らかの情報がある場合は、初期オーバーライドを追加すると、残りの構造を推論するのに役立つことがあります。", "xpack.dataVisualizer.file.fileErrorCallouts.fileCouldNotBeReadTitle": "ファイル構造を決定できません", "xpack.dataVisualizer.file.fileErrorCallouts.fileSizeExceedsAllowedSizeByDiffFormatErrorMessage": "アップロードするよう選択されたファイルのサイズが {diffFormatted} に許可された最大サイズの {maxFileSizeFormatted} を超えています", @@ -9972,6 +10055,7 @@ "xpack.dataVisualizer.searchPanel.sampleSizeOptionLabel": "サンプルサイズ(シャード単位):{wrappedValue}", "xpack.dataVisualizer.searchPanel.showEmptyFields": "空のフィールドを表示", "xpack.dataVisualizer.searchPanel.totalDocCountLabel": "合計ドキュメント数:{strongTotalCount}", + "xpack.dataVisualizer.searchPanel.totalDocCountNumber": "{totalCount, plural, other {#}}", "xpack.dataVisualizer.title": "ファイルをアップロード", "xpack.discover.FlyoutCreateDrilldownAction.displayName": "基本データを調査", "xpack.embeddableEnhanced.actions.panelNotifications.manyDrilldowns": "パネルには{count}個のドリルダウンがあります", @@ -10025,6 +10109,7 @@ "xpack.enterpriseSearch.appSearch.crawler.automaticCrawlSchedule.scheduleUnitsLabel": "スケジュール時間単位", "xpack.enterpriseSearch.appSearch.crawler.automaticCrawlScheduler.disableCrawlSchedule.successMessage": "自動クローリングが無効にされました。", "xpack.enterpriseSearch.appSearch.crawler.automaticCrawlScheduler.submitCrawlSchedule.successMessage": "自動クローリングスケジュールが更新されました。", + "xpack.enterpriseSearch.appSearch.crawler.components.crawlDetailsSummary.crawlCountOnDomains": "{domainCount, plural, other {# 件のドメイン}}で{crawlType}クロール", "xpack.enterpriseSearch.appSearch.crawler.components.crawlDetailsSummary.crawlDepthLabel": "最大クロール深度", "xpack.enterpriseSearch.appSearch.crawler.components.crawlDetailsSummary.crawlTypeLabel": "クロールタイプ", "xpack.enterpriseSearch.appSearch.crawler.configurationDocumentationLinkDescription": "Kibanaでのクローラーログの構成の詳細をご覧ください", @@ -10218,8 +10303,12 @@ "xpack.enterpriseSearch.appSearch.documentCreation.showCreationModes.title": "新しいドキュメントの追加", "xpack.enterpriseSearch.appSearch.documentCreation.showSummary.documentNotIndexed": "このドキュメントにはインデックスが作成されていません。", "xpack.enterpriseSearch.appSearch.documentCreation.showSummary.fixErrors": "エラーを修正してください", + "xpack.enterpriseSearch.appSearch.documentCreation.showSummary.invalidDocuments": "エラーがある{invalidDocuments, number} {invalidDocuments, plural, other {個のドキュメント}}...", + "xpack.enterpriseSearch.appSearch.documentCreation.showSummary.newDocuments": "{newDocuments, number} {newDocuments, plural, other {個のドキュメント}}を追加しました。", + "xpack.enterpriseSearch.appSearch.documentCreation.showSummary.newSchemaFields": "{newFields, number} {newFields, plural, other {個のフィールド}}をエンジンのスキーマに追加しました。", "xpack.enterpriseSearch.appSearch.documentCreation.showSummary.noNewDocuments": "新しいドキュメントはありません。", "xpack.enterpriseSearch.appSearch.documentCreation.showSummary.noNewSchemaFields": "新しいスキーマフィールドはありません。", + "xpack.enterpriseSearch.appSearch.documentCreation.showSummary.otherDocuments": "と{documents, number} other {documents, plural, other {個のドキュメント}}。", "xpack.enterpriseSearch.appSearch.documentCreation.showSummary.title": "インデックス概要", "xpack.enterpriseSearch.appSearch.documentCreation.uploadJsonFile.label": ".jsonファイルがある場合は、ドラッグアンドドロップするか、アップロードします。JSONが有効であり、各ドキュメントオブジェクトが{maxDocumentByteSize}バイト未満であることを確認してください。", "xpack.enterpriseSearch.appSearch.documentCreation.uploadJsonFile.title": ".jsonをドラッグアンドドロップ", @@ -10293,6 +10382,7 @@ "xpack.enterpriseSearch.appSearch.engine.analytics.table.queriesColumn": "クエリ", "xpack.enterpriseSearch.appSearch.engine.analytics.table.resultsColumn": "結果", "xpack.enterpriseSearch.appSearch.engine.analytics.table.tagsColumn": "分析タグ", + "xpack.enterpriseSearch.appSearch.engine.analytics.table.tagsCountBadge": "{tagsCount, plural, other {#個のタグ}}", "xpack.enterpriseSearch.appSearch.engine.analytics.table.termColumn": "検索語", "xpack.enterpriseSearch.appSearch.engine.analytics.table.timeColumn": "時間", "xpack.enterpriseSearch.appSearch.engine.analytics.table.viewAction": "表示", @@ -10500,6 +10590,7 @@ "xpack.enterpriseSearch.appSearch.engine.relevanceTuning.preview.searchPlaceholder": "{engineName}を検索", "xpack.enterpriseSearch.appSearch.engine.relevanceTuning.preview.title": "プレビュー", "xpack.enterpriseSearch.appSearch.engine.relevanceTuning.schemaConflictsBannerLabel": "無効なフィールド ", + "xpack.enterpriseSearch.appSearch.engine.relevanceTuning.schemaConflictsErrorMessage": "フィールド型の競合が原因で無効な{schemaFieldsWithConflictsCount, number} {schemaFieldsWithConflictsCount, plural, other {個のフィールド}}。{link}", "xpack.enterpriseSearch.appSearch.engine.relevanceTuning.schemaFieldsLinkLabel": "スキーマフィールド", "xpack.enterpriseSearch.appSearch.engine.relevanceTuning.title": "関連性の調整", "xpack.enterpriseSearch.appSearch.engine.relevanceTuning.uncofirmedFieldsBannerLabel": "最近追加されたフィールドはデフォルトで検索されません", @@ -10555,6 +10646,7 @@ "xpack.enterpriseSearch.appSearch.engine.schema.metaEngine.activeFieldsTitle": "アクティブなフィールド", "xpack.enterpriseSearch.appSearch.engine.schema.metaEngine.allEngines": "すべて", "xpack.enterpriseSearch.appSearch.engine.schema.metaEngine.conflictsCalloutDescription": "フィールドのフィールド型が、このメタエンジンを構成するソースエンジン全体で一致していません。このフィールドを検索可能にするには、ソースエンジンから一貫性のあるフィールド型を適用します。", + "xpack.enterpriseSearch.appSearch.engine.schema.metaEngine.conflictsCalloutTitle": "{conflictingFieldsCount, plural, other {# 個のフィールド}}が検索可能ではありません", "xpack.enterpriseSearch.appSearch.engine.schema.metaEngine.description": "エンジン別のアクティブなフィールドと非アクティブなフィールド。", "xpack.enterpriseSearch.appSearch.engine.schema.metaEngine.fieldTypeConflicts": "フィールド型の競合", "xpack.enterpriseSearch.appSearch.engine.schema.metaEngine.inactiveFieldsDescription": "これらのフィールドの型が競合しています。これらのフィールドを有効にするには、一致するソースエンジンで型を変更します。", @@ -10598,6 +10690,7 @@ "xpack.enterpriseSearch.appSearch.engine.souceEngines.addSourceEnginesModal.description": "追加のエンジンをこのメタエンジンに追加します。", "xpack.enterpriseSearch.appSearch.engine.souceEngines.addSourceEnginesModal.title": "エンジンの追加", "xpack.enterpriseSearch.appSearch.engine.souceEngines.addSourceEnginesPlaceholder": "エンジンを選択", + "xpack.enterpriseSearch.appSearch.engine.souceEngines.addSourceEnginesSuccessMessage": "{sourceEnginesCount, plural, other {# 個のエンジン}}がこのメタエンジンに追加されました", "xpack.enterpriseSearch.appSearch.engine.souceEngines.removeSourceEngineSuccessMessage": "エンジン'{engineName}'はこのメタエンジンから削除されました", "xpack.enterpriseSearch.appSearch.engine.souceEngines.title": "エンジンの管理", "xpack.enterpriseSearch.appSearch.engine.suggestedCurationsCallout.description": "エンジンの分析に基づいて、レビュー可能な新しい提案されたキュレーションがあります。", @@ -10652,6 +10745,7 @@ "xpack.enterpriseSearch.appSearch.engines.metaEngines.emptyPromptDescription": "メタエンジンでは、複数のエンジンを1つの検索可能なエンジンに統合できます。", "xpack.enterpriseSearch.appSearch.engines.metaEngines.emptyPromptTitle": "最初のメタエンジンを作成", "xpack.enterpriseSearch.appSearch.engines.metaEnginesTable.fieldTypeConflictWarning": "フィールドタイプの矛盾", + "xpack.enterpriseSearch.appSearch.engines.metaEnginesTable.sourceEnginesCount": "{sourceEnginesCount, plural, other {# エンジン}}", "xpack.enterpriseSearch.appSearch.engines.title": "エンジン", "xpack.enterpriseSearch.appSearch.enginesOverview.metaEnginesTable.sourceEngines.title": "ソースエンジン", "xpack.enterpriseSearch.appSearch.enginesOverview.table.action.delete.buttonDescription": "このエンジンを削除", @@ -10676,6 +10770,7 @@ "xpack.enterpriseSearch.appSearch.logRetention.callout.disabledSinceTitle": "{logsTitle}は、{disabledDate}以降に無効にされました。", "xpack.enterpriseSearch.appSearch.logRetention.callout.disabledTitle": "{logsTitle}は無効です。", "xpack.enterpriseSearch.appSearch.logRetention.customPolicy": "カスタム{logsType}ログ保持ポリシーがあります。", + "xpack.enterpriseSearch.appSearch.logRetention.defaultPolicy": "{logsType}ログは{minAgeDays, plural, other {# 日}}以上保存されています。", "xpack.enterpriseSearch.appSearch.logRetention.noLogging": "すべてのエンジンの{logsType}ログが無効です。", "xpack.enterpriseSearch.appSearch.logRetention.noLogging.collected": "前回の{logsType}ログは{disabledAtDate}に収集されました。", "xpack.enterpriseSearch.appSearch.logRetention.noLogging.notCollected": "収集された{logsType}ログはありません。", @@ -10712,6 +10807,7 @@ "xpack.enterpriseSearch.appSearch.result.documentDetailLink": "ドキュメントの詳細を表示", "xpack.enterpriseSearch.appSearch.result.hideAdditionalFields": "追加フィールドを非表示", "xpack.enterpriseSearch.appSearch.result.resultPositionLabel": "#{resultPosition}", + "xpack.enterpriseSearch.appSearch.result.showAdditionalFields": "{numberOfAdditionalFields, number}個の追加の {numberOfAdditionalFields, plural, other {フィールド}}を表示。", "xpack.enterpriseSearch.appSearch.result.title": "ドキュメント{id}", "xpack.enterpriseSearch.appSearch.roleMappingCreatedMessage": "ロールマッピングが作成されました", "xpack.enterpriseSearch.appSearch.roleMappingDeletedMessage": "ロールマッピングが削除されました", @@ -10914,6 +11010,7 @@ "xpack.enterpriseSearch.shared.flashMessages.defaultErrorMessage": "予期しないエラーが発生しました", "xpack.enterpriseSearch.shared.unsavedChangesMessage": "変更は保存されていません。終了してよろしいですか?", "xpack.enterpriseSearch.trialCalloutLink": "Elastic Stackライセンスの詳細を参照してください。", + "xpack.enterpriseSearch.trialCalloutTitle": "Platinum機能を有効にするElastic Stack試用版ライセンスは{days, plural, other {# 日}}後に期限切れになります。", "xpack.enterpriseSearch.troubleshooting.differentEsClusters.description": "このプラグインは現在、異なるクラスターで実行されている{productName}とKibanaをサポートしていません。", "xpack.enterpriseSearch.troubleshooting.differentEsClusters.title": "{productName}とKibanaは別のElasticsearchクラスターにあります", "xpack.enterpriseSearch.units.allDaysLabel": "すべての日", @@ -11472,6 +11569,7 @@ "xpack.enterpriseSearch.workplaceSearch.sources.private.header.description": "非公開コンテンツソースは自分のみが使用できます。", "xpack.enterpriseSearch.workplaceSearch.sources.private.header.title": "自分の非公開コンテンツソース", "xpack.enterpriseSearch.workplaceSearch.sources.private.link": "非公開コンテンツソースを追加", + "xpack.enterpriseSearch.workplaceSearch.sources.private.privateOrg.header.description": "{groups, plural, other {グループ}} {groupsSentence}経由で{newline}次のソースにアクセスできます。", "xpack.enterpriseSearch.workplaceSearch.sources.private.privateOrg.header.title": "組織コンテンツソース", "xpack.enterpriseSearch.workplaceSearch.sources.private.vewOnly.description": "グループと共有するすべてのソースのステータスを確認します。", "xpack.enterpriseSearch.workplaceSearch.sources.private.vewOnly.title": "グループソースの確認", @@ -11540,6 +11638,7 @@ "xpack.enterpriseSearch.workplaceSearch.sources.utcTitle": "すべての時刻はUTC表記です", "xpack.enterpriseSearch.workplaceSearch.sourcesOnboardingCard.addFirstSources.button": "ソースの追加", "xpack.enterpriseSearch.workplaceSearch.sourcesOnboardingCard.addMoreSources.button": "その他のソースを追加", + "xpack.enterpriseSearch.workplaceSearch.sourcesOnboardingCard.description": "{sourcesCount, number}{sourcesCount, plural, other {組織ソース}}を追加しました。検索をご利用ください。", "xpack.enterpriseSearch.workplaceSearch.sourcesView.modal.docPermissions.description": "ユーザーおよびグループマッピングが構成されるまでは、ドキュメントをWorkplace Searchから検索できません。{documentPermissionsLink}", "xpack.enterpriseSearch.workplaceSearch.sourcesView.modal.heading": "{addedSourceName}には追加の構成が必要です", "xpack.enterpriseSearch.workplaceSearch.sourcesView.modal.success": "{addedSourceName}は正常に接続されました。初期コンテンツ同期がすでに実行中です。ドキュメントレベルのアクセス権情報を同期することを選択したので、{externalIdentitiesLink}を使用してユーザーおよびグループマッピングを指定する必要があります。", @@ -11623,9 +11722,11 @@ "xpack.fleet.addAgentHelpPopover.footActionButton": "OK", "xpack.fleet.addAgentHelpPopover.popoverBody": "統合が正常に動作するために、{elasticAgent}をホストに追加し、データを収集して、Elastic Stackに送信してください。{learnMoreLink}", "xpack.fleet.addAgentHelpPopover.title": "必ずエージェントをホストに追加してください", + "xpack.fleet.agentBulkActions.agentsSelected": "{count, plural, other {#個のエージェント} =all {すべてのエージェント}}が選択されました", "xpack.fleet.agentBulkActions.clearSelection": "選択した項目をクリア", "xpack.fleet.agentBulkActions.reassignPolicy": "新しいポリシーに割り当てる", "xpack.fleet.agentBulkActions.selectAll": "すべてのページのすべての項目を選択", + "xpack.fleet.agentBulkActions.totalAgents": "{count, plural, other {#個のエージェント}}を表示しています", "xpack.fleet.agentBulkActions.totalAgentsWithLimit": "{count}/{total}個のエージェントを表示しています", "xpack.fleet.agentBulkActions.unenrollAgents": "エージェントの登録を解除", "xpack.fleet.agentBulkActions.upgradeAgents": "エージェントをアップグレード", @@ -11756,6 +11857,7 @@ "xpack.fleet.agentPolicy.confirmModalConfirmButtonLabel": "変更を保存してデプロイ", "xpack.fleet.agentPolicy.confirmModalDescription": "このアクションは元に戻せません。続行していいですか?", "xpack.fleet.agentPolicy.confirmModalTitle": "変更を保存してデプロイ", + "xpack.fleet.agentPolicy.linkedAgentCountText": "{count, plural, other {#個のエージェント}}", "xpack.fleet.agentPolicy.postInstallAddAgentModal": "{packageName}統合が追加されました", "xpack.fleet.agentPolicy.postInstallAddAgentModalCancelButtonLabel": "後でElasticエージェントを追加", "xpack.fleet.agentPolicy.postInstallAddAgentModalConfirmButtonLabel": "Elasticエージェントをホストに追加", @@ -11811,8 +11913,10 @@ "xpack.fleet.agentPolicySummaryLine.revisionNumber": "rev. {revNumber}", "xpack.fleet.agentReassignPolicy.cancelButtonLabel": "キャンセル", "xpack.fleet.agentReassignPolicy.continueButtonLabel": "ポリシーの割り当て", + "xpack.fleet.agentReassignPolicy.flyoutDescription": "選択した {count, plural, other {エージェント}}を割り当てる新しいエージェントポリシーを選択します。", "xpack.fleet.agentReassignPolicy.flyoutTitle": "新しいエージェントポリシーを割り当てる", "xpack.fleet.agentReassignPolicy.packageBadgeFleetServerWarning": "Fleetサーバーはスタンドアロンモードで有効にされません。", + "xpack.fleet.agentReassignPolicy.policyDescription": "選択したエージェントポリシーは、{count, plural, other {{countValue}個の統合}}のデータを収集します。", "xpack.fleet.agentReassignPolicy.selectPolicyLabel": "エージェントポリシー", "xpack.fleet.agentReassignPolicy.successSingleNotificationTitle": "エージェントポリシーが再割り当てされました", "xpack.fleet.agentsInitializationErrorMessageTitle": "Elasticエージェントの集中管理を初期化できません", @@ -11881,6 +11985,7 @@ "xpack.fleet.createPackagePolicy.pageTitleWithPackageName": "{packageName}統合の追加", "xpack.fleet.createPackagePolicy.saveButton": "保存して続行", "xpack.fleet.createPackagePolicy.stepConfigure.advancedOptionsToggleLinkText": "高度なオプション", + "xpack.fleet.createPackagePolicy.stepConfigure.errorCountText": "{count, plural, other {# 件のエラー}}", "xpack.fleet.createPackagePolicy.stepConfigure.hideStreamsAriaLabel": "{type}入力を非表示", "xpack.fleet.createPackagePolicy.stepConfigure.inputSettingsDescription": "次の設定は以下のすべての入力に適用されます。", "xpack.fleet.createPackagePolicy.stepConfigure.inputSettingsTitle": "設定", @@ -11897,6 +12002,7 @@ "xpack.fleet.createPackagePolicy.stepConfigure.toggleAdvancedOptionsButtonText": "高度なオプション", "xpack.fleet.createPackagePolicy.stepConfigurePackagePolicyTitle": "統合の構成", "xpack.fleet.createPackagePolicy.stepSelectAgentPolicyTitle": "この統合を追加する場所", + "xpack.fleet.createPackagePolicy.StepSelectPolicy.agentPolicyAgentsDescriptionText": "{count, plural, other {# 個のエージェント}}が選択したエージェントポリシーで登録されています。", "xpack.fleet.createPackagePolicy.StepSelectPolicy.agentPolicyFormGroupDescription": "エージェントポリシーは、エージェントのセットで統合のグループを管理するために使用されます。", "xpack.fleet.createPackagePolicy.StepSelectPolicy.agentPolicyFormGroupTitle": "エージェントポリシー", "xpack.fleet.createPackagePolicy.StepSelectPolicy.agentPolicyLabel": "エージェントポリシー", @@ -11921,6 +12027,7 @@ "xpack.fleet.dataStreamList.viewDashboardActionText": "ダッシュボードを表示", "xpack.fleet.dataStreamList.viewDashboardsActionText": "ダッシュボードを表示", "xpack.fleet.dataStreamList.viewDashboardsPanelTitle": "ダッシュボードを表示", + "xpack.fleet.deleteAgentPolicy.confirmModal.affectedAgentsMessage": "{agentsCount, plural, other {#個のエージェントが}}このエージェントポリシーに割り当てられました。このポリシーを削除する前に、これらのエージェントの割り当てを解除します。", "xpack.fleet.deleteAgentPolicy.confirmModal.affectedAgentsTitle": "使用中のポリシー", "xpack.fleet.deleteAgentPolicy.confirmModal.cancelButtonLabel": "キャンセル", "xpack.fleet.deleteAgentPolicy.confirmModal.confirmButtonLabel": "ポリシーを削除", @@ -11933,7 +12040,9 @@ "xpack.fleet.deleteAgentPolicy.fatalErrorNotificationTitle": "エージェントポリシーの削除エラー", "xpack.fleet.deleteAgentPolicy.successSingleNotificationTitle": "エージェントポリシー「{id}」が削除されました", "xpack.fleet.deletePackagePolicy.confirmModal.affectedAgentsMessage": "{agentPolicyName}が一部のエージェントですでに使用されていることをFleetが検出しました。", + "xpack.fleet.deletePackagePolicy.confirmModal.affectedAgentsTitle": "このアクションは {agentsCount} {agentsCount, plural, other {# 個のエージェント}}に影響します。", "xpack.fleet.deletePackagePolicy.confirmModal.cancelButtonLabel": "キャンセル", + "xpack.fleet.deletePackagePolicy.confirmModal.deleteMultipleTitle": "{count, plural, other {# 個の統合}}を削除しますか?", "xpack.fleet.deletePackagePolicy.confirmModal.generalMessage": "このアクションは元に戻せません。続行していいですか?", "xpack.fleet.deletePackagePolicy.confirmModal.loadingAgentsCountMessage": "影響があるエージェントを確認中…", "xpack.fleet.deletePackagePolicy.confirmModal.loadingButtonLabel": "読み込み中…", @@ -12167,6 +12276,8 @@ "xpack.fleet.hostsInput.placeholder": "ホストURLを指定", "xpack.fleet.initializationErrorMessageTitle": "Fleet を初期化できません", "xpack.fleet.integration.settings.versionInfo.updatesAvailableBody": "バージョン{latestVersion}にアップグレードして最新の機能を入手", + "xpack.fleet.integrations.confirmUpdateModal.body.agentCount": "{agentCount, plural, other {# 個のエージェント}}", + "xpack.fleet.integrations.confirmUpdateModal.body.policyCount": "{packagePolicyCount, plural, other {# 個の統合ポリシー}}", "xpack.fleet.integrations.customInputsLink": "カスタム入力", "xpack.fleet.integrations.deploymentButton": "デプロイ詳細の表示", "xpack.fleet.integrations.discussForumLink": "フォーラム", @@ -12199,8 +12310,11 @@ "xpack.fleet.integrations.settings.confirmUninstallModal.uninstallCallout.title": "{numOfAssets}個のアセットが削除されます", "xpack.fleet.integrations.settings.confirmUninstallModal.uninstallDescription": "この操作は元に戻すことができません。続行していいですか?", "xpack.fleet.integrations.settings.confirmUninstallModal.uninstallTitle": "{packageName}をアンインストール", + "xpack.fleet.integrations.settings.confirmUpdateModal.body": "このアクションでは、これらのポリシーを使用するすべてのエージェントに更新がデプロイされます。Fleetは{packagePolicyCountText} {packagePolicyCount, plural, other {が}}アップグレード可能で、{packagePolicyCount, plural, other {が}}すでに{agentCountText}によって使用されていることを検出しました。", "xpack.fleet.integrations.settings.confirmUpdateModal.cancel": "キャンセル", "xpack.fleet.integrations.settings.confirmUpdateModal.confirm": "{packageName}とポリシーのアップグレード", + "xpack.fleet.integrations.settings.confirmUpdateModal.conflictCallOut.body": "{conflictCount, plural, other {には}}競合があるため、自動的にアップグレードされません。このアップグレードを実行した後に、Fleetのエージェントポリシー設定を使用して、これらの競合を手動で解決できます。", + "xpack.fleet.integrations.settings.confirmUpdateModal.conflictCallOut.integrationPolicyCount": "{conflictCount, plural, other { # 個の統合ポリシー}}", "xpack.fleet.integrations.settings.confirmUpdateModal.conflictCallOut.title": "一部の統合ポリシーが競合しています", "xpack.fleet.integrations.settings.confirmUpdateModal.updateTitle": "{packageName}とポリシーのアップグレード", "xpack.fleet.integrations.settings.keepIntegrationPoliciesUpToDateDescription": "有効にすると、Fleetで自動的に統合ポリシーをアップグレードおよびデプロイしようとします", @@ -12314,6 +12428,8 @@ "xpack.fleet.settings.confirmModal.cancelButtonText": "キャンセル", "xpack.fleet.settings.confirmModal.confirmButtonText": "保存してデプロイ", "xpack.fleet.settings.deleteHostButton": "ホストの削除", + "xpack.fleet.settings.deleteOutput.agentPolicyCount": "{agentPolicyCount, plural, other {# 件のエージェントポリシー}}", + "xpack.fleet.settings.deleteOutput.agentsCount": "{agentCount, plural, other {# 個のエージェント}}", "xpack.fleet.settings.deleteOutput.confirmModalText": "{outputName}出力が削除されます。{policies}と{agents}が更新されます。このアクションは元に戻せません。続行していいですか?", "xpack.fleet.settings.deleteOutput.confirmModalTitle": "変更を保存してデプロイしますか?", "xpack.fleet.settings.deleteOutputs.errorToastTitle": "出力の削除エラー", @@ -12341,6 +12457,8 @@ "xpack.fleet.settings.fleetServerHostSectionTitle": "Fleetサーバーホスト", "xpack.fleet.settings.fleetServerHostsEmptyError": "1つ以上のURLが必要です。", "xpack.fleet.settings.fleetServerHostsError": "無効なURL", + "xpack.fleet.settings.fleetServerHostsFlyout.agentPolicyCount": "{agentPolicyCount, plural, other {# 件のエージェントポリシー}}", + "xpack.fleet.settings.fleetServerHostsFlyout.agentsCount": "{agentCount, plural, other {# 個のエージェント}}", "xpack.fleet.settings.fleetServerHostsFlyout.cancelButtonLabel": "キャンセル", "xpack.fleet.settings.fleetServerHostsFlyout.confirmModalText": "{policies}と{agents}が更新されます。このアクションは元に戻せません。続行していいですか?", "xpack.fleet.settings.fleetServerHostsFlyout.confirmModalTitle": "変更を保存してデプロイしますか?", @@ -12372,6 +12490,8 @@ "xpack.fleet.settings.outputsTable.nameColomnTitle": "名前", "xpack.fleet.settings.outputsTable.typeColomnTitle": "型", "xpack.fleet.settings.sortHandle": "ホストハンドルの並べ替え", + "xpack.fleet.settings.updateOutput.agentPolicyCount": "{agentPolicyCount, plural, other {# 件のエージェントポリシー}}", + "xpack.fleet.settings.updateOutput.agentsCount": "{agentCount, plural, other {# 個のエージェント}}", "xpack.fleet.settings.updateOutput.confirmModalText": "{outputName}出力が更新されます。{policies}と{agents}が更新されます。このアクションは元に戻せません。続行していいですか?", "xpack.fleet.settings.updateOutput.confirmModalTitle": "変更を保存してデプロイしますか?", "xpack.fleet.settings.userGuideLink": "FleetおよびElasticエージェントガイド", @@ -12393,20 +12513,28 @@ "xpack.fleet.unenrollAgents.deleteMultipleDescription": "このアクションにより、複数のエージェントがFleetから削除され、新しいデータを取り込めなくなります。これらのエージェントによってすでに送信されたデータは一切影響を受けません。この操作は元に戻すことができません。", "xpack.fleet.unenrollAgents.deleteSingleDescription": "このアクションにより、「{hostName}」で実行中の選択したエージェントがFleetから削除されます。エージェントによってすでに送信されたデータは一切削除されません。この操作は元に戻すことができません。", "xpack.fleet.unenrollAgents.deleteSingleTitle": "エージェントの登録解除", + "xpack.fleet.unenrollAgents.fatalErrorNotificationTitle": "{count, plural, other {エージェント}}の登録解除エラー", "xpack.fleet.unenrollAgents.forceDeleteMultipleTitle": "{count}個のエージェントを登録解除", + "xpack.fleet.unenrollAgents.forceUnenrollCheckboxLabel": "{count, plural, other {エージェント}}を直ちに削除します。エージェントが最後のデータを送信するまで待機しない。", + "xpack.fleet.unenrollAgents.forceUnenrollLegendText": "{count, plural, other {エージェント}}を強制的に登録解除", "xpack.fleet.unenrollAgents.successForceMultiNotificationTitle": "エージェントが登録解除されました", "xpack.fleet.unenrollAgents.successForceSingleNotificationTitle": "エージェントが登録解除されました", "xpack.fleet.unenrollAgents.successMultiNotificationTitle": "エージェントを登録解除しています", "xpack.fleet.unenrollAgents.successSingleNotificationTitle": "エージェントを登録解除しています", "xpack.fleet.unenrollAgents.unenrollFleetServerDescription": "エージェントを登録解除すると、Fleetサーバーから切断されます。他のFleetサーバーが存在しない場合、エージェントはデータを送信できません。", "xpack.fleet.unenrollAgents.unenrollFleetServerTitle": "このエージェントはFleetサーバーを実行しています", + "xpack.fleet.upgradeAgents.bulkResultAllErrorsNotificationTitle": "{count, plural, other {{count}個のエージェント} =true {すべての選択されたエージェント}}のアップグレードエラー", + "xpack.fleet.upgradeAgents.bulkResultErrorResultsSummary": "{count} {count, plural, other {個のエージェント}}が成功しませんでした", "xpack.fleet.upgradeAgents.cancelButtonLabel": "キャンセル", + "xpack.fleet.upgradeAgents.confirmMultipleButtonLabel": "{count, plural, other {{count}個のエージェント} =true {すべての選択されたエージェント}}をアップグレード", "xpack.fleet.upgradeAgents.confirmSingleButtonLabel": "エージェントをアップグレード", "xpack.fleet.upgradeAgents.experimentalLabel": "実験的", "xpack.fleet.upgradeAgents.experimentalLabelTooltip": "アップグレードエージェントは今後のリリースで変更または削除される可能性があり、SLA のサポート対象になりません。", + "xpack.fleet.upgradeAgents.fatalErrorNotificationTitle": "{count, plural, other {{count}個のエージェント} =true {すべての選択されたエージェント}}のアップグレードエラー", "xpack.fleet.upgradeAgents.successMultiNotificationTitle": "{isMixed, select, true {{success}/{total}個の} other {{isAllAgents, select, true {すべての選択された} other {{success}} }}}エージェントをアップグレードしました", "xpack.fleet.upgradeAgents.successSingleNotificationTitle": "{count}個のエージェントをアップグレードしました", "xpack.fleet.upgradeAgents.upgradeMultipleDescription": "このアクションにより、複数のエージェントがバージョン{version}にアップグレードされます。このアクションは元に戻せません。続行していいですか?", + "xpack.fleet.upgradeAgents.upgradeMultipleTitle": "{count, plural, other {{count}個のエージェント} =true {すべての選択されたエージェント}}を最新バージョンにアップグレード", "xpack.fleet.upgradeAgents.upgradeSingleDescription": "このアクションにより、「{hostName}」で実行中のエージェントがバージョン{version}にアップグレードされます。このアクションは元に戻せません。続行していいですか?", "xpack.fleet.upgradeAgents.upgradeSingleTitle": "エージェントを最新バージョンにアップグレード", "xpack.fleet.upgradePackagePolicy.pageDescriptionFromUpgrade": "この統合をアップグレードし、選択したエージェントポリシーに変更をデプロイします", @@ -12425,6 +12553,7 @@ "xpack.globalSearchBar.searchBar.noResultsHeading": "結果が見つかりませんでした", "xpack.globalSearchBar.searchBar.noResultsImageAlt": "ブラックホールの図", "xpack.globalSearchBar.searchBar.optionTagListAriaLabel": "タグ", + "xpack.globalSearchBar.searchbar.overflowTagsAriaLabel": "{n} その他の {n, plural, other {個のタグ}}:{tags}", "xpack.globalSearchBar.searchBar.placeholder": "Elastic を検索", "xpack.globalSearchBar.searchBar.shortcutDescription.macCommandDescription": "コマンド+ /", "xpack.globalSearchBar.searchBar.shortcutDescription.shortcutDetail": "{shortcutDescription} {commandDescription}", @@ -12766,6 +12895,7 @@ "xpack.idxMgmt.componentTemplatesList.table.createButtonLabel": "コンポーネントテンプレートの作成", "xpack.idxMgmt.componentTemplatesList.table.deleteActionDescription": "このコンポーネントテンプレートを削除", "xpack.idxMgmt.componentTemplatesList.table.deleteActionLabel": "削除", + "xpack.idxMgmt.componentTemplatesList.table.deleteComponentTemplatesButtonLabel": "{count, plural, other {個のコンポーネントテンプレート} }を削除", "xpack.idxMgmt.componentTemplatesList.table.disabledSelectionLabel": "コンポーネントテンプレートは使用中であるため、削除できません", "xpack.idxMgmt.componentTemplatesList.table.inUseFilterOptionLabel": "使用中", "xpack.idxMgmt.componentTemplatesList.table.isInUseColumnTitle": "使用カウント", @@ -12835,6 +12965,7 @@ "xpack.idxMgmt.dataStreamList.table.actionColumnTitle": "アクション", "xpack.idxMgmt.dataStreamList.table.actionDeleteDescription": "このデータストリームを削除", "xpack.idxMgmt.dataStreamList.table.actionDeleteText": "削除", + "xpack.idxMgmt.dataStreamList.table.deleteDataStreamsButtonLabel": "{count, plural, other {個のデータストリーム}}を削除", "xpack.idxMgmt.dataStreamList.table.healthColumnTitle": "ヘルス", "xpack.idxMgmt.dataStreamList.table.hiddenDataStreamBadge": "非表示", "xpack.idxMgmt.dataStreamList.table.indicesColumnTitle": "インデックス", @@ -12850,19 +12981,26 @@ "xpack.idxMgmt.dataStreamListControls.includeStatsSwitchToolTip": "統計情報を含めると、再読み込み時間が長くなることがあります", "xpack.idxMgmt.dataStreamListDescription.learnMoreLinkText": "詳細情報", "xpack.idxMgmt.deleteDataStreamsConfirmationModal.cancelButtonLabel": "キャンセル", + "xpack.idxMgmt.deleteDataStreamsConfirmationModal.confirmButtonLabel": "{dataStreamsCount, plural, other {個のデータストリーム}}を削除", "xpack.idxMgmt.deleteDataStreamsConfirmationModal.deleteDescription": "{dataStreamsCount, plural, one {このデータストリーム} other {これらのデータストリーム}}を削除しようとしています。", "xpack.idxMgmt.deleteDataStreamsConfirmationModal.errorNotificationMessageText": "データストリーム「{name}」の削除エラー", + "xpack.idxMgmt.deleteDataStreamsConfirmationModal.modalTitleText": "{dataStreamsCount, plural, other {# 個のデータストリーム}}を削除", "xpack.idxMgmt.deleteDataStreamsConfirmationModal.multipleErrorsNotificationMessageText": "{count}件のデータストリームの削除エラー", + "xpack.idxMgmt.deleteDataStreamsConfirmationModal.successDeleteMultipleNotificationMessageText": "{numSuccesses, plural, other {# 個のデータストリーム}}を削除しました", "xpack.idxMgmt.deleteDataStreamsConfirmationModal.successDeleteSingleNotificationMessageText": "データストリーム「{dataStreamName}」を削除しました", "xpack.idxMgmt.deleteDataStreamsConfirmationModal.warningMessage": "データストリームは時系列インデックスのコレクションです。データストリームを削除すると、インデックスも削除されます。", "xpack.idxMgmt.deleteDataStreamsConfirmationModal.warningTitle": "データストリームを削除すると、インデックスも削除されます", "xpack.idxMgmt.deleteIndicesAction.successfullyDeletedIndicesMessage": "[{indexNames}] が削除されました", "xpack.idxMgmt.deleteTemplatesModal.cancelButtonLabel": "キャンセル", + "xpack.idxMgmt.deleteTemplatesModal.confirmButtonLabel": "{numTemplatesToDelete, plural, other {個のテンプレート} }を削除", "xpack.idxMgmt.deleteTemplatesModal.confirmDeleteCheckboxLabel": "システムテンプレートを削除することの重大な影響を理解しています", + "xpack.idxMgmt.deleteTemplatesModal.deleteDescription": "{numTemplatesToDelete, plural, other {これらのテンプレート} }を削除しようとしています:", "xpack.idxMgmt.deleteTemplatesModal.errorNotificationMessageText": "テンプレート「{name}」の削除中にエラーが発生", + "xpack.idxMgmt.deleteTemplatesModal.modalTitleText": "{numTemplatesToDelete, plural, other {# 個のテンプレート}}の削除", "xpack.idxMgmt.deleteTemplatesModal.multipleErrorsNotificationMessageText": "{count} 個のテンプレートの削除中にエラーが発生", "xpack.idxMgmt.deleteTemplatesModal.proceedWithCautionCallOutDescription": "システムテンプレートは内部オペレーションに不可欠です。このテンプレートを削除すると、復元することはできません。", "xpack.idxMgmt.deleteTemplatesModal.proceedWithCautionCallOutTitle": "システムテンプレートを削除することで、Kibana に重大な障害が生じる可能性があります", + "xpack.idxMgmt.deleteTemplatesModal.successDeleteMultipleNotificationMessageText": "{numSuccesses, plural, other {# 個のテンプレート}}を削除しました", "xpack.idxMgmt.deleteTemplatesModal.successDeleteSingleNotificationMessageText": "テンプレート「{templateName}」を削除しました", "xpack.idxMgmt.deleteTemplatesModal.systemTemplateLabel": "システムテンプレート", "xpack.idxMgmt.deprecations.enabled.manualStepOneMessage": "kibana.yml構成ファイルを開きます。", @@ -12907,10 +13045,13 @@ "xpack.idxMgmt.home.appTitle": "インデックス管理", "xpack.idxMgmt.home.componentTemplates.checkingPrivilegesDescription": "権限を確認中…", "xpack.idxMgmt.home.componentTemplates.checkingPrivilegesErrorMessage": "サーバーからユーザー特権を取得中にエラーが発生。", + "xpack.idxMgmt.home.componentTemplates.confirmButtonLabel": "{numComponentTemplatesToDelete, plural, other {個のコンポーネントテンプレート} }を削除", "xpack.idxMgmt.home.componentTemplates.deleteModal.cancelButtonLabel": "キャンセル", "xpack.idxMgmt.home.componentTemplates.deleteModal.deleteDescription": "{numComponentTemplatesToDelete, plural, one {このコンポーネントテンプレート} other {これらのコンポーネントテンプレート} }を削除しようとしています。", "xpack.idxMgmt.home.componentTemplates.deleteModal.errorNotificationMessageText": "コンポーネントテンプレート「{name}」の削除エラー", + "xpack.idxMgmt.home.componentTemplates.deleteModal.modalTitleText": "{numComponentTemplatesToDelete, plural, other {# 個のコンポーネントテンプレート}}を削除", "xpack.idxMgmt.home.componentTemplates.deleteModal.multipleErrorsNotificationMessageText": "{count}個のコンポーネントテンプレートの削除エラー", + "xpack.idxMgmt.home.componentTemplates.deleteModal.successDeleteMultipleNotificationMessageText": "{numSuccesses, plural, other {# 個のコンポーネントテンプレート}}を削除しました", "xpack.idxMgmt.home.componentTemplates.deleteModal.successDeleteSingleNotificationMessageText": "コンポーネントテンプレート「{componentTemplateName}」を削除しました", "xpack.idxMgmt.home.componentTemplates.deniedPrivilegeDescription": "コンポーネントテンプレートを使用するには、{privilegesCount, plural, one {このクラスター特権} other {これらのクラスター特権}}が必要です:{missingPrivileges}。", "xpack.idxMgmt.home.componentTemplates.deniedPrivilegeTitle": "クラスターの権限が必要です", @@ -12932,18 +13073,27 @@ "xpack.idxMgmt.home.legacyIndexTemplatesDeprecation.ctaLearnMoreLinkText": "詳細情報。", "xpack.idxMgmt.home.legacyIndexTemplatesDeprecation.learnMoreLinkText": "詳細情報", "xpack.idxMgmt.home.legacyIndexTemplatesTitle": "レガシーインデックステンプレート", + "xpack.idxMgmt.indexActionsMenu.clearIndexCacheLabel": "{selectedIndexCount, plural, other {個のインデックス} }のキャッシュを消去", "xpack.idxMgmt.indexActionsMenu.closeIndex.checkboxLabel": "システムインデックスを閉じることの重大な影響を理解しています", "xpack.idxMgmt.indexActionsMenu.closeIndex.closeDescription": "{selectedIndexCount, plural, one {このインデックス} other {これらのインデックス} }を閉じようとしています。", + "xpack.idxMgmt.indexActionsMenu.closeIndex.confirmModal.confirmButtonText": "{selectedIndexCount, plural, other {個のインデックス} }を閉じる", + "xpack.idxMgmt.indexActionsMenu.closeIndex.confirmModal.modalTitle": "{selectedIndexCount, plural, other {# 個のインデックス} }を閉じる", "xpack.idxMgmt.indexActionsMenu.closeIndex.proceedWithCautionCallOutDescription": "システムインデックスは内部オペレーションに不可欠です。Open Index APIを使用して再オープンすることができます。", "xpack.idxMgmt.indexActionsMenu.closeIndex.proceedWithCautionCallOutTitle": "システムインデックスを閉じることで、Kibanaに重大な障害が生じる可能性があります", "xpack.idxMgmt.indexActionsMenu.closeIndex.systemIndexLabel": "システムインデックス", + "xpack.idxMgmt.indexActionsMenu.closeIndexLabel": "{selectedIndexCount, plural, other {個のインデックス} }を閉じる", "xpack.idxMgmt.indexActionsMenu.deleteIndex.checkboxLabel": "システムインデックスを削除することの重大な影響を理解しています", "xpack.idxMgmt.indexActionsMenu.deleteIndex.confirmModal.cancelButtonText": "キャンセル", + "xpack.idxMgmt.indexActionsMenu.deleteIndex.confirmModal.confirmButtonText": "{selectedIndexCount, plural, other {個のインデックス} }を削除", + "xpack.idxMgmt.indexActionsMenu.deleteIndex.confirmModal.modalTitle": "{selectedIndexCount, plural, other {# 個のインデックス} } の削除", "xpack.idxMgmt.indexActionsMenu.deleteIndex.deleteDescription": "{selectedIndexCount, plural, one {このインデックス} other {これらのインデックス} }を削除しようとしています:", "xpack.idxMgmt.indexActionsMenu.deleteIndex.deleteWarningDescription": "削除されたインデックスは復元できません。適切なバックアップがあることを確認してください。", "xpack.idxMgmt.indexActionsMenu.deleteIndex.proceedWithCautionCallOutDescription": "システムインデックスは内部オペレーションに不可欠です。システムインデックスを削除すると、復元することはできません。適切なバックアップがあることを確認してください。", "xpack.idxMgmt.indexActionsMenu.deleteIndex.proceedWithCautionCallOutTitle": "十分ご注意ください!", "xpack.idxMgmt.indexActionsMenu.deleteIndex.systemIndexLabel": "システムインデックス", + "xpack.idxMgmt.indexActionsMenu.deleteIndexLabel": "{selectedIndexCount, plural, other {個のインデックス} }を削除", + "xpack.idxMgmt.indexActionsMenu.editIndexSettingsLabel": "{selectedIndexCount, plural, other {個のインデックス} }の設定を編集", + "xpack.idxMgmt.indexActionsMenu.flushIndexLabel": "{selectedIndexCount, plural, other {個のインデックス} }をフラッシュ", "xpack.idxMgmt.indexActionsMenu.forceMerge.confirmModal.cancelButtonText": "キャンセル", "xpack.idxMgmt.indexActionsMenu.forceMerge.confirmModal.confirmButtonText": "強制結合", "xpack.idxMgmt.indexActionsMenu.forceMerge.confirmModal.modalTitle": "強制結合", @@ -12952,7 +13102,17 @@ "xpack.idxMgmt.indexActionsMenu.forceMerge.forceMergeWarningDescription": " まだ書き込み中のインデックスや、将来もう一度書き込む予定がある強制・マージしないでください。自動バックグラウンドマージプロセスを活用して、スムーズなインデックス実行に必要なマージを実行できます。強制・マージインデックスに書き込む場合、パフォーマンスが大幅に低下する可能性があります。", "xpack.idxMgmt.indexActionsMenu.forceMerge.maximumNumberOfSegmentsFormRowLabel": "シャードごとの最大セグメント数", "xpack.idxMgmt.indexActionsMenu.forceMerge.proceedWithCautionCallOutTitle": "十分ご注意ください!", + "xpack.idxMgmt.indexActionsMenu.forceMergeIndexLabel": "{selectedIndexCount, plural, other {個のインデックス} }を強制結合", + "xpack.idxMgmt.indexActionsMenu.manageButtonAriaLabel": "{selectedIndexCount, plural, other {個のインデックス} }オプション", + "xpack.idxMgmt.indexActionsMenu.manageButtonLabel": "{selectedIndexCount, plural, other {{selectedIndexCount} 個のインデックス}}を管理", + "xpack.idxMgmt.indexActionsMenu.openIndexLabel": "{selectedIndexCount, plural, other {個のインデックス} }を開く", + "xpack.idxMgmt.indexActionsMenu.panelTitle": "{selectedIndexCount, plural, other {個のインデックス} }オプション", + "xpack.idxMgmt.indexActionsMenu.refreshIndexLabel": "{selectedIndexCount, plural, other {個のインデックス} }を更新", "xpack.idxMgmt.indexActionsMenu.segmentsNumberErrorMessage": "セグメント数は 0 より大きい値である必要があります。", + "xpack.idxMgmt.indexActionsMenu.showIndexMappingLabel": "{selectedIndexCount, plural, other {個のインデックス} }のマッピングを表示", + "xpack.idxMgmt.indexActionsMenu.showIndexSettingsLabel": "{selectedIndexCount, plural, other {個のインデックス} }の設定を表示", + "xpack.idxMgmt.indexActionsMenu.showIndexStatsLabel": "{selectedIndexCount, plural, other {個のインデックス} }の統計を表示", + "xpack.idxMgmt.indexActionsMenu.unfreezeIndexLabel": "{selectedIndexCount, plural, other {個のインデックス} }の凍結を解除", "xpack.idxMgmt.indexStatusLabels.clearingCacheStatusLabel": "キャッシュを消去中...", "xpack.idxMgmt.indexStatusLabels.closedStatusLabel": "クローズ済み", "xpack.idxMgmt.indexStatusLabels.closingStatusLabel": "クローズ中...", @@ -12961,6 +13121,7 @@ "xpack.idxMgmt.indexStatusLabels.mergingStatusLabel": "結合中...", "xpack.idxMgmt.indexStatusLabels.openingStatusLabel": "開いています...", "xpack.idxMgmt.indexStatusLabels.refreshingStatusLabel": "更新中...", + "xpack.idxMgmt.indexTable.captionText": "以下は {total} 列中 {count, plural, other {# 列}} を含むインデックステーブルです。", "xpack.idxMgmt.indexTable.headers.dataStreamHeader": "データストリーム", "xpack.idxMgmt.indexTable.headers.documentsHeader": "ドキュメント数", "xpack.idxMgmt.indexTable.headers.healthHeader": "ヘルス", @@ -13366,6 +13527,7 @@ "xpack.idxMgmt.mappingsEditor.loadJsonModal.validationError.fieldMessage": "{fieldPath}フィールドは無効です。", "xpack.idxMgmt.mappingsEditor.loadJsonModal.validationError.parameterMessage": "{fieldPath}フィールドの{paramName}パラメーターは無効です。", "xpack.idxMgmt.mappingsEditor.loadJsonModal.validationErrorDescription": "引き続きオブジェクトを読み込む場合、有効なオプションのみが受け入れられます。", + "xpack.idxMgmt.mappingsEditor.loadJsonModal.validationErrorTitle": "{totalErrors} {totalErrors, plural , other {個の無効なオプション}}が{mappings}オブジェクトで検出されました", "xpack.idxMgmt.mappingsEditor.loadJsonModalTitle": "JSONの読み込み", "xpack.idxMgmt.mappingsEditor.mappingTypesDetectedCallOutDescription": "このテンプレートのマッピングは削除されたタイプを使用しています。{docsLink}", "xpack.idxMgmt.mappingsEditor.mappingTypesDetectedCallOutDocumentationLink": "タイプのマッピングにはこれらの代替方法を検討してください。", @@ -13570,6 +13732,7 @@ "xpack.idxMgmt.templateDetails.summaryTab.componentsDescriptionListTitle": "コンポーネントテンプレート", "xpack.idxMgmt.templateDetails.summaryTab.dataStreamDescriptionListTitle": "データストリーム", "xpack.idxMgmt.templateDetails.summaryTab.ilmPolicyDescriptionListTitle": "ILMポリシー", + "xpack.idxMgmt.templateDetails.summaryTab.indexPatternsDescriptionListTitle": "インデックス{numIndexPatterns, plural, other {個のパターン}}", "xpack.idxMgmt.templateDetails.summaryTab.metaDescriptionListTitle": "メタデータ", "xpack.idxMgmt.templateDetails.summaryTab.noDescriptionText": "いいえ", "xpack.idxMgmt.templateDetails.summaryTab.noneDescriptionText": "なし", @@ -13624,6 +13787,7 @@ "xpack.idxMgmt.templateForm.stepReview.stepTitle": "「{templateName}」の詳細の確認", "xpack.idxMgmt.templateForm.stepReview.summaryTab.aliasesLabel": "エイリアス", "xpack.idxMgmt.templateForm.stepReview.summaryTab.componentsLabel": "コンポーネントテンプレート", + "xpack.idxMgmt.templateForm.stepReview.summaryTab.indexPatternsLabel": "インデックス{numIndexPatterns, plural, other {個のパターン}}", "xpack.idxMgmt.templateForm.stepReview.summaryTab.indexPatternsWarningDescription": "作成するすべての新規インデックスにこのテンプレートが使用されます。", "xpack.idxMgmt.templateForm.stepReview.summaryTab.indexPatternsWarningLinkText": "インデックスパターンの編集。", "xpack.idxMgmt.templateForm.stepReview.summaryTab.indexPatternsWarningTitle": "このテンプレートはワイルドカード(*)をインデックスパターンとして使用します。", @@ -13653,6 +13817,7 @@ "xpack.idxMgmt.templateList.legacyTable.contentColumnTitle": "コンテンツ", "xpack.idxMgmt.templateList.legacyTable.createLegacyTemplatesButtonLabel": "レガシーテンプレートの作成", "xpack.idxMgmt.templateList.legacyTable.deleteCloudManagedTemplateTooltip": "管理されているテンプレートは削除できません。", + "xpack.idxMgmt.templateList.legacyTable.deleteTemplatesButtonLabel": "{count, plural, other {個のテンプレート} }を削除", "xpack.idxMgmt.templateList.legacyTable.ilmPolicyColumnDescription": "インデックスライフサイクルポリシー「{policyName}」", "xpack.idxMgmt.templateList.legacyTable.ilmPolicyColumnTitle": "ILMポリシー", "xpack.idxMgmt.templateList.legacyTable.indexPatternsColumnTitle": "インデックスパターン", @@ -13670,6 +13835,7 @@ "xpack.idxMgmt.templateList.table.createTemplatesButtonLabel": "テンプレートを作成", "xpack.idxMgmt.templateList.table.dataStreamColumnTitle": "データストリーム", "xpack.idxMgmt.templateList.table.deleteCloudManagedTemplateTooltip": "管理されているテンプレートは削除できません。", + "xpack.idxMgmt.templateList.table.deleteTemplatesButtonLabel": "{count, plural, other {個のテンプレート} }を削除", "xpack.idxMgmt.templateList.table.indexPatternsColumnTitle": "インデックスパターン", "xpack.idxMgmt.templateList.table.nameColumnTitle": "名前", "xpack.idxMgmt.templateList.table.noIndexTemplatesMessage": "インデックステンプレートが見つかりません", @@ -13763,6 +13929,7 @@ "xpack.indexLifecycleMgmt.editPolicy.deletePhase.waitForSnapshotDescription": "インデックスを削除する前に実行するスナップショットポリシーを指定します。これにより、削除されたインデックスのスナップショットが利用可能であることが保証されます。", "xpack.indexLifecycleMgmt.editPolicy.deletePhase.waitForSnapshotLabel": "スナップショットポリシー名", "xpack.indexLifecycleMgmt.editPolicy.deletePhase.waitForSnapshotTitle": "スナップショットポリシーを待機", + "xpack.indexLifecycleMgmt.editPolicy.dependenciesMessage": " 行ったすべての変更は、このポリシーに関連付けられ{count, plural, other {ている}}{dependenciesLinks}に影響します。", "xpack.indexLifecycleMgmt.editPolicy.differentPolicyNameRequiredError": "ポリシー名は異なるものである必要があります。", "xpack.indexLifecycleMgmt.editPolicy.documentationLinkText": "ドキュメント", "xpack.indexLifecycleMgmt.editPolicy.editingExistingPolicyMessage": "既存のポリシーを編集しています。", @@ -13804,6 +13971,8 @@ "xpack.indexLifecycleMgmt.editPolicy.learnAboutTimingText": "タイミングの詳細をご覧ください", "xpack.indexLifecycleMgmt.editPolicy.lifecyclePoliciesLoadingFailedTitle": "既存のライフサイクルポリシーを読み込めません", "xpack.indexLifecycleMgmt.editPolicy.lifecyclePoliciesReloadButton": "再試行", + "xpack.indexLifecycleMgmt.editPolicy.linkedIndexTemplates": "{indexTemplatesCount, plural, other {# 個のリンクされたインデックステンプレート}}", + "xpack.indexLifecycleMgmt.editPolicy.linkedIndices": "{indicesCount, plural, other {# リンクされたインデックス}}", "xpack.indexLifecycleMgmt.editPolicy.loadSnapshotRepositoriesErrorBody": "このフィールドを更新し、既存のスナップショットリポジトリの名前を入力します。", "xpack.indexLifecycleMgmt.editPolicy.loadSnapshotRepositoriesErrorTitle": "スナップショットリポジトリを読み込めません", "xpack.indexLifecycleMgmt.editPolicy.maximumPrimaryShardSizeAriaLabel": "最大シャードサイズ単位", @@ -13930,9 +14099,12 @@ "xpack.indexLifecycleMgmt.indexManagementTable.addLifecyclePolicyToTemplateConfirmModal.errorLoadingTemplatesButton": "再試行", "xpack.indexLifecycleMgmt.indexManagementTable.addLifecyclePolicyToTemplateConfirmModal.indexHasNoAliasesWarningMessage": "このインデックステンプレートにはすでにポリシー {existingPolicyName} が適用されています。このポリシーを追加するとこの構成が上書きされます。", "xpack.indexLifecycleMgmt.indexManagementTable.removeLifecyclePolicyConfirmModal.cancelButtonText": "キャンセル", + "xpack.indexLifecycleMgmt.indexManagementTable.removeLifecyclePolicyConfirmModal.modalTitle": "{count, plural, other {個のインデックス}}からライフサイクルポリシーを削除します", "xpack.indexLifecycleMgmt.indexManagementTable.removeLifecyclePolicyConfirmModal.removeMessage": "{count, plural, one {このインデックス} other {これらのインデックス}}からインデックスポリシーを削除しようとしています。この操作は元に戻すことができません。", "xpack.indexLifecycleMgmt.indexManagementTable.removeLifecyclePolicyConfirmModal.removePolicyButtonText": "ポリシーを削除", + "xpack.indexLifecycleMgmt.indexManagementTable.removeLifecyclePolicyConfirmModal.removePolicySuccess": "{count, plural, other {個のインデックス}}からライフサイクルポリシーを削除しました", "xpack.indexLifecycleMgmt.indexManagementTable.removeLifecyclePolicyConfirmModal.removePolicyToIndexError": "ポリシーの削除中にエラーが発生しました", + "xpack.indexLifecycleMgmt.indexMgmtBanner.errorMessage": "{ numIndicesWithLifecycleErrors, number}\n {numIndicesWithLifecycleErrors, plural, other {インデックスには} }\n インデックスエラー", "xpack.indexLifecycleMgmt.indexMgmtBanner.filterLabel": "エラーを表示", "xpack.indexLifecycleMgmt.indexMgmtFilter.coldLabel": "コールド", "xpack.indexLifecycleMgmt.indexMgmtFilter.deleteLabel": "削除", @@ -13975,6 +14147,7 @@ "xpack.indexLifecycleMgmt.policyTable.addLifecyclePolicyToTemplateConfirmModal.templateHasPolicyWarningTitle": "テンプレートにすでにポリシーがあります", "xpack.indexLifecycleMgmt.policyTable.addLifecyclePolicyToTemplateConfirmModal.title": "インデックステンプレートにポリシー「{name}」 を追加", "xpack.indexLifecycleMgmt.policyTable.addPolicyToTemplateButtonText": "インデックステンプレートにポリシーを追加", + "xpack.indexLifecycleMgmt.policyTable.captionText": "次の表には{count, plural, other {# 個のインデックスライフサイクルポリシー}}が含まれています。", "xpack.indexLifecycleMgmt.policyTable.deletePolicyButtonDisabledTooltip": "インデックスが使用中のポリシーは削除できません", "xpack.indexLifecycleMgmt.policyTable.deletePolicyButtonText": "ポリシーを削除", "xpack.indexLifecycleMgmt.policyTable.emptyPrompt.createButtonLabel": "ポリシーを作成", @@ -14049,6 +14222,7 @@ "xpack.infra.analysisSetup.endTimeAfterStartTimeErrorMessage": "終了時刻は開始時刻よりも後でなければなりません。", "xpack.infra.analysisSetup.endTimeDefaultDescription": "永久", "xpack.infra.analysisSetup.endTimeLabel": "終了時刻", + "xpack.infra.analysisSetup.indexDatasetFilterIncludeAllButtonLabel": "{includeType, select, includeAll {すべてのデータセット} includeSome {{includedDatasetCount, plural, other {# 個のデータセット}}}}", "xpack.infra.analysisSetup.indicesSelectionDescription": "既定では、機械学習は、ソースに対して構成されたすべてのログインデックスにあるログメッセージを分析します。インデックス名のサブセットのみを分析することを選択できます。すべての選択したインデックス名は、ログエントリを含む1つ以上のインデックスと一致する必要があります。特定のデータセットのサブセットのみを含めることを選択できます。データセットフィルターはすべての選択したインデックスに適用されます。", "xpack.infra.analysisSetup.indicesSelectionIndexNotFound": "インデックスがパターン{index}と一致しません", "xpack.infra.analysisSetup.indicesSelectionLabel": "インデックス", @@ -14086,6 +14260,7 @@ "xpack.infra.deprecations.containerIdFieldTitle": "ソース構成フィールド[コンテナーID]は廃止予定です。", "xpack.infra.deprecations.deprecatedFieldConfigDescription": "「xpack.infra.sources.default.fields.{fieldKey}」の構成は廃止予定であり、8.0.0で削除されます。", "xpack.infra.deprecations.deprecatedFieldConfigTitle": "\"{fieldKey}\"は廃止予定です。", + "xpack.infra.deprecations.deprecatedFieldDescription": "\"{fieldName}\"フィールドの構成は廃止予定です。8.0.0で削除されます。このプラグインはECSと連動するように設計され、このフィールドには`{defaultValue}`の値が入ることが想定されています。ソース{configCount, plural, other {構成}}で別の値が設定されています:{configNames}", "xpack.infra.deprecations.hostAdjustIndexing": "インデックスを調整し、\"{field}\"を使用してホストを特定", "xpack.infra.deprecations.hostnameFieldName": "ホスト名", "xpack.infra.deprecations.hostnameFieldTitle": "ソース構成フィールド[ホスト名]は廃止予定です。", @@ -14266,7 +14441,9 @@ "xpack.infra.logs.analsysisSetup.indexQualityWarningTooltipMessage": "これらのインデックスからのログメッセージの分析中に、結果の品質を低下させる可能性がある一部の問題が検出されました。これらのインデックスや問題のあるデータセットを分析から除外することを検討してください。", "xpack.infra.logs.analysis.analyzeInMlButtonLabel": "ML で分析", "xpack.infra.logs.analysis.anomaliesExpandedRowActualRateDescription": "実際", + "xpack.infra.logs.analysis.anomaliesExpandedRowActualRateTitle": "{actualCount, plural, other {件のメッセージ}}", "xpack.infra.logs.analysis.anomaliesExpandedRowTypicalRateDescription": "通常", + "xpack.infra.logs.analysis.anomaliesExpandedRowTypicalRateTitle": "{typicalCount, plural, other {件のメッセージ}}", "xpack.infra.logs.analysis.anomaliesSectionLoadingAriaLabel": "異常を読み込み中", "xpack.infra.logs.analysis.anomaliesTableAnomalyDatasetName": "データセット", "xpack.infra.logs.analysis.anomaliesTableAnomalyMessageName": "異常", @@ -14320,10 +14497,19 @@ "xpack.infra.logs.customizeLogs.customizeButtonLabel": "カスタマイズ", "xpack.infra.logs.customizeLogs.lineWrappingFormRowLabel": "改行", "xpack.infra.logs.customizeLogs.textSizeFormRowLabel": "テキストサイズ", + "xpack.infra.logs.customizeLogs.textSizeRadioGroup": "{textScale, select, small {小さい} Medium {中くらい} Large {大きい } other {{textScale}}}", "xpack.infra.logs.customizeLogs.wrapLongLinesSwitchLabel": "長い行を改行", "xpack.infra.logs.emptyView.checkForNewDataButtonLabel": "新規データを確認", "xpack.infra.logs.emptyView.noLogMessageDescription": "フィルターを調整してみてください。", "xpack.infra.logs.emptyView.noLogMessageTitle": "表示するログメッセージがありません。", + "xpack.infra.logs.extendTimeframeByDaysButton": "{amount, number} {amount, plural, other {日}}だけタイムフレームを拡張", + "xpack.infra.logs.extendTimeframeByHoursButton": "{amount, number} {amount, plural, other {時間}}だけタイムフレームを拡張する", + "xpack.infra.logs.extendTimeframeByMillisecondsButton": "{amount, number} {amount, plural, other {ミリ秒}}だけタイムフレームを拡張", + "xpack.infra.logs.extendTimeframeByMinutesButton": "{amount, number} {amount, plural, other {分}}だけタイムフレームを拡張", + "xpack.infra.logs.extendTimeframeByMonthsButton": "{amount, number} {amount, plural, other {か月}}だけタイムフレームを拡張", + "xpack.infra.logs.extendTimeframeBySecondsButton": "{amount, number} {amount, plural, other {秒}}だけタイムフレームを拡張", + "xpack.infra.logs.extendTimeframeByWeeksButton": "{amount, number} {amount, plural, other {週間}}だけタイムフレームを拡張", + "xpack.infra.logs.extendTimeframeByYearsButton": "{amount, number} {amount, plural, other {年}}だけタイムフレームを拡張", "xpack.infra.logs.highlights.clearHighlightTermsButtonLabel": "ハイライトする用語をクリア", "xpack.infra.logs.highlights.goToNextHighlightButtonLabel": "次のハイライトにスキップ", "xpack.infra.logs.highlights.goToPreviousHighlightButtonLabel": "前のハイライトにスキップ", @@ -14360,6 +14546,7 @@ "xpack.infra.logs.logEntryCategories.singleCategoryWarningReasonDescription": "分析では、ログメッセージから2つ以上のカテゴリを抽出できませんでした。", "xpack.infra.logs.logEntryCategories.topCategoriesSectionLoadingAriaLabel": "メッセージカテゴリーを読み込み中", "xpack.infra.logs.logEntryCategories.trendColumnTitle": "傾向", + "xpack.infra.logs.logEntryCategories.truncatedPatternSegmentDescription": "{extraSegmentCount, plural, other {# 個の追加セグメント}}", "xpack.infra.logs.logEntryExamples.exampleEmptyDescription": "選択した時間範囲内に例は見つかりませんでした。ログエントリー保持期間を長くするとメッセージサンプルの可用性が向上します。", "xpack.infra.logs.logEntryExamples.exampleEmptyReloadButtonLabel": "再読み込み", "xpack.infra.logs.logEntryExamples.exampleLoadingFailureDescription": "サンプルの読み込みに失敗しました。", @@ -14376,6 +14563,7 @@ "xpack.infra.logs.search.previousButtonLabel": "前へ", "xpack.infra.logs.search.searchInLogsAriaLabel": "検索", "xpack.infra.logs.search.searchInLogsPlaceholder": "検索", + "xpack.infra.logs.searchResultTooltip": "{bucketCount, plural, other {# 件のハイライトされたエントリー}}", "xpack.infra.logs.showingEntriesFromTimestamp": "{timestamp} 以降のエントリーを表示中", "xpack.infra.logs.showingEntriesUntilTimestamp": "{timestamp} までのエントリーを表示中", "xpack.infra.logs.startStreamingButtonLabel": "ライブストリーム", @@ -15064,9 +15252,12 @@ "xpack.ingestPipelines.createRoute.duplicatePipelineIdErrorMessage": "「{name}」という名前のパイプラインがすでに存在します。", "xpack.ingestPipelines.csvToIngestPipeline.error.emptyFileErrors": "ファイル読み取りエラー:指定されたファイルは空です。", "xpack.ingestPipelines.deleteModal.cancelButtonLabel": "キャンセル", + "xpack.ingestPipelines.deleteModal.confirmButtonLabel": "{numPipelinesToDelete, plural, other {パイプライン} }を削除", "xpack.ingestPipelines.deleteModal.deleteDescription": " {numPipelinesToDelete, plural, one {このパイプライン} other {これらのパイプライン} }を削除しようとしています:", "xpack.ingestPipelines.deleteModal.errorNotificationMessageText": "パイプライン'{name}'の削除エラー", + "xpack.ingestPipelines.deleteModal.modalTitleText": "{numPipelinesToDelete, plural, other {# 個のパイプライン} }を削除", "xpack.ingestPipelines.deleteModal.multipleErrorsNotificationMessageText": "{count}件のパイプラインの削除エラー", + "xpack.ingestPipelines.deleteModal.successDeleteMultipleNotificationMessageText": "{numSuccesses, plural, other {#個のパイプライン}}を削除しました", "xpack.ingestPipelines.deleteModal.successDeleteSingleNotificationMessageText": "パイプライン'{pipelineName}'を削除しました", "xpack.ingestPipelines.edit.docsButtonLabel": "パイプラインドキュメントを編集", "xpack.ingestPipelines.edit.fetchPipelineError": "'{name}'を読み込めません", @@ -15085,8 +15276,10 @@ "xpack.ingestPipelines.form.onFailureFieldHelpText": "JSON フォーマットを使用:{code}", "xpack.ingestPipelines.form.pipelineNameRequiredError": "名前が必要です。", "xpack.ingestPipelines.form.saveButtonLabel": "パイプラインを保存", + "xpack.ingestPipelines.form.savePip10mbelineError.showFewerButton": "{hiddenErrorsCount, plural, other {# 件のエラー}}を非表示", "xpack.ingestPipelines.form.savePipelineError": "パイプラインを作成できません", "xpack.ingestPipelines.form.savePipelineError.processorLabel": "{type}プロセッサー", + "xpack.ingestPipelines.form.savePipelineError.showAllButton": "{hiddenErrorsCount, plural, other {# 件のエラー}}を追加で表示", "xpack.ingestPipelines.form.savingButtonLabel": "保存中...", "xpack.ingestPipelines.form.showRequestButtonLabel": "リクエストを表示", "xpack.ingestPipelines.form.unknownError": "不明なエラーが発生しました。", @@ -15118,6 +15311,7 @@ "xpack.ingestPipelines.list.table.createPipelineFromCsvButtonLabel": "CSVからのパイプライン", "xpack.ingestPipelines.list.table.deleteActionDescription": "このパイプラインを削除", "xpack.ingestPipelines.list.table.deleteActionLabel": "削除", + "xpack.ingestPipelines.list.table.deletePipelinesButtonLabel": "{count, plural, other {パイプライン} }を削除", "xpack.ingestPipelines.list.table.editActionDescription": "このパイプラインを編集", "xpack.ingestPipelines.list.table.editActionLabel": "編集", "xpack.ingestPipelines.list.table.emptyCreatePipelineDropdownLabel": "パイプラインの作成", @@ -15129,6 +15323,7 @@ "xpack.ingestPipelines.list.table.nameColumnTitle": "名前", "xpack.ingestPipelines.list.table.reloadButtonLabel": "再読み込み", "xpack.ingestPipelines.mapToIngestPipeline.error.invalidFormatAction": "無効なフォーマットアクション[{ formatAction }です。有効なアクションは{formatActions}です", + "xpack.ingestPipelines.mapToIngestPipeline.error.missingHeaders": "見つからない必須のヘッダー:{missingHeaders} {missingHeadersCount, plural, other {ヘッダー}}をCSVファイルに含めます。", "xpack.ingestPipelines.mapToIngestPipeline.error.parseErrors": "ファイル読み取りエラー:ファイルの処理中に予期しない問題が発生しました。", "xpack.ingestPipelines.pipelineEditor.addDocuments.addDocumentButtonLabel": "ドキュメントを追加", "xpack.ingestPipelines.pipelineEditor.addDocuments.addDocumentErrorMessage": "ドキュメントの追加エラー", @@ -15810,17 +16005,20 @@ "xpack.logstash.pipelineEditor.queueMaxBytesFormRowLabel": "キューの最大バイト数", "xpack.logstash.pipelineEditor.queueTypeFormRowLabel": "キュータイプ", "xpack.logstash.pipelineIdRequiredMessage": "パイプライン ID が必要です", + "xpack.logstash.pipelineList.couldNotDeletePipelinesNotification": "{numErrors, plural, other {# パイプライン}}の削除に失敗しました", "xpack.logstash.pipelineList.head": "パイプライン", "xpack.logstash.pipelineList.noPermissionToManageDescription": "管理者にお問い合わせください。", "xpack.logstash.pipelineList.noPermissionToManageTitle": "Logstash パイプラインを変更するパーミッションがありません。", "xpack.logstash.pipelineList.noPipelinesDescription": "パイプラインが定義されていません。", "xpack.logstash.pipelineList.noPipelinesTitle": "パイプラインがありません", "xpack.logstash.pipelineList.pipelineLoadingErrorNotification": "パイプラインを読み込めませんでした。エラー:「{errStatusText}」。", + "xpack.logstash.pipelineList.pipelinesCouldNotBeDeletedDescription": "しかし {numErrors, plural, other {# パイプライン}}を削除できませんでした。", "xpack.logstash.pipelineList.pipelinesLoadingErrorDescription": "パイプラインの読み込み中にエラーが発生しました。", "xpack.logstash.pipelineList.pipelinesLoadingErrorTitle": "エラー", "xpack.logstash.pipelineList.pipelinesLoadingMessage": "パイプラインを読み込み中…", "xpack.logstash.pipelineList.pipelinesSuccessfullyDeletedNotification": "「{id}」が削除されました", "xpack.logstash.pipelineList.subhead": "Logstash イベントの処理を管理して結果を表示", + "xpack.logstash.pipelineList.successfullyDeletedPipelinesNotification": "{numPipelinesSelected, plural, other {# 本中}}{numSuccesses} 本のパイプラインが削除されました", "xpack.logstash.pipelineNotCentrallyManagedTooltip": "このパイプラインは集中構成管理で作成されませんでした。ここで管理または編集できません。", "xpack.logstash.pipelines.createBreadcrumb": "作成", "xpack.logstash.pipelines.listBreadcrumb": "パイプライン", @@ -16015,6 +16213,7 @@ "xpack.maps.layerPanel.metricsExpression.helpText": "右のソースのメトリックを構成します。これらの値はレイヤー機能に追加されます。", "xpack.maps.layerPanel.metricsExpression.joinMustBeSetErrorMessage": "JOIN の設定が必要です", "xpack.maps.layerPanel.metricsExpression.metricsPopoverTitle": "メトリック", + "xpack.maps.layerPanel.metricsExpression.useMetricsDescription": "{metricsLength, plural, other {してメトリックを使用します}}", "xpack.maps.layerPanel.settingsPanel.fittableFlagLabel": "データ境界への適合計算にレイヤーを含める", "xpack.maps.layerPanel.settingsPanel.fittableFlagTooltip": "データ境界に合わせると、マップ範囲が調整され、すべてのデータが表示されます。レイヤーは参照データを提供する場合があります。データ境界への適合計算には含めないでください。このオプションを使用すると、データ境界への適合計算からレイヤーを除外します。", "xpack.maps.layerPanel.settingsPanel.labelsOnTop": "上部にラベルを表示", @@ -16510,6 +16709,8 @@ "xpack.ml.advancedSettings.enableAnomalyDetectionDefaultTimeRangeDesc": "シングルメトリックビューアーと異常エクスプローラーでデフォルト時間フィルターを使用します。有効ではない場合、ジョブの全時間範囲の結果が表示されます。", "xpack.ml.advancedSettings.enableAnomalyDetectionDefaultTimeRangeName": "異常検知結果の時間フィルターデフォルトを有効にする", "xpack.ml.alertConditionValidation.alertIntervalTooHighMessage": "チェック間隔がルックバック間隔を超えています。通知を見逃す可能性を回避するには、{lookbackInterval}に減らします。", + "xpack.ml.alertConditionValidation.notifyWhenWarning": "最大{notificationDuration, plural, other {# 分間}}は同じ異常に関する重複した通知を受信することが想定されます。重複した通知を回避するには、チェック間隔を大きくするか、ステータス変更時のみに通知するように切り替えます。", + "xpack.ml.alertConditionValidation.stoppedDatafeedJobsMessage": "次の{count, plural, other {ジョブ}} {jobIds}のデータフィードが開始していません。", "xpack.ml.alertConditionValidation.title": "アラート条件には次の問題が含まれます。", "xpack.ml.alertContext.anomalyExplorerUrlDescription": "異常エクスプローラーを開くURL", "xpack.ml.alertContext.isInterimDescription": "上位の一致に中間結果が含まれるかどうかを示します", @@ -16535,17 +16736,22 @@ "xpack.ml.alertTypes.jobsHealthAlertingRule.betaBadgeTooltipContent": "異常検知ジョブ正常性アラートはベータ版の機能です。フィードバックをお待ちしています。", "xpack.ml.alertTypes.jobsHealthAlertingRule.datafeedCheckDescription": "ジョブの対応するデータフィードが開始していない場合にアラートで通知します", "xpack.ml.alertTypes.jobsHealthAlertingRule.datafeedCheckName": "データフィードが開始していません", + "xpack.ml.alertTypes.jobsHealthAlertingRule.datafeedStateMessage": "{count, plural, other {ジョブ}} {jobsString}のデータフィードが開始していません", "xpack.ml.alertTypes.jobsHealthAlertingRule.defaultActionMessage": "[\\{\\{rule.name\\}\\}] 異常検知ジョブヘルスチェック結果:\n\\{\\{context.message\\}\\}\n\\{\\{#context.results\\}\\}\n ジョブID:\\{\\{job_id\\}\\}\n \\{\\{#datafeed_id\\}\\}データフィードID: \\{\\{datafeed_id\\}\\}\n \\{\\{/datafeed_id\\}\\}\\{\\{#datafeed_state\\}\\}データフィード状態:\\{\\{datafeed_state\\}\\}\n \\{\\{/datafeed_state\\}\\}\\{\\{#memory_status\\}\\}メモリステータス:\\{\\{memory_status\\}\\}\n \\{\\{/memory_status\\}\\}\\{\\{#model_bytes\\}\\}モデルサイズ:\\{\\{model_bytes\\}\\}\n \\{\\{/model_bytes\\}\\}\\{\\{#model_bytes_memory_limit\\}\\}モデルメモリ上限:\\{\\{model_bytes_memory_limit\\}\\}\n \\{\\{/model_bytes_memory_limit\\}\\}\\{\\{#peak_model_bytes\\}\\}ピークモデルバイト:\\{\\{peak_model_bytes\\}\\}\n \\{\\{/peak_model_bytes\\}\\}\\{\\{#model_bytes_exceeded\\}\\}モデルが超過しました:\\{\\{model_bytes_exceeded\\}\\}\n \\{\\{/model_bytes_exceeded\\}\\}\\{\\{#log_time\\}\\}メモリログ時間:\\{\\{log_time\\}\\}\n \\{\\{/log_time\\}\\}\\{\\{#failed_category_count\\}\\}失敗したカテゴリ件数:\\{\\{failed_category_count\\}\\}\n \\{\\{/failed_category_count\\}\\}\\{\\{#annotation\\}\\}注釈: \\{\\{annotation\\}\\}\n \\{\\{/annotation\\}\\}\\{\\{#missed_docs_count\\}\\}見つからないドキュメント数:\\{\\{missed_docs_count\\}\\}\n \\{\\{/missed_docs_count\\}\\}\\{\\{#end_timestamp\\}\\}見つからないドキュメントで最後に確定されたバケット:\\{\\{end_timestamp\\}\\}\n \\{\\{/end_timestamp\\}\\}\\{\\{#errors\\}\\}エラーメッセージ:\\{\\{message\\}\\} \\{\\{/errors\\}\\}\n\\{\\{/context.results\\}\\}\n", "xpack.ml.alertTypes.jobsHealthAlertingRule.delayedDataCheckDescription": "データ遅延のためにジョブのデータがない場合にアラートで通知します。", "xpack.ml.alertTypes.jobsHealthAlertingRule.delayedDataCheckName": "データ遅延が発生しました", + "xpack.ml.alertTypes.jobsHealthAlertingRule.delayedDataMessage": "{count, plural, other {ジョブ}} {jobsString} {count, plural, other {では}}データの遅延が発生しています。", "xpack.ml.alertTypes.jobsHealthAlertingRule.description": "異常検知ジョブで運用の問題が発生しているときにアラートで通知します。きわめて重要なジョブの適切なアラートを有効にします。", "xpack.ml.alertTypes.jobsHealthAlertingRule.errorMessagesCheckDescription": "ジョブのジョブメッセージにエラーが含まれている場合にアラートで通知します。", "xpack.ml.alertTypes.jobsHealthAlertingRule.errorMessagesCheckName": "ジョブメッセージのエラー", + "xpack.ml.alertTypes.jobsHealthAlertingRule.errorMessagesMessage": "{count, plural, other {ジョブ}} {jobsString} {count, plural, other {には}}メッセージのエラーがあります。", "xpack.ml.alertTypes.jobsHealthAlertingRule.excludeJobs.label": "ジョブまたはグループを除外", "xpack.ml.alertTypes.jobsHealthAlertingRule.includeJobs.errorMessage": "ジョブ選択は必須です", "xpack.ml.alertTypes.jobsHealthAlertingRule.includeJobs.label": "ジョブまたはグループを含める", "xpack.ml.alertTypes.jobsHealthAlertingRule.mmlCheckDescription": "ジョブがソフトまたはハードモデルメモリ上限に達したときにアラートで通知します。", "xpack.ml.alertTypes.jobsHealthAlertingRule.mmlCheckName": "モデルメモリ上限に達しました", + "xpack.ml.alertTypes.jobsHealthAlertingRule.mmlMessage": "{count, plural, other {ジョブ}} {jobsString}がハードモデルメモリ上限に達しました。ハード上限に達する前に、ジョブのメモリ割当量を増やすか、スナップショットから復元してください。", + "xpack.ml.alertTypes.jobsHealthAlertingRule.mmlSoftLimitMessage": "{count, plural, other {ジョブ}} {jobsString}がソフトモデルメモリ上限に達しました。ジョブのメモリ割当量を増やすか、データフィードフィルターを編集して分析の範囲を絞り込んでください。", "xpack.ml.alertTypes.jobsHealthAlertingRule.testsConfig.delayedData.docsCountErrorMessage": "無効なドキュメント数", "xpack.ml.alertTypes.jobsHealthAlertingRule.testsConfig.delayedData.timeIntervalErrorMessage": "無効な時間間隔", "xpack.ml.alertTypes.jobsHealthAlertingRule.testsConfig.errorMessage": "1つ以上のヘルスチェックを有効にする必要があります。", @@ -16745,6 +16951,7 @@ "xpack.ml.calendarsList.table.allJobsLabel": "すべてのジョブに適用", "xpack.ml.calendarsList.table.deleteButtonLabel": "削除", "xpack.ml.calendarsList.table.eventsColumnName": "イベント", + "xpack.ml.calendarsList.table.eventsCountLabel": "{eventsLength, plural, other {# イベント}}", "xpack.ml.calendarsList.table.idColumnName": "ID", "xpack.ml.calendarsList.table.jobsColumnName": "ジョブ", "xpack.ml.calendarsList.table.newButtonLabel": "新規", @@ -16816,6 +17023,7 @@ "xpack.ml.dataframe.analytics.classificationExploration.evaluateSectionRocTitle": "受信者操作特性(ROC)曲線", "xpack.ml.dataframe.analytics.classificationExploration.evaluateSectionTitle": "モデル評価", "xpack.ml.dataframe.analytics.classificationExploration.evaluationQualityMetricsHelpText": "評価品質メトリック", + "xpack.ml.dataframe.analytics.classificationExploration.generalizationDocsCount": "{docsCount, plural, other {#個のドキュメント}}が評価されました", "xpack.ml.dataframe.analytics.classificationExploration.recallAndAccuracyAccuracyColumn": "精度", "xpack.ml.dataframe.analytics.classificationExploration.recallAndAccuracyClassColumn": "クラス", "xpack.ml.dataframe.analytics.classificationExploration.recallAndAccuracyRecallColumn": "再現率", @@ -16954,6 +17162,7 @@ "xpack.ml.dataframe.analytics.create.gammaText": "損失計算のツリーサイズの乗数。非負の値でなければなりません。", "xpack.ml.dataframe.analytics.create.hyperParametersDetailsTitle": "ハイパーパラメータ", "xpack.ml.dataframe.analytics.create.hyperParametersSectionTitle": "ハイパーパラメータ", + "xpack.ml.dataframe.analytics.create.includedFieldsCount": "{numFields, plural, other {# 個のフィールド}}が分析に含まれます", "xpack.ml.dataframe.analytics.create.includedFieldsLabel": "含まれるフィールド", "xpack.ml.dataframe.analytics.create.isIncludedOption": "含まれる", "xpack.ml.dataframe.analytics.create.isNotIncludedOption": "含まれない", @@ -16962,6 +17171,7 @@ "xpack.ml.dataframe.analytics.create.jobIdExistsError": "このIDの分析ジョブがすでに存在します。", "xpack.ml.dataframe.analytics.create.jobIdInputAriaLabel": "固有の分析ジョブIDを選択してください。", "xpack.ml.dataframe.analytics.create.jobIdInvalidError": "小文字のアルファベットと数字(a-zと0-9)、ハイフンまたはアンダーラインのみ使用でき、最初と最後を英数字にする必要があります。", + "xpack.ml.dataframe.analytics.create.jobIdInvalidMaxLengthErrorMessage": "ジョブ ID は {maxLength, plural, other {# 文字}}以下でなければなりません。", "xpack.ml.dataframe.analytics.create.jobIdLabel": "ジョブID", "xpack.ml.dataframe.analytics.create.jobIdPlaceholder": "ジョブID", "xpack.ml.dataframe.analytics.create.jsonEditorDisabledSwitchText": "構成には、フォームでサポートされていない高度なフィールドが含まれます。フォームに切り替えることができません。", @@ -17129,6 +17339,7 @@ "xpack.ml.dataframe.analytics.regressionExploration.evaluateNoTestingDocsError": "テストドキュメントが見つかりません", "xpack.ml.dataframe.analytics.regressionExploration.evaluateNoTrainingDocsError": "トレーニングドキュメントが見つかりません", "xpack.ml.dataframe.analytics.regressionExploration.evaluateSectionTitle": "モデル評価", + "xpack.ml.dataframe.analytics.regressionExploration.generalizationDocsCount": "{docsCount, plural, other {# 件のドキュメント}}が評価されました", "xpack.ml.dataframe.analytics.regressionExploration.generalizationErrorTitle": "一般化エラー", "xpack.ml.dataframe.analytics.regressionExploration.generalizationFilterText": ".学習データをフィルタリングしています。", "xpack.ml.dataframe.analytics.regressionExploration.huberLinkText": "Pseudo Huber損失関数", @@ -17141,6 +17352,7 @@ "xpack.ml.dataframe.analytics.regressionExploration.rSquaredText": "R の二乗", "xpack.ml.dataframe.analytics.regressionExploration.rSquaredTooltipContent": "適合度を表します。モデルによる観察された結果の複製の効果を測定します。", "xpack.ml.dataframe.analytics.regressionExploration.tableJobIdTitle": "回帰ジョブID {jobId}のデスティネーションインデックス", + "xpack.ml.dataframe.analytics.regressionExploration.trainingDocsCount": "{docsCount, plural, other {# 件のドキュメント}}が評価されました", "xpack.ml.dataframe.analytics.regressionExploration.trainingErrorTitle": "トレーニングエラー", "xpack.ml.dataframe.analytics.regressionExploration.trainingFilterText": ".テストデータをフィルタリングしています。", "xpack.ml.dataframe.analytics.results.dataViewMissingErrorMessage": "このページを表示するには、この分析ジョブのターゲットまたはソースインデックスのKibanaデータビューが必要です。", @@ -17286,6 +17498,7 @@ "xpack.ml.dataGrid.invalidSortingColumnError": "列「{columnId}」は並べ替えに使用できません。", "xpack.ml.dataGridChart.histogramNotAvailable": "グラフはサポートされていません。", "xpack.ml.dataGridChart.notEnoughData": "0個のドキュメントにフィールドが含まれます。", + "xpack.ml.dataGridChart.singleCategoryLegend": "{cardinality, plural, other {# カテゴリ}}", "xpack.ml.dataGridChart.topCategoriesLegend": "上位 {maxChartColumns}/{cardinality} カテゴリ", "xpack.ml.dataViewNotBasedOnTimeSeriesNotificationDescription": "異常検知は時間ベースのインデックスでのみ実行されます", "xpack.ml.dataViewNotBasedOnTimeSeriesNotificationTitle": "データビュー{dataViewName}は時系列に基づいていません", @@ -17319,6 +17532,7 @@ "xpack.ml.deepLink.overview": "概要", "xpack.ml.deepLink.settings": "設定", "xpack.ml.deepLink.trainedModels": "学習済みモデル", + "xpack.ml.deleteJobCheckModal.buttonTextCanDelete": "続行して、{length, plural, other {# 個のジョブ}}を削除します", "xpack.ml.deleteJobCheckModal.buttonTextCanUnTagConfirm": "現在のスペースから削除", "xpack.ml.deleteJobCheckModal.buttonTextClose": "閉じる", "xpack.ml.deleteJobCheckModal.buttonTextNoAction": "閉じる", @@ -17390,6 +17604,7 @@ "xpack.ml.explorer.distributionChart.anomalyScoreLabel": "異常スコア", "xpack.ml.explorer.distributionChart.entityLabel": "エンティティ", "xpack.ml.explorer.distributionChart.typicalLabel": "通常", + "xpack.ml.explorer.distributionChart.unusualByFieldValuesLabel": "{ numberOfCauses, plural, other {#{plusSign} 異常な {byFieldName} 値}}", "xpack.ml.explorer.distributionChart.valueLabel": "値", "xpack.ml.explorer.distributionChart.valueWithoutAnomalyScoreLabel": "値", "xpack.ml.explorer.intervalLabel": "間隔", @@ -17407,6 +17622,7 @@ "xpack.ml.explorer.noInfluencersFoundTitleFilterMessage": "指定されたフィルターの{viewBySwimlaneFieldName} 影響因子が見つかりません", "xpack.ml.explorer.noJobSelectedLabel": "ジョブが選択されていません", "xpack.ml.explorer.noMatchingAnomaliesFoundTitle": "一致する注釈が見つかりません", + "xpack.ml.explorer.noResultForSelectedJobsMessage": "選択した{jobsCount, plural, other {個のジョブ}}で結果が見つかりません", "xpack.ml.explorer.noResultsFoundLabel": "結果が見つかりませんでした", "xpack.ml.explorer.overallLabel": "全体", "xpack.ml.explorer.overallSwimlaneUnfilteredLabel": "{label}(フィルタリングなし)", @@ -17422,6 +17638,7 @@ "xpack.ml.explorer.singleMetricChart.valueWithoutAnomalyScoreLabel": "値", "xpack.ml.explorer.sortedByMaxAnomalyScoreForTimeFormattedLabel": "({viewByLoadedForTimeFormatted} の最高異常スコアで分類)", "xpack.ml.explorer.sortedByMaxAnomalyScoreLabel": "(最高異常スコアで分類)", + "xpack.ml.explorer.stoppedPartitionsExistCallout": "stop_on_warnがオンであるため、想定される結果よりも少ない可能性があります。分類ステータスが警告に変更された{jobsWithStoppedPartitions, plural, other {ジョブ}} [{stoppedPartitions}]の一部のパーティションでは、分類と後続の異常検知の両方が停止しました。", "xpack.ml.explorer.swimlane.maxAnomalyScoreLabel": "最高異常スコア", "xpack.ml.explorer.swimlaneActions": "アクション", "xpack.ml.explorer.swimlaneAnnotationLabel": "注釈", @@ -17480,6 +17697,11 @@ "xpack.ml.importExport.exportFlyout.exportDownloading": "ファイルはバックグラウンドでダウンロード中です", "xpack.ml.importExport.exportFlyout.exportError": "選択したジョブをエクスポートできませんでした", "xpack.ml.importExport.exportFlyout.exportJobDependenciesWarningCallout.calendarDependencies": "ジョブをエクスポートするときには、カレンダーおよびフィルターリストは含まれません。ジョブをインポートする前にフィルターリストを作成する必要があります。そうでないと、インポートが失敗します。新しいジョブを続行してスケジュールされたイベントを無視する場合は、カレンダーを作成する必要があります。", + "xpack.ml.importExport.exportFlyout.exportJobDependenciesWarningCallout.calendarList": "{num, plural, other {個のカレンダー}}:{calendars}", + "xpack.ml.importExport.exportFlyout.exportJobDependenciesWarningCallout.calendarOnlyTitle": "{jobCount, plural, other {# 件のジョブが}} {calendarCount, plural, other {個のカレンダー}}を使用します", + "xpack.ml.importExport.exportFlyout.exportJobDependenciesWarningCallout.filterAndCalendarTitle": "{jobCount, plural, other {# 件のジョブが}}フィルターリストとカレンダーを使用します", + "xpack.ml.importExport.exportFlyout.exportJobDependenciesWarningCallout.filterList": "{num, plural, other {リスト}}をフィルター:{filters}", + "xpack.ml.importExport.exportFlyout.exportJobDependenciesWarningCallout.filterOnlyTitle": "{jobCount, plural, other {# 件のジョブが}} {filterCount, plural, other {個のフィルターリスト}}を使用します", "xpack.ml.importExport.exportFlyout.exportJobDependenciesWarningCallout.jobUsingCalendarsAria": "カレンダーを使用したジョブ", "xpack.ml.importExport.exportFlyout.exportJobDependenciesWarningCallout.jobUsingCalendarsButton": "カレンダーを使用したジョブ", "xpack.ml.importExport.exportFlyout.exportJobDependenciesWarningCallout.jobUsingFiltersAria": "フィルターリストを使用したジョブ", @@ -17492,6 +17714,9 @@ "xpack.ml.importExport.importButton": "ジョブのインポート", "xpack.ml.importExport.importFlyout.cannotImportJobCallout.jobListAria": "ジョブを表示", "xpack.ml.importExport.importFlyout.cannotImportJobCallout.jobListButton": "ジョブを表示", + "xpack.ml.importExport.importFlyout.cannotImportJobCallout.missingFilters": "フィルター{num, plural, other {リスト}}がありません:{filters}", + "xpack.ml.importExport.importFlyout.cannotImportJobCallout.missingIndex": "インデックス{num, plural, other {パターン}}がありません:{indices}", + "xpack.ml.importExport.importFlyout.cannotImportJobCallout.title": "{num, plural, other {# 件のジョブ}}をインポートできません", "xpack.ml.importExport.importFlyout.cannotReadFileCallout.body": "ジョブのエクスポートオプションを使用してKibanaからエクスポートされた機械学習ジョブを含むファイルを選択してください。", "xpack.ml.importExport.importFlyout.cannotReadFileCallout.title": "ファイルを読み取れません", "xpack.ml.importExport.importFlyout.closeButton": "閉じる", @@ -17500,10 +17725,16 @@ "xpack.ml.importExport.importFlyout.destIndex": "デスティネーションインデックス", "xpack.ml.importExport.importFlyout.fileSelect": "ファイルを選択するかドラッグ &amp; ドロップしてください", "xpack.ml.importExport.importFlyout.flyoutHeader": "ジョブのインポート", + "xpack.ml.importExport.importFlyout.importableFiles": "{num, plural, other {# 件のジョブ}}をインポート", + "xpack.ml.importExport.importFlyout.importJobErrorToast": "{count, plural, other {# 件のジョブ}}を正常にインポートできませんでした", + "xpack.ml.importExport.importFlyout.importJobSuccessToast": "{count, plural, other {# 件のジョブ}}が正常にインポートされました", "xpack.ml.importExport.importFlyout.jobId": "ジョブID", + "xpack.ml.importExport.importFlyout.selectedFiles.ad": "{num}件の異常検知{num, plural, other {ジョブ}}がファイルから読み取ります", + "xpack.ml.importExport.importFlyout.selectedFiles.dfa": "{num}件のデータフレーム分析{num, plural, other {ジョブ}}がファイルから読み取ります", "xpack.ml.importExport.importFlyout.validateDestIndex.destIndexEmpty": "有効なデスティネーションインデックス名を入力", "xpack.ml.importExport.importFlyout.validateDestIndex.destIndexExists": "この名前のインデックスがすでに存在します。この分析ジョブを実行すると、デスティネーションインデックスが変更されます。", "xpack.ml.importExport.importFlyout.validateDestIndex.destIndexInvalid": "無効なデスティネーションインデックス名。", + "xpack.ml.importExport.importFlyout.validateJobId.jobIdInvalidMaxLengthErrorMessage": "ジョブ ID は {maxLength, plural, other {# 文字}}以下でなければなりません。", "xpack.ml.importExport.importFlyout.validateJobId.jobNameAllowedCharacters": "ジョブ名にはアルファベットの小文字(a-z と 0-9)、ハイフンまたはアンダーラインが使用でき、最初と最後を英数字にする必要があります", "xpack.ml.importExport.importFlyout.validateJobId.jobNameAlreadyExists": "ジョブ ID がすでに存在します。ジョブ ID は既存のジョブやグループと同じにできません。", "xpack.ml.importExport.importFlyout.validateJobId.jobNameEmpty": "有効なジョブIDを入力", @@ -17539,11 +17770,13 @@ "xpack.ml.jobMessages.timeLabel": "時間", "xpack.ml.jobMessages.toggleInChartAriaLabel": "グラフで切り替え", "xpack.ml.jobMessages.toggleInChartTooltipText": "グラフで切り替え", + "xpack.ml.jobsAwaitingNodeWarning.noMLNodesAvailableDescription": "{jobCount, plural, other {}} {jobCount, plural, other {# 個のジョブ}}が機械学習ノードの起動を待機しています。", "xpack.ml.jobsAwaitingNodeWarning.title": "機械学習ノードの待機中", "xpack.ml.jobsAwaitingNodeWarningShared.isCloud": "Elastic Cloudデプロイは自動拡張し、機械学習能力を追加することができます。これには5~20分かかる場合があります。", "xpack.ml.jobsAwaitingNodeWarningShared.isCloud.link": "{link}で進行状況を監視できます。", "xpack.ml.jobsAwaitingNodeWarningShared.linkToCloud.learnMore": "詳細情報", "xpack.ml.jobsAwaitingNodeWarningShared.linkToCloud.linkText": "Elastic Cloud管理コンソール", + "xpack.ml.jobsAwaitingNodeWarningShared.noMLNodesAvailableDescription": "{jobCount, plural, other {}} {jobCount, plural, other {# 個のジョブ}}が機械学習ノードの起動を待機しています。", "xpack.ml.jobsAwaitingNodeWarningShared.notCloud": "Elastic Cloudデプロイのみが自動拡張できます。機械学習ノードは追加する必要があります。{link}", "xpack.ml.jobsAwaitingNodeWarningShared.title": "機械学習ノードの待機中", "xpack.ml.jobsBreadcrumbs.advancedConfigurationLabel": "高度な構成", @@ -17556,6 +17789,7 @@ "xpack.ml.jobsBreadcrumbs.selectIndexOrSearchLabelRecognize": "認識されたインデックス", "xpack.ml.jobsBreadcrumbs.selectJobType": "ジョブを作成", "xpack.ml.jobsBreadcrumbs.singleMetricLabel": "シングルメトリック", + "xpack.ml.jobSelect.requestedJobsDoesNotExistWarningMessage": "リクエストされました\n{invalidIdsLength, plural, other {ジョブ {invalidIds} は存在しません}}", "xpack.ml.jobSelectList.groupTimeRangeLabel": "{fromString} ~ {toString}", "xpack.ml.jobSelector.applyFlyoutButton": "適用", "xpack.ml.jobSelector.applyTimerangeSwitchLabel": "時間範囲を適用", @@ -17566,6 +17800,7 @@ "xpack.ml.jobSelector.customTable.selectAllCheckboxLabel": "すべて選択", "xpack.ml.jobSelector.filterBar.groupLabel": "グループ", "xpack.ml.jobSelector.filterBar.invalidSearchErrorMessage": "無効な検索:{errorMessage}", + "xpack.ml.jobSelector.filterBar.jobGroupTitle": "({jobsCount, plural, other {# 件のジョブ}})", "xpack.ml.jobSelector.flyoutTitle": "ジョブの選択", "xpack.ml.jobSelector.formControlLabel": "ジョブを選択", "xpack.ml.jobSelector.groupOptionsLabel": "グループ", @@ -17582,6 +17817,7 @@ "xpack.ml.jobSelector.noResultsForJobLabel": "成果がありません", "xpack.ml.jobSelector.selectAllGroupLabel": "すべて選択", "xpack.ml.jobSelector.selectAllOptionLabel": "*", + "xpack.ml.jobSelector.selectedGroupJobs": "({jobsCount, plural, other {# 件のジョブ}})", "xpack.ml.jobSelector.showBarBadges": "その他 {overFlow} 件", "xpack.ml.jobSelector.showFlyoutBadges": "その他 {overFlow} 件", "xpack.ml.jobService.activeDatafeedsLabel": "アクティブなデータフィード", @@ -17598,6 +17834,7 @@ "xpack.ml.jobsList.actionFailedNotificationMessage": "{failureId} が {actionText} に失敗しました", "xpack.ml.jobsList.actionsLabel": "アクション", "xpack.ml.jobsList.alertingRules.screenReaderDescription": "ジョブに関連付けられたアラートルールがあるときに、この列にアイコンが表示されます", + "xpack.ml.jobsList.alertingRules.tooltipContent": "ジョブ{rulesCount}はアラート{rulesCount, plural, other { ルール}}に関連付けられています", "xpack.ml.jobsList.analyticsSpacesLabel": "スペース", "xpack.ml.jobsList.auditMessageColumn.screenReaderDescription": "この列は、過去24時間にエラーまたは警告があった場合にアイコンを表示します", "xpack.ml.jobsList.breadcrumb": "ジョブ", @@ -17648,6 +17885,7 @@ "xpack.ml.jobsList.deleteJobModal.deleteAction": "削除中", "xpack.ml.jobsList.deleteJobModal.deleteButtonLabel": "削除", "xpack.ml.jobsList.deleteJobModal.deleteJobsTitle": "{jobsCount, plural, one {{jobId}} other {# 件のジョブ}}を削除しますか?", + "xpack.ml.jobsList.deleteJobModal.deleteMultipleJobsDescription": "{jobsCount, plural, one {ジョブ} other {複数ジョブ}}の削除には時間がかかる場合があります。{jobsCount, plural, other {}}バックグラウンドで削除され、ジョブリストからすぐに消えない場合があります。", "xpack.ml.jobsList.deleteJobModal.deletingJobsStatusLabel": "ジョブを削除中", "xpack.ml.jobsList.descriptionLabel": "説明", "xpack.ml.jobsList.editJobFlyout.changesNotSavedNotificationMessage": "{jobId} への変更を保存できませんでした", @@ -17739,6 +17977,7 @@ "xpack.ml.jobsList.jobFilterBar.failedLabel": "失敗", "xpack.ml.jobsList.jobFilterBar.groupLabel": "グループ", "xpack.ml.jobsList.jobFilterBar.invalidSearchErrorMessage": "無効な検索:{errorMessage}", + "xpack.ml.jobsList.jobFilterBar.jobGroupTitle": "({jobsCount, plural, other {# 件のジョブ}})", "xpack.ml.jobsList.jobFilterBar.openedLabel": "オープン", "xpack.ml.jobsList.jobFilterBar.startedLabel": "開始", "xpack.ml.jobsList.jobFilterBar.stoppedLabel": "停止", @@ -17772,11 +18011,18 @@ "xpack.ml.jobsList.multiJobActions.groupSelector.addButtonAriaLabel": "追加", "xpack.ml.jobsList.multiJobActions.groupSelector.addNewGroupPlaceholder": "新規グループを追加", "xpack.ml.jobsList.multiJobActions.groupSelector.applyButtonLabel": "適用", + "xpack.ml.jobsList.multiJobActions.groupSelector.applyGroupsToJobTitle": "{jobsCount, plural, other {件のジョブ}}にグループを適用", "xpack.ml.jobsList.multiJobActions.groupSelector.editJobGroupsButtonAriaLabel": "ジョブグループを編集します", "xpack.ml.jobsList.multiJobActions.groupSelector.editJobGroupsButtonTooltip": "ジョブグループを編集します", "xpack.ml.jobsList.multiJobActions.groupSelector.groupsAndJobsCanNotUseSameIdErrorMessage": "この ID のジョブがすでに存在します。グループとジョブは同じ ID を共有できません。", "xpack.ml.jobsList.multiJobActionsMenu.managementActionsAriaLabel": "管理アクション", + "xpack.ml.jobsList.multiJobsActions.closeJobsLabel": "{jobsCount, plural, other {件のジョブ}}を閉じる", "xpack.ml.jobsList.multiJobsActions.createAlertsLabel": "アラートルールを作成", + "xpack.ml.jobsList.multiJobsActions.deleteJobsLabel": "{jobsCount, plural, other {件のジョブ}}を削除", + "xpack.ml.jobsList.multiJobsActions.jobsSelectedLabel": "{selectedJobsCount, plural, other {# 件のジョブ}} を選択済み", + "xpack.ml.jobsList.multiJobsActions.resetJobsLabel": "{jobsCount, plural, other {件のジョブ}}をリセット", + "xpack.ml.jobsList.multiJobsActions.startDatafeedsLabel": "{jobsCount, plural, other {データフィード}}を開始", + "xpack.ml.jobsList.multiJobsActions.stopDatafeedsLabel": "{jobsCount, plural, other {データフィード}}を停止", "xpack.ml.jobsList.nodeAvailableWarning.linkToCloud.hereLinkText": "Elastic Cloud 展開", "xpack.ml.jobsList.nodeAvailableWarning.linkToCloudDescription": "{link}を編集してください。1GBの空き機械学習ノードを有効にするか、既存のML構成を拡張することができます。", "xpack.ml.jobsList.nodeAvailableWarning.noMLNodesAvailableDescription": "利用可能な ML ノードがありません。", @@ -17790,8 +18036,10 @@ "xpack.ml.jobsList.resetJobModal.cancelButtonLabel": "キャンセル", "xpack.ml.jobsList.resetJobModal.openJobsWarningCallout.description1": "{openJobsCount, plural, one {このジョブ} other {これらのジョブ}}を終了した後に、{openJobsCount, plural, one {それを} other {それらを}}リセットできます。", "xpack.ml.jobsList.resetJobModal.openJobsWarningCallout.description2": "以下の[リセット]ボタンをクリックしても、{openJobsCount, plural, one {このジョブ} other {これらのジョブ}}はリセットされません。", + "xpack.ml.jobsList.resetJobModal.openJobsWarningCallout.title": "{openJobsCount, plural, other {# 件のジョブ}}が終了しません", "xpack.ml.jobsList.resetJobModal.resetButtonLabel": "リセット", "xpack.ml.jobsList.resetJobModal.resetJobsTitle": "{jobsCount, plural, one {{jobId}} other {# 件のジョブ}}をリセット", + "xpack.ml.jobsList.resetJobModal.resetMultipleJobsDescription": "{jobsCount, plural, one {ジョブ} other {複数ジョブ}}のリセットには時間がかかる場合があります。{jobsCount, plural, other {}}バックグラウンドでリセットされ、ジョブリストではすぐに更新されない場合があります。", "xpack.ml.jobsList.resultActions.openJobsInAnomalyExplorerText": "{jobsCount, plural, one {{jobId}} other {# 件のジョブ}} を異常エクスプローラーで開く", "xpack.ml.jobsList.resultActions.openJobsInSingleMetricViewerText": "シングルメトリックビューアーで {jobsCount, plural, one {{jobId}} other {# 件のジョブ}} を開く", "xpack.ml.jobsList.resultActions.singleMetricDisabledMessageText": "{reason}のため無効です。", @@ -17815,6 +18063,8 @@ "xpack.ml.jobsList.startDatafeedModal.startFromNowLabel": "今から開始", "xpack.ml.jobsList.startDatafeedModal.startJobsTitle": "{jobsCount, plural, one {{jobId}} other {# 件のジョブ}}を開始", "xpack.ml.jobsList.startDatafeedsConfirmModal.cancelButtonLabel": "キャンセル", + "xpack.ml.jobsList.startDatafeedsConfirmModal.closeButtonLabel": "{jobsCount, plural, other {件のジョブ}}を閉じる", + "xpack.ml.jobsList.startDatafeedsModal.closeDatafeedsTitle": "{jobsCount, plural, one {{jobId}} other {#件のジョブ}}を閉じますか?", "xpack.ml.jobsList.startDatafeedsModal.resetManagedDatafeedsDescription": "セットしています", "xpack.ml.jobsList.startDatafeedsModal.startManagedDatafeedsDescription": "{jobsCount, plural, one {このジョブ} other {これらのジョブの1つ以上}}はElasticによってあらかじめ構成されています。終了日を指定して{jobsCount, plural, one {それを} other {それらを}}開始すると、製品の他の部分に影響する可能性があります。", "xpack.ml.jobsList.startedActionStatusText": "開始済み", @@ -17827,6 +18077,7 @@ "xpack.ml.jobsList.statsBar.totalJobsLabel": "合計ジョブ数", "xpack.ml.jobsList.stopActionStatusText": "停止", "xpack.ml.jobsList.stopDatafeedsConfirmModal.cancelButtonLabel": "キャンセル", + "xpack.ml.jobsList.stopDatafeedsConfirmModal.stopButtonLabel": "{jobsCount, plural, other {データフィード}}を停止", "xpack.ml.jobsList.stopDatafeedsModal.stopManagedDatafeedsDescription": "停止中", "xpack.ml.jobsList.stopJobErrorMessage": "ジョブの停止に失敗しました", "xpack.ml.jobsList.stoppedActionStatusText": "停止中", @@ -17865,6 +18116,7 @@ "xpack.ml.management.syncSavedObjectsFlyout.savedObjectsDeleted.description": "付属するジョブがない保存されたオブジェクトがある場合、削除されます。", "xpack.ml.management.syncSavedObjectsFlyout.savedObjectsDeleted.title": "一致しない保存されたオブジェクト({count})", "xpack.ml.management.syncSavedObjectsFlyout.sync.error": "一部のジョブを同期できません。", + "xpack.ml.management.syncSavedObjectsFlyout.sync.success": "{successCount} {successCount, plural, other {件のジョブ}}が同期されました", "xpack.ml.management.syncSavedObjectsFlyout.syncButton": "同期", "xpack.ml.maps.anomalyLayerActualLabel": "実際", "xpack.ml.maps.anomalyLayerByFieldNameLabel": "フィールド名別", @@ -17912,6 +18164,8 @@ "xpack.ml.models.dfaValidation.messages.lowTrainingPercentWarning": "学習ドキュメントの数が少ないと、モデルが不正確になる可能性があります。学習割合を増やすか、もっと大きいデータセットを使用してください。", "xpack.ml.models.dfaValidation.messages.noTestingDataTrainingPercentWarning": "モデルの学習では、すべての適格なドキュメントが使用されます。モデッルを評価するには、学習割合を減らして、テストデータを提供します。", "xpack.ml.models.dfaValidation.messages.topClassesHeading": "最上位クラス", + "xpack.ml.models.dfaValidation.messages.topClassesSuccessMessage": " {numCategories, plural, other {#個のカテゴリ}}の想定された確立が報告されます。", + "xpack.ml.models.dfaValidation.messages.topClassesWarningMessage": " {numCategories, plural, other {#個のカテゴリ}}の想定された確立が報告されます。多数のクラスがある場合は、ターゲットインデックスのサイズへの影響が大きい可能性があります。", "xpack.ml.models.dfaValidation.messages.trainingPercentHeading": "トレーニングパーセンテージ", "xpack.ml.models.dfaValidation.messages.trainingPercentSuccess": "学習割合が十分に高く、データのパターンをモデリングできます。", "xpack.ml.models.dfaValidation.messages.validationErrorHeading": "ジョブを検証できません。", @@ -17922,6 +18176,7 @@ "xpack.ml.models.jobService.categorization.messages.medianLineLength": "分析したフィールドの長さの中央値が{medianLimit}文字を超えています。", "xpack.ml.models.jobService.categorization.messages.noDataFound": "このフィールドには例が見つかりませんでした。選択した日付範囲にデータが含まれていることを確認してください。", "xpack.ml.models.jobService.categorization.messages.nullValues": "{percent}%以上のフィールド値が無効です。", + "xpack.ml.models.jobService.categorization.messages.tokenLengthValidation": "{number}フィールド{number, plural, other {個の値}}が分析されました。{percentage}%に{validTokenCount}以上のトークンが含まれます。", "xpack.ml.models.jobService.categorization.messages.tooManyTokens": "{sampleSize}値のサンプルに{tokenLimit}以上のトークンが見つかったため、フィールド値の例のトークン化に失敗しました。", "xpack.ml.models.jobService.categorization.messages.validFailureToGetTokens": "読み込んだサンプルのトークン化が正常に完了しました。", "xpack.ml.models.jobService.categorization.messages.validMedianLineLength": "読み込んだサンプルの行の長さの中央値は {medianCharCount} 文字未満でした。", @@ -17977,11 +18232,15 @@ "xpack.ml.models.jobValidation.messages.influencerLowMessage": "影響因子が構成されていません。影響因子の構成を強くお勧めします。", "xpack.ml.models.jobValidation.messages.influencerLowSuggestionMessage": "影響因子が構成されていません。影響因子として {influencerSuggestion} を使用することを検討してください。", "xpack.ml.models.jobValidation.messages.influencerLowSuggestionsMessage": "影響因子が構成されていません。1 つ以上の {influencerSuggestion} を使用することを検討してください。", + "xpack.ml.models.jobValidation.messages.jobGroupIdInvalidMaxLengthErrorMessage": "ジョブグループ名は {maxLength, plural, other {# 文字}}以下でなければなりません。", "xpack.ml.models.jobValidation.messages.jobGroupIdInvalidMessage": "ジョブグループ名の 1 つが無効です。アルファベットの小文字(a-z と 0-9)、ハイフンまたはアンダーラインが使用でき、最初と最後を英数字にする必要があります。", "xpack.ml.models.jobValidation.messages.jobGroupIdValidHeading": "ジョブグループ ID のフォーマットは有効です。", + "xpack.ml.models.jobValidation.messages.jobGroupIdValidMessage": "アルファベットの小文字(a-z と 0-9)、ハイフンまたはアンダーライン、最初と最後を英数字にし、{maxLength, plural, other {# 文字}} 以内にする必要があります。", "xpack.ml.models.jobValidation.messages.jobIdEmptyMessage": "ジョブ名フィールドは未入力のままにできません。", + "xpack.ml.models.jobValidation.messages.jobIdInvalidMaxLengthErrorMessage": "ジョブ ID は {maxLength, plural, other {# 文字}}以下でなければなりません。", "xpack.ml.models.jobValidation.messages.jobIdInvalidMessage": "ジョブ ID が無効です。アルファベットの小文字(a-z と 0-9)、ハイフンまたはアンダーラインが使用でき、最初と最後を英数字にする必要があります。", "xpack.ml.models.jobValidation.messages.jobIdValidHeading": "ジョブ ID のフォーマットは有効です。", + "xpack.ml.models.jobValidation.messages.jobIdValidMessage": "アルファベットの小文字(a-z と 0-9)、ハイフンまたはアンダーライン、最初と最後を英数字にし、{maxLength, plural, other {# 文字}} 以内にする必要があります。", "xpack.ml.models.jobValidation.messages.missingSummaryCountFieldNameMessage": "データフィードとアグリゲーションで構成されたジョブは summary_count_field_name を設定する必要があります。doc_count または適切な代替項目を使用してください。", "xpack.ml.models.jobValidation.messages.mmlGreaterThanEffectiveMaxMmlMessage": "現在のクラスターではジョブを実行できません。モデルメモリ上限が {effectiveMaxModelMemoryLimit} を超えています。", "xpack.ml.models.jobValidation.messages.mmlGreaterThanMaxMmlMessage": "モデルメモリー制限が、このクラスターに構成された最大モデルメモリー制限を超えています。", @@ -18053,6 +18312,7 @@ "xpack.ml.newJob.recognize.alreadyExistsLabel": "(すでに存在します)", "xpack.ml.newJob.recognize.cancelJobOverrideLabel": "閉じる", "xpack.ml.newJob.recognize.createJobButtonAriaLabel": "ジョブを作成", + "xpack.ml.newJob.recognize.createJobButtonLabel": "{numberOfJobs, plural, other {ジョブ}} を作成", "xpack.ml.newJob.recognize.dashboardsLabel": "ダッシュボード", "xpack.ml.newJob.recognize.datafeed.savedAriaLabel": "保存されました", "xpack.ml.newJob.recognize.datafeed.saveFailedAriaLabel": "保存に失敗", @@ -18065,12 +18325,14 @@ "xpack.ml.newJob.recognize.jobIdPrefixLabel": "ジョブ ID の接頭辞", "xpack.ml.newJob.recognize.jobLabel": "ジョブ名", "xpack.ml.newJob.recognize.jobLabelAllowedCharactersDescription": "ジョブラベルにはアルファベットの小文字(a-z と 0-9)、ハイフンまたはアンダーラインが使用でき、最初と最後を英数字にする必要があります", + "xpack.ml.newJob.recognize.jobPrefixInvalidMaxLengthErrorMessage": "ジョブグループ名は {maxLength, plural, other {# 文字}}以下でなければなりません。", "xpack.ml.newJob.recognize.jobsCreatedTitle": "ジョブが作成されました", "xpack.ml.newJob.recognize.jobsCreationFailed.resetButtonAriaLabel": "リセット", "xpack.ml.newJob.recognize.jobSettingsTitle": "ジョブ設定", "xpack.ml.newJob.recognize.jobsTitle": "ジョブ", "xpack.ml.newJob.recognize.moduleCheckJobsExistWarningDescription": "モジュールのジョブがクラッシュしたか確認する際にエラーが発生しました。", "xpack.ml.newJob.recognize.moduleCheckJobsExistWarningTitle": "モジュール {moduleId} の確認中にエラーが発生", + "xpack.ml.newJob.recognize.moduleSetupFailedWarningDescription": "モジュールでの{count, plural, other {件のジョブ}}の作成中にエラーが発生しました。", "xpack.ml.newJob.recognize.moduleSetupFailedWarningTitle": "モジュール {moduleId} のセットアップ中にエラーが発生", "xpack.ml.newJob.recognize.newJobFromTitle": "{pageTitle} からの新しいジョブ", "xpack.ml.newJob.recognize.overrideConfigurationHeader": "{jobID}の構成を上書き", @@ -18392,6 +18654,8 @@ "xpack.ml.newJob.wizard.validateJob.frequencyInvalidTimeIntervalFormatErrorMessage": "{value}は有効な期間の形式ではありません。例:{thirtySeconds}、{tenMinutes}、{oneHour}、{sevenDays}。また、0よりも大きい数字である必要があります。", "xpack.ml.newJob.wizard.validateJob.groupNameAlreadyExists": "グループ ID がすでに存在します。グループ ID は既存のジョブやグループと同じにできません。", "xpack.ml.newJob.wizard.validateJob.jobGroupAllowedCharactersDescription": "ジョブグループ名にはアルファベットの小文字(a-z と 0-9)、ハイフンまたはアンダーラインが使用でき、最初と最後を英数字にする必要があります", + "xpack.ml.newJob.wizard.validateJob.jobGroupMaxLengthDescription": "ジョブグループ名は {maxLength, plural, other {# 文字}}以下でなければなりません。", + "xpack.ml.newJob.wizard.validateJob.jobIdInvalidMaxLengthErrorMessage": "ジョブ ID は {maxLength, plural, other {# 文字}}以下でなければなりません。", "xpack.ml.newJob.wizard.validateJob.jobNameAllowedCharactersDescription": "ジョブ名にはアルファベットの小文字(a-z と 0-9)、ハイフンまたはアンダーラインが使用でき、最初と最後を英数字にする必要があります", "xpack.ml.newJob.wizard.validateJob.jobNameAlreadyExists": "ジョブ ID がすでに存在します。ジョブ ID は既存のジョブやグループと同じにできません。", "xpack.ml.newJob.wizard.validateJob.modelMemoryLimitRangeInvalidErrorMessage": "モデルメモリー制限は最高値の {maxModelMemoryLimit} よりも高くできません", @@ -18460,7 +18724,9 @@ "xpack.ml.previewAlert.hideResultsButtonLabel": "結果を非表示", "xpack.ml.previewAlert.intervalLabel": "ルール条件と間隔を確認", "xpack.ml.previewAlert.jobsLabel": "ジョブID:", + "xpack.ml.previewAlert.otherValuesLabel": "および{count, plural, other {#その他}}", "xpack.ml.previewAlert.previewErrorTitle": "プレビューを読み込めません", + "xpack.ml.previewAlert.previewMessage": "過去{interval}に{alertsCount, plural, other {# 件の異常}}が見つかりました。", "xpack.ml.previewAlert.scoreLabel": "異常スコア:", "xpack.ml.previewAlert.showResultsButtonLabel": "結果を表示", "xpack.ml.previewAlert.testButtonLabel": "テスト", @@ -18570,10 +18836,12 @@ "xpack.ml.sampleDataLinkLabel": "ML ジョブ", "xpack.ml.selectDataViewLabel": "データビューを選択", "xpack.ml.settings.anomalyDetection.anomalyDetectionTitle": "異常検知", + "xpack.ml.settings.anomalyDetection.calendarsSummaryCount": "{calendarsCountBadge} {calendarsCount, plural, other {個のカレンダー}}があります", "xpack.ml.settings.anomalyDetection.calendarsText": "システム停止日や祝日など、異常値を生成したくないイベントについては、カレンダーに予定されているイベントのリストを登録できます。", "xpack.ml.settings.anomalyDetection.calendarsTitle": "カレンダー", "xpack.ml.settings.anomalyDetection.createCalendarLink": "作成", "xpack.ml.settings.anomalyDetection.createFilterListsLink": "作成", + "xpack.ml.settings.anomalyDetection.filterListsSummaryCount": "{filterListsCountBadge} {filterListsCount, plural, other {個のフィルターリスト}}があります", "xpack.ml.settings.anomalyDetection.filterListsText": "フィルターリストには、イベントを機械学習分析に含める、または除外するのに使用する値が含まれています。", "xpack.ml.settings.anomalyDetection.filterListsTitle": "フィルターリスト", "xpack.ml.settings.anomalyDetection.loadingCalendarsCountErrorMessage": "カレンダー数の取得中にエラーが発生しました", @@ -18623,6 +18891,7 @@ "xpack.ml.settings.filterLists.editFilterList.loadingDetailsOfFilterErrorMessage": "フィルター {filterId} の詳細の読み込み中にエラーが発生しました", "xpack.ml.settings.filterLists.editFilterList.saveButtonLabel": "保存", "xpack.ml.settings.filterLists.editFilterList.savingFilterErrorMessage": "フィルター {filterId} の保存中にエラーが発生しました", + "xpack.ml.settings.filterLists.editFilterList.totalItemsDescription": "合計{totalItemCount, plural, other {# 個のアイテム}}", "xpack.ml.settings.filterLists.filterLists.loadingFilterListsErrorMessage": "フィルターリストの読み込み中にエラーが発生しました", "xpack.ml.settings.filterLists.filterWithIdExistsErrorMessage": "ID {filterId} のフィルターがすでに存在します", "xpack.ml.settings.filterLists.listHeader.filterListsContainsNotAllowedValuesDescription": "フィルターリストには、イベントを機械学習分析に含める、または除外するのに使用する値が含まれています。同じフィルターリストを複数ジョブに使用できます。{br}{learnMoreLink}", @@ -18680,10 +18949,12 @@ "xpack.ml.timeSeriesExplorer.annotationDescriptionList.startTitle": "開始", "xpack.ml.timeSeriesExplorer.annotationFlyout.addAnnotationTitle": "注釈の追加", "xpack.ml.timeSeriesExplorer.annotationFlyout.annotationTextLabel": "注釈テキスト", + "xpack.ml.timeSeriesExplorer.annotationFlyout.approachingMaxLengthWarning": "残り {charsRemaining, number} {charsRemaining, plural, other {文字}}", "xpack.ml.timeSeriesExplorer.annotationFlyout.cancelButtonLabel": "キャンセル", "xpack.ml.timeSeriesExplorer.annotationFlyout.createButtonLabel": "作成", "xpack.ml.timeSeriesExplorer.annotationFlyout.deleteButtonLabel": "削除", "xpack.ml.timeSeriesExplorer.annotationFlyout.editAnnotationTitle": "注釈を編集します", + "xpack.ml.timeSeriesExplorer.annotationFlyout.maxLengthError": "最長 {maxChars} 文字を {charsOver, number} {charsOver, plural, other {文字}} 超過", "xpack.ml.timeSeriesExplorer.annotationFlyout.noAnnotationTextError": "注釈テキストを入力してください", "xpack.ml.timeSeriesExplorer.annotationFlyout.updateButtonLabel": "更新", "xpack.ml.timeSeriesExplorer.annotationsErrorCallOutTitle": "注釈の読み込み中にエラーが発生しました。", @@ -18696,9 +18967,11 @@ "xpack.ml.timeSeriesExplorer.ascOptionsOrderLabel": "昇順", "xpack.ml.timeSeriesExplorer.autoSelectingFirstJobText": "、初めのジョブを自動選択します", "xpack.ml.timeSeriesExplorer.bucketAnomalyScoresErrorMessage": "バケット異常スコアの取得エラー", + "xpack.ml.timeSeriesExplorer.canNotViewRequestedJobsWarningMessage": "リクエストされた‘{invalidIdsCount, plural, other {件のジョブ}} {invalidIds} をこのダッシュボードで表示できません", "xpack.ml.timeSeriesExplorer.canNotViewRequestedJobsWarningWithReasonMessage": "{reason}ため、このダッシュボードでは {selectedJobId} を表示できません。", "xpack.ml.timeSeriesExplorer.countDataInChartDetailsDescription": "{openBrace}{cardinalityValue} 特徴的な {fieldName} {cardinality, plural, one {} other { 値}}{closeBrace}", "xpack.ml.timeSeriesExplorer.createNewSingleMetricJobLinkText": "新規シングルメトリックジョブを作成", + "xpack.ml.timeSeriesExplorer.dataNotChartableDescription": "選択された{entityCount, plural, other {エンティティ}}のモデルプロットは収集されていません。\nこのディテクターのソースデータはプロットできません。", "xpack.ml.timeSeriesExplorer.deleteAnnotationModal.cancelButtonLabel": "キャンセル", "xpack.ml.timeSeriesExplorer.deleteAnnotationModal.deleteAnnotationTitle": "この注釈を削除しますか?", "xpack.ml.timeSeriesExplorer.deleteAnnotationModal.deleteButtonLabel": "削除", @@ -18768,6 +19041,7 @@ "xpack.ml.timeSeriesExplorer.setManualInputHelperText": "一致する値がありません", "xpack.ml.timeSeriesExplorer.showForecastLabel": "予測を表示", "xpack.ml.timeSeriesExplorer.showModelBoundsLabel": "モデルバウンドを表示", + "xpack.ml.timeSeriesExplorer.singleMetricRequiredMessage": "シングルメトリックを表示するには、{missingValuesCount, plural, one {{fieldName1}の値} other {{fieldName1}および{fieldName2}の値}}を選択します。", "xpack.ml.timeSeriesExplorer.singleTimeSeriesAnalysisTitle": "{functionLabel} の単独時系列分析", "xpack.ml.timeSeriesExplorer.sortByLabel": "並べ替え基準", "xpack.ml.timeSeriesExplorer.sortByNameLabel": "名前", @@ -18819,6 +19093,7 @@ "xpack.ml.trainedModels.modelsList.deleteModal.cancelButtonLabel": "キャンセル", "xpack.ml.trainedModels.modelsList.deleteModal.deleteButtonLabel": "削除", "xpack.ml.trainedModels.modelsList.deleteModal.header": "{modelsCount, plural, one {{modelId}} other {#個のモデル}}を削除しますか?", + "xpack.ml.trainedModels.modelsList.deleteModal.modelsWithPipelinesWarningMessage": "{modelsWithPipelinesCount, plural, other {モデル}} {modelsWithPipelines} {modelsWithPipelinesCount, plural, other {には}}パイプラインが関連付けられています。", "xpack.ml.trainedModels.modelsList.deleteModelActionLabel": "モデルを削除", "xpack.ml.trainedModels.modelsList.deleteModelsButtonLabel": "削除", "xpack.ml.trainedModels.modelsList.disableSelectableMessage": "モデルにはパイプラインが関連付けられています", @@ -18836,6 +19111,7 @@ "xpack.ml.trainedModels.modelsList.expandedRow.processorsTitle": "定義", "xpack.ml.trainedModels.modelsList.expandedRow.statsTabLabel": "統計", "xpack.ml.trainedModels.modelsList.expandRow": "拡張", + "xpack.ml.trainedModels.modelsList.fetchDeletionErrorMessage": "{modelsCount, plural, other {モデル}}を削除できませんでした", "xpack.ml.trainedModels.modelsList.fetchFailedErrorMessage": "モデルの取り込みが失敗しました", "xpack.ml.trainedModels.modelsList.fetchModelStatsErrorMessage": "モデル統計情報の取り込みが失敗しました", "xpack.ml.trainedModels.modelsList.forceStopDialog.cancelText": "キャンセル", @@ -18854,11 +19130,13 @@ "xpack.ml.trainedModels.modelsList.pipelines.processorStats.timePerDocHeader": "ドキュメントごとの時間", "xpack.ml.trainedModels.modelsList.pipelines.processorStats.typeHeader": "プロセッサータイプ", "xpack.ml.trainedModels.modelsList.selectableMessage": "モデルを選択", + "xpack.ml.trainedModels.modelsList.selectedModelsMessage": "{modelsCount, plural, other {#個のモデル}}が選択されました", "xpack.ml.trainedModels.modelsList.startFailed": "\"{modelId}\"の開始に失敗しました", "xpack.ml.trainedModels.modelsList.startSuccess": "\"{modelId}\"のデプロイが正常に開始しました。", "xpack.ml.trainedModels.modelsList.stateHeader": "ステータス", "xpack.ml.trainedModels.modelsList.stopFailed": "\"{modelId}\"の停止に失敗しました", "xpack.ml.trainedModels.modelsList.stopSuccess": "\"{modelId}\"のデプロイが正常に停止しました。", + "xpack.ml.trainedModels.modelsList.successfullyDeletedMessage": "{modelsCount, plural, one {モデル{modelsToDeleteIds}} other {#個のモデル}} {modelsCount, plural, other {が}}正常に削除されました", "xpack.ml.trainedModels.modelsList.totalAmountLabel": "学習済みモデルの合計数", "xpack.ml.trainedModels.modelsList.typeHeader": "型", "xpack.ml.trainedModels.modelsList.unableToDeleteModelsErrorMessage": "モデルを削除できません", @@ -18997,6 +19275,10 @@ "xpack.monitoring.alerts.elasticsearchVersionMismatch.ui.firingMessage": "このクラスターでは、複数のバージョンの Elasticsearch({versions})が実行されています。", "xpack.monitoring.alerts.filterHelpText": "KQL式を使用して、アラートトリガーの範囲を制限します。", "xpack.monitoring.alerts.filterLable": "フィルター", + "xpack.monitoring.alerts.flyoutExpressions.timeUnits.dayLabel": "{timeValue, plural, other {日}}", + "xpack.monitoring.alerts.flyoutExpressions.timeUnits.hourLabel": "{timeValue, plural, other {時間}}", + "xpack.monitoring.alerts.flyoutExpressions.timeUnits.minuteLabel": "{timeValue, plural, other {分}}", + "xpack.monitoring.alerts.flyoutExpressions.timeUnits.secondLabel": "{timeValue, plural, other {秒}}", "xpack.monitoring.alerts.kibanaVersionMismatch.actionVariables.clusterHealth": "このクラスターを実行しているKibanaのバージョン。", "xpack.monitoring.alerts.kibanaVersionMismatch.actionVariables.clusterName": "インスタンスが属しているクラスター。", "xpack.monitoring.alerts.kibanaVersionMismatch.description": "クラスターに複数のバージョンの Kibana があるときにアラートを発行します。", @@ -20473,6 +20755,7 @@ "xpack.observability.alertsTable.loadingTextLabel": "アラートを読み込んでいます", "xpack.observability.alertsTable.moreActionsTextLabel": "さらにアクションを表示", "xpack.observability.alertsTable.notEnoughPermissions": "追加の権限が必要です", + "xpack.observability.alertsTable.showingAlertsTitle": "{totalAlerts, plural, other {アラート}}", "xpack.observability.alertsTable.viewDetailsTextLabel": "詳細を表示", "xpack.observability.alertsTable.viewInAppTextLabel": "アプリで表示", "xpack.observability.alertsTable.viewRuleDetailsButtonText": "ルール詳細を表示", @@ -20831,6 +21114,7 @@ "xpack.osquery.agent_policy_details.fetchError": "エージェントポリシー詳細の取得中にエラーが発生しました", "xpack.osquery.agent_status.fetchError": "エージェントステータスの取得中にエラーが発生しました", "xpack.osquery.agentDetails.fetchError": "エージェント詳細の取得中にエラーが発生しました", + "xpack.osquery.agentPolicy.confirmModalCalloutDescription": "選択した{agentPolicyCount, plural, other {エージェントポリシー}}が一部のエージェントですでに使用されていることをFleetが検出しました。このアクションの結果として、Fleetはこの{agentPolicyCount, plural, other {エージェントポリシー}}を使用するすべてのエージェントに更新をデプロイします。", "xpack.osquery.agentPolicy.confirmModalCancelButtonLabel": "キャンセル", "xpack.osquery.agentPolicy.confirmModalConfirmButtonLabel": "変更を保存してデプロイ", "xpack.osquery.agentPolicy.confirmModalDescription": "続行していいですか?", @@ -20863,6 +21147,7 @@ "xpack.osquery.configUploader.exampleConfigLinkLabel": "構成の例", "xpack.osquery.configUploader.initialPromptTextLabel": "osquery構成ファイルを選択するか、ドラッグします", "xpack.osquery.configUploader.unsupportedFileTypeText": "ファイルタイプ{fileType}はサポートされていません。{supportedFileTypes}構成ファイルをアップロードしてください", + "xpack.osquery.createScheduledQuery.agentPolicyAgentsCountText": "{count, plural, other {#個のエージェント}}が登録されました", "xpack.osquery.deletePack.confirmationModal.body": "このパックを削除しようとしています。実行しますか?", "xpack.osquery.deletePack.confirmationModal.cancelButtonLabel": "キャンセル", "xpack.osquery.deletePack.confirmationModal.confirmButtonLabel": "確認", @@ -20903,6 +21188,7 @@ "xpack.osquery.liveQuery.permissionDeniedPromptBody": "クエリ結果を表示するには、ユーザーロールを更新して、{logs}インデックスに対する{read}権限を付与するように、管理者に依頼してください。", "xpack.osquery.liveQuery.permissionDeniedPromptTitle": "パーミッションが拒否されました", "xpack.osquery.liveQuery.queryForm.largeQueryError": "クエリが大きすぎます(最大{maxLength}文字)", + "xpack.osquery.liveQueryActionResults.summary.agentsQueriedLabelText": "{count, plural, other {#個のエージェント}}をクエリしました", "xpack.osquery.liveQueryActionResults.summary.expiredLabelText": "期限切れ", "xpack.osquery.liveQueryActionResults.summary.failedLabelText": "失敗", "xpack.osquery.liveQueryActionResults.summary.pendingLabelText": "未応答", @@ -20988,6 +21274,7 @@ "xpack.osquery.pack.queryFlyoutForm.versionFieldLabel": "最低Osqueryバージョン", "xpack.osquery.pack.table.activatedSuccessToastMessageText": "\"{packName}\"が正常にアクティブ化されました", "xpack.osquery.pack.table.deactivatedSuccessToastMessageText": "\"{packName}\"が正常に非アクティブ化されました", + "xpack.osquery.pack.table.deleteQueriesButtonLabel": "{queriesCount, plural, other {# 個のクエリ}}を削除", "xpack.osquery.packDetails.pageTitle": "{queryName}詳細", "xpack.osquery.packDetails.viewAllPackListTitle": "すべてのパックを表示", "xpack.osquery.packDetailsPage.editQueryButtonLabel": "編集", @@ -21005,6 +21292,9 @@ "xpack.osquery.packUploader.unsupportedFileTypeText": "ファイルタイプ{fileType}はサポートされていません。{supportedFileTypes}構成ファイルをアップロードしてください", "xpack.osquery.permissionDeniedErrorMessage": "このページへのアクセスが許可されていません。", "xpack.osquery.permissionDeniedErrorTitle": "パーミッションが拒否されました", + "xpack.osquery.queriesStatusTable.agentsLabelText": "{count, plural, other {エージェント}}", + "xpack.osquery.queriesStatusTable.documentLabelText": "{count, plural, other {ドキュメント}}", + "xpack.osquery.queriesStatusTable.errorsLabelText": "{count, plural, other {エラー}}", "xpack.osquery.queriesTable.osqueryVersionAllLabel": "すべて", "xpack.osquery.queryFlyoutForm.addFormTitle": "次のクエリを関連付ける", "xpack.osquery.queryFlyoutForm.cancelButtonLabel": "キャンセル", @@ -21015,6 +21305,7 @@ "xpack.osquery.results.errorSearchDescription": "すべての結果検索でエラーが発生しました", "xpack.osquery.results.failSearchDescription": "結果を取得できませんでした", "xpack.osquery.results.fetchError": "結果の取得中にエラーが発生しました", + "xpack.osquery.results.multipleAgentsResponded": "{agentsResponded, plural, other {# エージェント}}が応答しましたが、osqueryデータが報告されていません。", "xpack.osquery.savedQueries.dropdown.searchFieldLabel": "保存されたクエリから構築(任意)", "xpack.osquery.savedQueries.dropdown.searchFieldPlaceholder": "保存されたクエリの検索", "xpack.osquery.savedQueries.form.packConfigSection.description": "次のリストのオプションは任意であり、クエリがパックに割り当てられるときにのみ適用されます。", @@ -21130,6 +21421,7 @@ "xpack.remoteClusters.editAction.successTitle": "リモートクラスター「{name}」が編集されました", "xpack.remoteClusters.editBreadcrumbTitle": "編集", "xpack.remoteClusters.editTitle": "リモートクラスターを編集します", + "xpack.remoteClusters.form.errors.illegalCharacters": "名前から {characterListLength, plural, other {文字}} {characterList} を削除してください。", "xpack.remoteClusters.form.errors.illegalSpace": "名前にスペースは使用できません。", "xpack.remoteClusters.form.errors.nameMissing": "名前が必要です。", "xpack.remoteClusters.form.errors.seedMissing": "シードノードが最低 1 つ必要です。", @@ -21348,6 +21640,7 @@ "xpack.reporting.listing.table.deleteConfirmTitle": "「{name}」レポートを削除しますか?", "xpack.reporting.listing.table.deleteFailedErrorMessage": "レポートは削除されませんでした:{error}", "xpack.reporting.listing.table.deleteNumConfirmTitle": "{num} 件のレポートを削除しますか?", + "xpack.reporting.listing.table.deleteReportButton": "{num, plural, other {件のレポート} }を削除", "xpack.reporting.listing.table.downloadReportButtonLabel": "レポートをダウンロード", "xpack.reporting.listing.table.downloadReportDescription": "このレポートを新しいタブでダウンロードします。", "xpack.reporting.listing.table.loadingReportsDescription": "レポートを読み込み中です", @@ -21569,6 +21862,7 @@ "xpack.rollupJobs.featureCatalogueDescription": "今後の分析用に履歴データを小さなインデックスに要約して格納します。", "xpack.rollupJobs.indexMgmtBadge.rollupLabel": "ロールアップ", "xpack.rollupJobs.indexMgmtToggle.toggleLabel": "ロールアップインデックスを含める", + "xpack.rollupJobs.jobActionMenu.buttonLabel": "{jobCount, plural, other {件のジョブ}}の管理", "xpack.rollupJobs.jobActionMenu.cloneJobLabel": "ジョブのクローンを作成します", "xpack.rollupJobs.jobActionMenu.deleteJob.confirmModal.cancelButtonText": "キャンセル", "xpack.rollupJobs.jobActionMenu.deleteJob.confirmModal.confirmButtonText": "削除", @@ -21577,8 +21871,11 @@ "xpack.rollupJobs.jobActionMenu.deleteJob.confirmModal.multipleDeletionDescription": "{isSingleSelection, plural, one {このジョブ} other {これらのジョブ}}を削除しようとしています", "xpack.rollupJobs.jobActionMenu.deleteJob.confirmModal.multipleDeletionTitle": "{count} 件のロールアップジョブを削除しますか?", "xpack.rollupJobs.jobActionMenu.deleteJob.confirmModal.startedMessage": "開始済み", + "xpack.rollupJobs.jobActionMenu.deleteJobLabel": "{isSingleSelection, plural, other {件のジョブ}}を削除", "xpack.rollupJobs.jobActionMenu.jobActionMenuButtonAriaLabel": "ジョブオプション", "xpack.rollupJobs.jobActionMenu.panelTitle": "ジョブオプション", + "xpack.rollupJobs.jobActionMenu.startJobLabel": "{isSingleSelection, plural, other {件のジョブ}}を開始", + "xpack.rollupJobs.jobActionMenu.stopJobLabel": "{isSingleSelection, plural, other {件のジョブ}}を停止", "xpack.rollupJobs.jobActionMenu.updatingText": "更新中", "xpack.rollupJobs.jobDetails.tabHistogram.intervalLabel": "ヒストグラムの間隔", "xpack.rollupJobs.jobDetails.tabHistogram.nameColumnLabel": "フィールド", @@ -21660,19 +21957,26 @@ "xpack.savedObjectsTagging.assignFlyout.actionBar.pendingChanges": "{count} 件の変更が保留中です", "xpack.savedObjectsTagging.assignFlyout.actionBar.resetLabel": "リセット", "xpack.savedObjectsTagging.assignFlyout.actionBar.selectedAllLabel": "すべて選択", + "xpack.savedObjectsTagging.assignFlyout.actionBar.totalResultsLabel": "{count, plural, other {# 個の保存されたオブジェクト}}", "xpack.savedObjectsTagging.assignFlyout.cancelButtonLabel": "キャンセル", "xpack.savedObjectsTagging.assignFlyout.confirmButtonLabel": "タグ割り当てを保存", "xpack.savedObjectsTagging.assignFlyout.resultList.addedLabel": "追加", "xpack.savedObjectsTagging.assignFlyout.resultList.removedLabel": "削除しました", "xpack.savedObjectsTagging.assignFlyout.searchPlaceholder": "保存されたオブジェクト名で検索", + "xpack.savedObjectsTagging.assignFlyout.successNotificationTitle": "{count, plural, other {# 個の保存されたオブジェクト}}に割り当てを保存しました", "xpack.savedObjectsTagging.assignFlyout.title": "タグ割り当ての管理", "xpack.savedObjectsTagging.assignFlyout.typeFilterName": "型", "xpack.savedObjectsTagging.components.tagSelector.createTagOptionLabel": "タグを作成", "xpack.savedObjectsTagging.feature.featureName": "タグ管理", + "xpack.savedObjectsTagging.management.actionBar.selectedTagsLabel": "{count, plural, other {# 個の選択されたタグ}}", + "xpack.savedObjectsTagging.management.actionBar.totalTagsLabel": "{count, plural, other {# 個のタグ}}", "xpack.savedObjectsTagging.management.actions.bulkAssign.label": "タグ割り当ての管理", "xpack.savedObjectsTagging.management.actions.bulkDelete.ariaLabel": "選択したタグを削除", + "xpack.savedObjectsTagging.management.actions.bulkDelete.confirm.confirmButtonText": "{count, plural, other {個のタグ}}を削除", "xpack.savedObjectsTagging.management.actions.bulkDelete.confirm.text": "{count, plural, one {このタグ} other {これらのタグ}}を削除すると、{count, plural, one {それを} other {それらを}}保存されたオブジェクトに割り当てることができなくなります。{count, plural, one {このタグ} other {これらのタグ}}は、現在{count, plural, one {それを} other {それらを}}使用しているすべての保存されたオブジェクトから削除されます。", + "xpack.savedObjectsTagging.management.actions.bulkDelete.confirm.title": "{count, plural, other {# 個のタグ}}を削除", "xpack.savedObjectsTagging.management.actions.bulkDelete.label": "削除", + "xpack.savedObjectsTagging.management.actions.bulkDelete.notification.successTitle": "{count, plural, other {# 個のタグ}}を削除しました", "xpack.savedObjectsTagging.management.actions.clearSelection.label": "選択した項目をクリア", "xpack.savedObjectsTagging.management.actions.createTagButton": "タグを作成", "xpack.savedObjectsTagging.management.breadcrumb.index": "タグ", @@ -21698,6 +22002,7 @@ "xpack.savedObjectsTagging.management.table.columns.connections": "接続", "xpack.savedObjectsTagging.management.table.columns.description": "説明", "xpack.savedObjectsTagging.management.table.columns.name": "名前", + "xpack.savedObjectsTagging.management.table.content.connectionCount": "{relationCount, plural, other {# 個の保存されたオブジェクト}}", "xpack.savedObjectsTagging.management.tagPreviewText": "プレビュー", "xpack.savedObjectsTagging.modals.confirmDelete.confirmButtonText": "タグを削除", "xpack.savedObjectsTagging.modals.confirmDelete.text": "このタグを削除すると、保存されたオブジェクトに割り当てることができなくなります。このタグは、現在それを使用しているすべての保存されたオブジェクトから削除されます。", @@ -21876,6 +22181,7 @@ "xpack.security.management.apiKeys.createBreadcrumb": "作成", "xpack.security.management.apiKeys.createSuccessMessage": "API キー'{name}'を無効にしました", "xpack.security.management.apiKeys.deleteApiKey.confirmModal.cancelButtonLabel": "キャンセル", + "xpack.security.management.apiKeys.deleteApiKey.confirmModal.confirmButtonLabel": "{count, plural, other {APIキー}}を削除", "xpack.security.management.apiKeys.deleteApiKey.confirmModal.deleteMultipleListDescription": "これらのAPIキーを削除しようとしています。", "xpack.security.management.apiKeys.deleteApiKey.confirmModal.deleteMultipleTitle": "{count} APIキーを削除しますか?", "xpack.security.management.apiKeys.deleteApiKey.confirmModal.deleteSingleTitle": "APIキー'{name}'を削除しますか?", @@ -21903,6 +22209,7 @@ "xpack.security.management.apiKeys.table.deleteAction": "削除", "xpack.security.management.apiKeys.table.deleteDescription": "このAPIキーを削除", "xpack.security.management.apiKeys.table.fetchingApiKeysErrorMessage": "権限の確認エラー:{message}", + "xpack.security.management.apiKeys.table.invalidateApiKeyButton": "{count, plural, other {APIキー}}を削除", "xpack.security.management.apiKeys.table.loadingApiKeysDescription": "API キーを読み込み中…", "xpack.security.management.apiKeys.table.manageOwnKeysWarning": "自分のAPIキーを管理する権限のみが付与されています。", "xpack.security.management.apiKeys.table.nameColumnName": "名前", @@ -21950,6 +22257,7 @@ "xpack.security.management.editRole.errorSavingRoleError": "ロールの保存エラー", "xpack.security.management.editRole.featureTable.actionLegendText": "{featureName} 機能権限", "xpack.security.management.editRole.featureTable.customizeSubFeaturePrivilegesSwitchLabel": "サブ機能権限をカスタマイズする", + "xpack.security.management.editRole.featureTable.featureAccordionSwitchLabel": "{grantedCount} / {featureCount} {featureCount, plural, other {機能}}が付与されました", "xpack.security.management.editRole.featureTable.featureVisibilityTitle": "機能権限をカスタマイズ", "xpack.security.management.editRole.featureTable.managementCategoryHelpText": "スタック管理へのアクセスは、ElasticsearchとKibanaの両方の権限によって決まり、明示的に無効にすることはできません。", "xpack.security.management.editRole.featureTable.privilegeCustomizationTooltip": "機能でサブ機能の権限がカスタマイズされています。この行を展開すると詳細が表示されます。", @@ -22130,6 +22438,7 @@ "xpack.security.management.roleMappings.createRoleMappingButton": "ロールマッピングの作成", "xpack.security.management.roleMappings.createRoleMappingButtonLabel": "ロールマッピングの作成", "xpack.security.management.roleMappings.deleteRoleMapping.confirmModal.cancelButtonLabel": "キャンセル", + "xpack.security.management.roleMappings.deleteRoleMapping.confirmModal.confirmButtonLabel": "{count, plural, other {ロールマッピング}}を削除", "xpack.security.management.roleMappings.deleteRoleMapping.confirmModal.deleteMultipleListDescription": "これらのロールマッピングを削除しようとしています:", "xpack.security.management.roleMappings.deleteRoleMapping.confirmModal.deleteMultipleTitle": "{count}個のロールマッピングを削除しますか?", "xpack.security.management.roleMappings.deleteRoleMapping.confirmModal.deleteSingleTitle": "ロールマッピング'{name}'を削除しますか?", @@ -22138,6 +22447,7 @@ "xpack.security.management.roleMappings.deleteRoleMapping.successMultipleNotificationTitle": "{count}個のロールマッピングを削除しました", "xpack.security.management.roleMappings.deleteRoleMapping.successSingleNotificationTitle": "ロールマッピング'{name}'を削除しました", "xpack.security.management.roleMappings.deleteRoleMapping.unknownError": "ロールマッピングの削除エラー", + "xpack.security.management.roleMappings.deleteRoleMappingButton": "{count, plural, other {ロールマッピング}}を削除", "xpack.security.management.roleMappings.deniedPermissionDescription": "システム管理者にお問い合わせください。", "xpack.security.management.roleMappings.deniedPermissionTitle": "ロールマッピングを管理するにはパーミッションが必要です", "xpack.security.management.roleMappings.emptyPromptDescription": "ロールマッピングはユーザーに割り当てられるルールを制御します。", @@ -22155,6 +22465,7 @@ "xpack.security.management.roleMappings.roleMappingTableLoadingMessage": "ロールマッピングを読み込んでいます…", "xpack.security.management.roleMappings.roleMappingTitle": "ロールマッピング", "xpack.security.management.roleMappings.rolesColumnName": "ロール", + "xpack.security.management.roleMappings.roleTemplates": "{templateCount, plural, other {# ロールテンプレート}}が定義されました", "xpack.security.management.roleMappingsTitle": "ロールマッピング", "xpack.security.management.roles.actionsColumnName": "アクション", "xpack.security.management.roles.cloneRoleActionLabel": "{roleName} を複製", @@ -22211,17 +22522,23 @@ "xpack.security.management.users.confirmDelete.userDeletingErrorNotificationMessage": "ユーザー {username} の削除中にエラーが発生しました", "xpack.security.management.users.confirmDelete.userSuccessfullyDeletedNotificationMessage": "ユーザー {username} が削除されました", "xpack.security.management.users.confirmDeleteUsers.cancelButton": "キャンセル", + "xpack.security.management.users.confirmDeleteUsers.confirmButton": "{isLoading, select, true{{count, plural, other {ユーザー}}を削除しています…} other{{count, plural, other {ユーザー}}の削除}}", + "xpack.security.management.users.confirmDeleteUsers.description": "{count, plural, one{このユーザー} other{これらのユーザー}}は完全に削除されます。Elasticへのアクセスが削除されました{count, plural, other {。}}", "xpack.security.management.users.confirmDeleteUsers.errorMessage": "ユーザー'{username}'を削除できませんでした", "xpack.security.management.users.confirmDeleteUsers.successMessage": "ユーザー'{username}'が削除されました", "xpack.security.management.users.confirmDeleteUsers.title": "{count, plural, one{user '{username}'} other{{count}人のユーザー}}を削除しますか?", "xpack.security.management.users.confirmDisableUsers.cancelButton": "キャンセル", + "xpack.security.management.users.confirmDisableUsers.confirmButton": "{isLoading, select, true{{count, plural, other {ユーザー}}を無効にしています…} other{{count, plural, other {ユーザー}}の無効化}}", "xpack.security.management.users.confirmDisableUsers.confirmSystemPasswordButton": "{isLoading, select, true{ユーザーを無効にしています…} other{理解しています。このユーザーを無効にする}}", + "xpack.security.management.users.confirmDisableUsers.description": "{count, plural, one{このユーザー} other{これらのユーザー}}はElasticにアクセスできなくなります{count, plural, other {。}}", "xpack.security.management.users.confirmDisableUsers.errorMessage": "ユーザー'{username}'を無効にできませんでした", "xpack.security.management.users.confirmDisableUsers.successMessage": "ユーザー'{username}'が無効にされました", "xpack.security.management.users.confirmDisableUsers.systemUserDescription": "無効にした後は、別のユーザー詳細情報を使用して手動で構成ファイルを更新し、Kibanaを再起動する必要があります。", "xpack.security.management.users.confirmDisableUsers.systemUserWarning": "このユーザーを無効にすると、KibanaはElasticsearchと通信できません。", "xpack.security.management.users.confirmDisableUsers.title": "{count, plural, one{user '{username}'} other{{count}人のユーザー}}を無効にしますか?", "xpack.security.management.users.confirmEnableUsers.cancelButton": "キャンセル", + "xpack.security.management.users.confirmEnableUsers.confirmButton": "{isLoading, select, true{{count, plural, other {ユーザー}}を有効にしています…} other{{count, plural, other {ユーザー}}の有効化}}", + "xpack.security.management.users.confirmEnableUsers.description": "{count, plural, one{このユーザー} other{これらのユーザー}}はElasticにアクセスできます{count, plural, other {。}}", "xpack.security.management.users.confirmEnableUsers.errorMessage": "ユーザー'{username}'を有効にできませんでした", "xpack.security.management.users.confirmEnableUsers.successMessage": "ユーザー {username} が有効にされました", "xpack.security.management.users.confirmEnableUsers.title": "{count, plural, one{user '{username}'} other{{count}人のユーザー}}を有効にしますか?", @@ -22390,6 +22707,7 @@ "xpack.securitySolution.alertsView.moduleLabel": "モジュール", "xpack.securitySolution.alertsView.showing": "表示中", "xpack.securitySolution.alertsView.totalCountOfAlerts": "外部アラート", + "xpack.securitySolution.alertsView.unit": "外部{totalCount, plural, other {アラート}}", "xpack.securitySolution.allHost.errorSearchDescription": "すべてのホスト検索でエラーが発生しました", "xpack.securitySolution.allHost.failSearchDescription": "すべてのホストで検索を実行できませんでした", "xpack.securitySolution.andOrBadge.and": "AND", @@ -22397,6 +22715,7 @@ "xpack.securitySolution.anomaliesTable.table.anomaliesDescription": "異常", "xpack.securitySolution.anomaliesTable.table.anomaliesTooltip": "異常表はSIEMグローバルKQL検索でフィルタリングできません。", "xpack.securitySolution.anomaliesTable.table.showingDescription": "表示中", + "xpack.securitySolution.anomaliesTable.table.unit": "{totalCount, plural, other {異常}}", "xpack.securitySolution.artifactCard.comments.label.hide": "コメントを非表示({count})", "xpack.securitySolution.artifactCard.comments.label.show": "コメントを表示({count})", "xpack.securitySolution.artifactCard.conditions.and": "AND", @@ -22417,6 +22736,8 @@ "xpack.securitySolution.artifactCard.globalEffectScope": "グローバルに適用", "xpack.securitySolution.artifactCard.lastUpdated": "最終更新", "xpack.securitySolution.artifactCard.lastUpdatedBy": "更新者", + "xpack.securitySolution.artifactCard.policyEffectScope": "{count} {count, plural, other {ポリシー}}に適用", + "xpack.securitySolution.artifactCard.policyEffectScope.title": "次の{count, plural, other {ポリシー}}に適用", "xpack.securitySolution.artifactCardGrid.assignmentColumn": "割り当て", "xpack.securitySolution.artifactCardGrid.DescriptionColumn": "説明", "xpack.securitySolution.artifactCardGrid.expandCollapseLabel": "すべてのカードを{action}", @@ -22556,8 +22877,10 @@ "xpack.securitySolution.authenticationsTable.lastSuccessfulDestination": "前回成功したデスティネーション", "xpack.securitySolution.authenticationsTable.lastSuccessfulSource": "前回成功したソース", "xpack.securitySolution.authenticationsTable.lastSuccessfulTime": "前回の成功", + "xpack.securitySolution.authenticationsTable.rows": "{numRows} {numRows, plural, other {行}}", "xpack.securitySolution.authenticationsTable.successes": "成功", "xpack.securitySolution.authenticationsTable.uncommonProcessTable": "非共通プロセス", + "xpack.securitySolution.authenticationsTable.unit": "{totalCount, plural, other {ユーザー}}", "xpack.securitySolution.authenticationsTable.user": "ユーザー", "xpack.securitySolution.authz.mlUnavailable": "機械学習プラグインが使用できません。プラグインを有効にしてください。", "xpack.securitySolution.authz.userIsNotMlAdminMessage": "現在のユーザーは機械学習管理者ではありません。", @@ -22595,6 +22918,7 @@ "xpack.securitySolution.components.embeddables.indexPatternsMissingPrompt.errorTitle": "必要なインデックスパターンが構成されていません", "xpack.securitySolution.components.embeddables.mapToolTip.errorTitle": "マップ機能の読み込み中にエラーが発生", "xpack.securitySolution.components.embeddables.mapToolTip.filterForValueHoverAction": "値でフィルター", + "xpack.securitySolution.components.embeddables.mapToolTip.footerLabel": "{currentFeature}/{totalFeatures} {totalFeatures, plural, other {機能}}", "xpack.securitySolution.components.embeddables.mapToolTip.lineContent.clientLabel": "クライアント", "xpack.securitySolution.components.embeddables.mapToolTip.lineContent.destinationLabel": "送信先", "xpack.securitySolution.components.embeddables.mapToolTip.lineContent.serverLabel": "サーバー", @@ -22644,6 +22968,8 @@ "xpack.securitySolution.components.mlPopup.machineLearningLink": "機械学習", "xpack.securitySolution.components.mlPopup.mlJobSettingsButtonLabel": "MLジョブ設定", "xpack.securitySolution.components.mlPopup.moduleNotCompatibleDescription": "データが見つかりませんでした。機械学習ジョブ要件の詳細については、{mlDocs}を参照してください。", + "xpack.securitySolution.components.mlPopup.moduleNotCompatibleTitle": "{incompatibleJobCount} {incompatibleJobCount, plural, other {件のジョブ}}が現在使用できません。", + "xpack.securitySolution.components.mlPopup.showingLabel": "{filterResultsLength} 件の{filterResultsLength, plural, other {ジョブ}}を表示中", "xpack.securitySolution.components.mlPopup.upgradeButtonLabel": "サブスクリプションオプション", "xpack.securitySolution.components.mlPopup.upgradeDescription": "SIEMの異常検出機能にアクセスするには、ライセンスをプラチナに更新するか、30日間の無料トライアルを開始するか、AWS、GCP、またはAzureで{cloudLink}にサインアップしてください。その後、機械学習ジョブを実行して異常を表示できます。", "xpack.securitySolution.components.mlPopup.upgradeTitle": "E lastic Platinumへのアップグレード", @@ -22710,6 +23036,7 @@ "xpack.securitySolution.dataProviders.valuePlaceholder": "値", "xpack.securitySolution.detectionEngine.alerts.acknowledgedAlertFailedToastMessage": "アラートを確認済みに設定できませんでした", "xpack.securitySolution.detectionEngine.alerts.acknowledgedAlertsTitle": "認識", + "xpack.securitySolution.detectionEngine.alerts.acknowledgedAlertSuccessToastMessage": "{totalAlerts} {totalAlerts, plural, other {件のアラート}}を確認済みに設定しました。", "xpack.securitySolution.detectionEngine.alerts.actions.acknowledgedAlertTitle": "確認済みに設定", "xpack.securitySolution.detectionEngine.alerts.actions.addEndpointException": "エンドポイント例外の追加", "xpack.securitySolution.detectionEngine.alerts.actions.addEventFilter": "エンドポイントイベントフィルターを追加", @@ -22718,14 +23045,16 @@ "xpack.securitySolution.detectionEngine.alerts.actions.investigateInTimelineAriaLabel": "アラートをタイムラインに送信", "xpack.securitySolution.detectionEngine.alerts.actions.investigateInTimelineTitle": "タイムラインで調査", "xpack.securitySolution.detectionEngine.alerts.actions.openAlertTitle": "アラートを開く", - "xpack.securitySolution.detectionEngine.alerts.alertsUnit": "{totalCount, plural, other {アラート}}", + "xpack.securitySolution.detectionEngine.alerts.alertsUnit": "{totalCount, plural, other {アラート}}", "xpack.securitySolution.detectionEngine.alerts.closedAlertFailedToastMessage": "アラートをクローズできませんでした。", "xpack.securitySolution.detectionEngine.alerts.closedAlertsTitle": "終了", + "xpack.securitySolution.detectionEngine.alerts.closedAlertSuccessToastMessage": "{totalAlerts} {totalAlerts, plural, other {件のアラート}}を正常にクローズしました。", "xpack.securitySolution.detectionEngine.alerts.count.countTableColumnTitle": "カウント", "xpack.securitySolution.detectionEngine.alerts.count.countTableTitle": "カウント", "xpack.securitySolution.detectionEngine.alerts.documentTypeTitle": "アラート", "xpack.securitySolution.detectionEngine.alerts.histogram.allOthersGroupingLabel": "その他すべて", "xpack.securitySolution.detectionEngine.alerts.histogram.headerTitle": "傾向", + "xpack.securitySolution.detectionEngine.alerts.histogram.showingAlertsTitle": "{modifier}{totalAlertsFormatted} {totalAlerts, plural, other {件のアラート}}を表示しています", "xpack.securitySolution.detectionEngine.alerts.histogram.stackByOptions.stackByAriaLabel": "フィールド値でアラートヒストグラムを積み上げ", "xpack.securitySolution.detectionEngine.alerts.histogram.stackByOptions.stackByLabel": "積み上げ", "xpack.securitySolution.detectionEngine.alerts.histogram.stackByOptions.stackByPlaceholder": "積み上げるフィールドを選択", @@ -22735,6 +23064,7 @@ "xpack.securitySolution.detectionEngine.alerts.moreActionsAriaLabel": "さらにアクションを表示", "xpack.securitySolution.detectionEngine.alerts.openAlertsTitle": "開く", "xpack.securitySolution.detectionEngine.alerts.openedAlertFailedToastMessage": "アラートを開けませんでした", + "xpack.securitySolution.detectionEngine.alerts.openedAlertSuccessToastMessage": "{totalAlerts} {totalAlerts, plural, other {件のアラート}}を正常に開きました。", "xpack.securitySolution.detectionEngine.alerts.totalCountOfAlertsTitle": "アラート", "xpack.securitySolution.detectionEngine.alerts.updateAlertStatusFailedSingleAlert": "アラートを更新できませんでした。アラートはすでに修正されています。", "xpack.securitySolution.detectionEngine.alerts.utilityBar.additionalFiltersActions.showBuildingBlockTitle": "基本アラートを含める", @@ -22748,6 +23078,9 @@ "xpack.securitySolution.detectionEngine.alerts.utilityBar.batchActions.viewSelectedInTimelineTitle": "タイムラインで選択した項目を表示", "xpack.securitySolution.detectionEngine.alerts.utilityBar.batchActionsTitle": "バッチ処理", "xpack.securitySolution.detectionEngine.alerts.utilityBar.clearSelectionTitle": "選択した項目をクリア", + "xpack.securitySolution.detectionEngine.alerts.utilityBar.selectAllAlertsTitle": "すべての{totalAlertsFormatted} {totalAlerts, plural, other {件のアラート}}を選択", + "xpack.securitySolution.detectionEngine.alerts.utilityBar.selectedAlertsTitle": "Selected {selectedAlertsFormatted} {selectedAlerts, plural, other {件のアラート}}", + "xpack.securitySolution.detectionEngine.alerts.utilityBar.showingAlertsTitle": "すべての{totalAlertsFormatted} {totalAlerts, plural, other {件のアラート}}を表示しています", "xpack.securitySolution.detectionEngine.alerts.utilityBar.takeActionTitle": "アクションを実行", "xpack.securitySolution.detectionEngine.alertTitle": "アラート", "xpack.securitySolution.detectionEngine.buttonManageRules": "ルールの管理", @@ -22770,6 +23103,8 @@ "xpack.securitySolution.detectionEngine.components.allRules.bulkActions.bulkEditFlyoutForm.deleteTagsTitle": "タグを削除", "xpack.securitySolution.detectionEngine.components.allRules.bulkActions.bulkEditFlyoutForm.indexPatternsRequiredErrorMessage": "インデックスパターンが最低1つ必要です。", "xpack.securitySolution.detectionEngine.components.allRules.bulkActions.bulkEditFlyoutForm.saveButtonLabel": "保存", + "xpack.securitySolution.detectionEngine.components.allRules.bulkActions.bulkEditFlyoutForm.setIndexPatternsWarningCallout": "{rulesCount, plural, other {# 個の選択したルール}} のインデックスパターンを上書きしようとしています。[保存]をクリックすると、変更が適用されます。", + "xpack.securitySolution.detectionEngine.components.allRules.bulkActions.bulkEditFlyoutForm.setTagsWarningCallout": "{rulesCount, plural, other {# 個の選択したルール}}のタグを上書きしようとしています。[保存]をクリックすると、変更が適用されます。\n", "xpack.securitySolution.detectionEngine.components.allRules.bulkActions.bulkEditFlyoutForm.tagsComoboxRequiredErrorMessage": "1つ以上のタグが必要です。", "xpack.securitySolution.detectionEngine.components.allRules.deleteConfirmationBody": "現在のフィルタークエリと一致するすべてのルールが削除されます。[確認]をクリックすると続行します。", "xpack.securitySolution.detectionEngine.components.allRules.deleteConfirmationCancel": "キャンセル", @@ -22779,12 +23114,16 @@ "xpack.securitySolution.detectionEngine.components.allRules.refreshPromptConfirm": "続行", "xpack.securitySolution.detectionEngine.components.allRules.refreshPromptTitle": "応答してください。", "xpack.securitySolution.detectionEngine.components.importRuleModal.cancelTitle": "キャンセル", + "xpack.securitySolution.detectionEngine.components.importRuleModal.exceptionsSuccessLabel": "{totalExceptions} {totalExceptions, plural, other {個の例外}}が正常にインポートされました。", + "xpack.securitySolution.detectionEngine.components.importRuleModal.importExceptionsFailedLabel": "{totalExceptions} {totalExceptions, plural, other {個の例外}}をインポートできませんでした", "xpack.securitySolution.detectionEngine.components.importRuleModal.importFailedDetailedTitle": "{message}", + "xpack.securitySolution.detectionEngine.components.importRuleModal.importFailedTitle": "{totalRules} {totalRules, plural, other {個のルール}}をインポートできませんでした", "xpack.securitySolution.detectionEngine.components.importRuleModal.importRuleTitle": "インポート", "xpack.securitySolution.detectionEngine.components.importRuleModal.initialPromptTextDescription": "有効なrules_export.ndjsonファイルを選択するか、ドラッグしてドロップします", "xpack.securitySolution.detectionEngine.components.importRuleModal.overwriteDescription": "競合するルールIDで既存の検出を上書き", "xpack.securitySolution.detectionEngine.components.importRuleModal.overwriteExceptionLabel": "競合する「list_id」で既存の例外リストを上書き", "xpack.securitySolution.detectionEngine.components.importRuleModal.selectRuleDescription": "インポートするルールを選択します。関連付けられたルールアクションと例外を含めることができます。", + "xpack.securitySolution.detectionEngine.components.importRuleModal.successfullyImportedRulesTitle": "{totalRules} {totalRules, plural, other {ルール}}を正常にインポートしました", "xpack.securitySolution.detectionEngine.createRule. stepScheduleRule.completeWithActivatingTitle": "ルールの作成と有効化", "xpack.securitySolution.detectionEngine.createRule. stepScheduleRule.completeWithoutActivatingTitle": "有効化せずにルールを作成", "xpack.securitySolution.detectionEngine.createRule.backToRulesButton": "ルール", @@ -23533,6 +23872,7 @@ "xpack.securitySolution.detectionEngine.mlJobCompatibilityCallout.messageBody.summary": "機械学習ルールは、Elastic Beatsによって入力されたデータフィールドに依存するMLジョブと、MLジョブが作成されたときに最新であったエージェント統合を指定します。V2というプレフィックスが付いた新しいMLジョブは、現時点で最新のECSフィールドで動作するように更新されています。ルール対応の継続を保証するには、複数のバージョンのBeatsとエージェントを使用している場合は、新しいML(V2)ジョブを指定する新しい機械学習ルールを作成し、既存の機械学習ルールと一緒に実行できるようにする必要があります。", "xpack.securitySolution.detectionEngine.mlJobCompatibilityCallout.messageTitle": "MLジョブはデータソースまたはMLルールと互換性がない可能性があります", "xpack.securitySolution.detectionEngine.mlRulesDisabledMessageTitle": "MLルールにはプラチナライセンスとML管理者権限が必要です", + "xpack.securitySolution.detectionEngine.mlUnavailableTitle": "{totalRules} {totalRules, plural, other {個のルール}}で機械学習を有効にする必要があります。", "xpack.securitySolution.detectionEngine.needAdminForUpdateCallOutBody.messageBody.essenceDescription": "現在、アラートデータを自動移行するための必要な権限がありません。アラートデータを自動移行するには、管理者にこのページを一度確認するように依頼してください。", "xpack.securitySolution.detectionEngine.needAdminForUpdateCallOutBody.messageBody.messageDetail": "{essence} 関連ドキュメント:{docs}", "xpack.securitySolution.detectionEngine.needAdminForUpdateCallOutBody.messageTitle": "アラート移行に必要な管理者権限", @@ -23557,6 +23897,8 @@ "xpack.securitySolution.detectionEngine.queryPreview.queryNoHits": "ヒットが見つかりませんでした。", "xpack.securitySolution.detectionEngine.queryPreview.queryPreviewEqlSequenceDescription": "現在 EQL シーケンスクエリではヒストグラムを使用できません。右上の調査を使用して、クエリ詳細を表示できます。", "xpack.securitySolution.detectionEngine.queryPreview.queryPreviewEqlSequenceTitle": "ヒストグラムがありません", + "xpack.securitySolution.detectionEngine.queryPreview.queryPreviewGraphThresholdWithFieldTitle": "{buckets} {buckets, plural, other {固有のヒット}}", + "xpack.securitySolution.detectionEngine.queryPreview.queryPreviewGraphTitle": "{hits} {hits, plural, other {ヒット}}", "xpack.securitySolution.detectionEngine.queryPreview.queryPreviewHelpText": "クエリ結果をプレビューするデータのタイムフレームを選択します", "xpack.securitySolution.detectionEngine.queryPreview.queryPreviewLabel": "クイッククエリプレビュー", "xpack.securitySolution.detectionEngine.queryPreview.queryPreviewSeeAllErrors": "すべてのエラーを表示", @@ -23597,6 +23939,7 @@ "xpack.securitySolution.detectionEngine.rules.all.exceptions.listName": "名前", "xpack.securitySolution.detectionEngine.rules.all.exceptions.numberRulesAssignedTitle": "割り当てられたルール数", "xpack.securitySolution.detectionEngine.rules.all.exceptions.rulesAssignedTitle": "割り当てられたルール", + "xpack.securitySolution.detectionEngine.rules.all.exceptions.rulesPopoverButton": "+{rulesCount} {rulesCount, plural, other {ルール}}", "xpack.securitySolution.detectionEngine.rules.allExceptionLists.filters.noExceptionsTitle": "例外リストが見つかりません", "xpack.securitySolution.detectionEngine.rules.allExceptionLists.search.placeholder": "検索例外リスト", "xpack.securitySolution.detectionEngine.rules.allExceptions.filters.noListsBody": "例外リストが見つかりませんでした。", @@ -23610,13 +23953,21 @@ "xpack.securitySolution.detectionEngine.rules.allRules.actions.editRuleSettingsToolTip": "Kibana アクション特権がありません", "xpack.securitySolution.detectionEngine.rules.allRules.actions.exportRuleDescription": "ルールのエクスポート", "xpack.securitySolution.detectionEngine.rules.allRules.activeRuleDescription": "アクティブ", + "xpack.securitySolution.detectionEngine.rules.allRules.batchActions.activateSelectedErrorTitle": "{totalRules, plural, other {個のルール}}の有効化エラー", + "xpack.securitySolution.detectionEngine.rules.allRules.batchActions.deactivateSelectedErrorTitle": "{totalRules, plural, other {個のルール}}の無効化エラー", + "xpack.securitySolution.detectionEngine.rules.allRules.batchActions.deleteSelectedErrorTitle": "{totalRules, plural, other {ルール}}の削除エラー", "xpack.securitySolution.detectionEngine.rules.allRules.batchActions.deleteSelectedImmutableTitle": "選択には削除できないイミュータブルルールがあります", "xpack.securitySolution.detectionEngine.rules.allRules.batchActionsTitle": "一斉アクション", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.addIndexPatternsTitle": "インデックスパターンを追加", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.addTagsTitle": "タグを追加", + "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.bulkEditConfirmationDescription": "更新アクションは、選択した{customRulesCount, plural, other {#個のカスタムルール}}にのみ適用されます。", + "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.bulkEditConfirmationTitle": "{elasticRulesCount, plural, other {#個のElasticルール}}を編集できません", + "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.bulkEditErrorToastDescription": "{rulesCount, plural, other {#個のルール}}を更新できませんでした。", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.bulkEditErrorToastTitle": "ルールの更新が失敗しました", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.bulkEditRejectionDescription": "Elasticルールは変更できません。更新アクションはカスタムルールにのみ適用されます。", + "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.bulkEditSuccessToastDescription": "{rulesCount, plural, other {#個のルール}}を正常に更新しました。", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.bulkEditSuccessToastTitle": "ルール変更が更新されました", + "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.bulkEditWarningToastDescription": "{rulesCount, plural, other {#個のルール}}を更新しています。", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.bulkEditWarningToastNotifyButtonLabel": "完了時に通知", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.bulkEditWarningToastTitle": "ルールを更新しています", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.contextMenuTitle": "オプション", @@ -23657,6 +24008,12 @@ "xpack.securitySolution.detectionEngine.rules.allRules.refreshTitle": "更新", "xpack.securitySolution.detectionEngine.rules.allRules.searchAriaLabel": "ルールの検索", "xpack.securitySolution.detectionEngine.rules.allRules.searchPlaceholder": "例:ルール名", + "xpack.securitySolution.detectionEngine.rules.allRules.selectAllRulesTitle": "すべての{totalRules} {totalRules, plural, other {個のルール}}を選択", + "xpack.securitySolution.detectionEngine.rules.allRules.selectedRulesTitle": "{selectedRules} {selectedRules, plural, other {ルール}}を選択しました", + "xpack.securitySolution.detectionEngine.rules.allRules.showingExceptionLists": "{totalLists} {totalLists, plural, other {件のリスト}}を表示しています。", + "xpack.securitySolution.detectionEngine.rules.allRules.showingRulesTitle": "{totalRules} {totalRules, plural, other {ルール}}を表示中", + "xpack.securitySolution.detectionEngine.rules.allRules.successfullyDuplicatedRulesTitle": "{totalRules, plural, other {{totalRules}ルール}}を正常に複製しました", + "xpack.securitySolution.detectionEngine.rules.allRules.successfullyExportedXofYRulesTitle": "{exportedRules}/{totalRules} {totalRules, plural, other {件のルール}}を正常にエクスポートしました事前構築済みルールは結果のファイルから除外されました。", "xpack.securitySolution.detectionEngine.rules.allRules.tableTitle": "すべてのルール", "xpack.securitySolution.detectionEngine.rules.allRules.tabs.exceptions": "例外リスト", "xpack.securitySolution.detectionEngine.rules.allRules.tabs.monitoring": "ルール監視", @@ -23685,6 +24042,9 @@ "xpack.securitySolution.detectionEngine.rules.refreshRulePopoverDescription": "表の自動更新", "xpack.securitySolution.detectionEngine.rules.refreshRulePopoverLabel": "自動更新設定", "xpack.securitySolution.detectionEngine.rules.releaseNotesHelp": "リリースノート", + "xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedRulesAndTimelinesButton": "{missingRules} Elastic事前構築済み{missingRules, plural, other {ルール}}と{missingTimelines} Elastic事前構築済み{missingTimelines, plural, other {タイムライン}}をインストール ", + "xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedRulesButton": "{missingRules} Elasticの事前構築済みの{missingRules, plural, other {個のルール}}をインストール ", + "xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedTimelinesButton": "{missingTimelines} Elasticの事前構築済みの{missingTimelines, plural, other {個のタイムライン}}をインストール ", "xpack.securitySolution.detectionEngine.rules.ruleActionsTitle": "ルールアクション", "xpack.securitySolution.detectionEngine.rules.scheduleRuleTitle": "ルールのスケジュール", "xpack.securitySolution.detectionEngine.rules.stepAboutTitle": "概要", @@ -23693,7 +24053,13 @@ "xpack.securitySolution.detectionEngine.rules.stepScheduleTitle": "スケジュール", "xpack.securitySolution.detectionEngine.rules.update.successfullySavedRuleTitle": "{ruleName}が保存されました", "xpack.securitySolution.detectionEngine.rules.updateButtonTitle": "更新", + "xpack.securitySolution.detectionEngine.rules.updatePrePackagedRulesAndTimelinesButton": "{updateRules} Elasticの事前構築済みの{updateRules, plural, other {個のルール}}と{updateTimelines} Elasticの事前構築済みの{updateTimelines, plural, other {個のタイムライン}}を更新", + "xpack.securitySolution.detectionEngine.rules.updatePrePackagedRulesAndTimelinesMsg": "{updateRules} Elasticの事前構築済みの{updateRules, plural, other {個のルール}}と{updateTimelines} Elasticの事前構築済みの{updateTimelines, plural, other {個のタイムライン}}を更新できます。これにより、削除されたElastic事前構築済みルールが再読み込みされます。", + "xpack.securitySolution.detectionEngine.rules.updatePrePackagedRulesButton": "{updateRules} Elastic事前構築済み{updateRules, plural, other {ルール}}を更新する", + "xpack.securitySolution.detectionEngine.rules.updatePrePackagedRulesMsg": "{updateRules} Elastic事前構築済み{updateRules, plural, other {ルール}}を更新することができます", "xpack.securitySolution.detectionEngine.rules.updatePrePackagedRulesTitle": "Elastic事前構築済みルールまたはタイムラインテンプレートを更新", + "xpack.securitySolution.detectionEngine.rules.updatePrePackagedTimelinesButton": "{updateTimelines} Elastic事前構築済み{updateTimelines, plural, other {タイムライン}}を更新", + "xpack.securitySolution.detectionEngine.rules.updatePrePackagedTimelinesMsg": "{updateTimelines} Elastic事前構築済み{updateTimelines, plural, other {タイムライン}}を更新できます。", "xpack.securitySolution.detectionEngine.ruleStatus.errorCalloutTitle": "ルール失敗", "xpack.securitySolution.detectionEngine.ruleStatus.partialErrorCalloutTitle": "警告", "xpack.securitySolution.detectionEngine.ruleStatus.refreshButton": "更新", @@ -23832,12 +24198,14 @@ "xpack.securitySolution.endpoint.hostisolation.isolate": "分離", "xpack.securitySolution.endpoint.hostIsolation.isolated": "分離済み", "xpack.securitySolution.endpoint.hostIsolation.isolateHost": "ホストの分離", + "xpack.securitySolution.endpoint.hostIsolation.isolateHost.casesAssociatedWithAlert": "{caseCount} {caseCount, plural, other {個のケース}}がこのホストに関連付けられています", "xpack.securitySolution.endpoint.hostIsolation.isolateThisHost": "ホスト{hostName}をネットワークから分離します。", "xpack.securitySolution.endpoint.hostIsolation.isolateThisHostAbout": "ホストの分離により、ホストがネットワークから切断されます。ホストはKibanaプラットフォームとのみ通信できます。", "xpack.securitySolution.endpoint.hostIsolation.isolation.successfulMessage": "ホスト{hostName}での分離は正常に送信されました", "xpack.securitySolution.endpoint.hostIsolation.notIsolated": "分離されていない", "xpack.securitySolution.endpoint.hostIsolation.placeholderCase": "{caseName}", "xpack.securitySolution.endpoint.hostIsolation.returnToAlertDetails": "アラート詳細に戻る", + "xpack.securitySolution.endpoint.hostIsolation.successfulIsolation.cases": "このアクションは次の{caseCount, plural, other {個のケース}}に関連付けられました。", "xpack.securitySolution.endpoint.hostIsolation.successProceedButton": "エンドポイント詳細に戻る", "xpack.securitySolution.endpoint.hostisolation.unisolate": "リリース", "xpack.securitySolution.endpoint.hostIsolation.unisolate.successfulMessage": "ホスト{hostName}でのリリースは正常に送信されました", @@ -23876,6 +24244,8 @@ "xpack.securitySolution.endpoint.list.stepOneTitle": "使用する統合を選択", "xpack.securitySolution.endpoint.list.stepTwo": "開始するために必要なコマンドが提供されます。", "xpack.securitySolution.endpoint.list.stepTwoTitle": "Fleet 経由で Endpoint Security によって有効にされたエージェントを登録", + "xpack.securitySolution.endpoint.list.totalCount": "{totalItemCount, plural, other {# 個のエンドポイント}}を表示しています", + "xpack.securitySolution.endpoint.list.totalCount.limited": "{totalItemCount, plural, other {# 個のエンドポイント}}の{limit}を表示しています", "xpack.securitySolution.endpoint.list.transformFailed.dismiss": "閉じる", "xpack.securitySolution.endpoint.list.transformFailed.docsLink": "トラブルシューティングドキュメンテーション", "xpack.securitySolution.endpoint.list.transformFailed.message": "現在、必須の変換{transformId}が失敗しています。通常、これは{transformsPage}で修正できます。ヘルプについては、{docsPage}をご覧ください", @@ -24013,6 +24383,7 @@ "xpack.securitySolution.endpoint.policy.eventFilters.empty.unexisting.action": "イベントフィルターを追加", "xpack.securitySolution.endpoint.policy.eventFilters.empty.unexisting.content": "現在、エンドポイントにはイベントフィルターが適用されていません。", "xpack.securitySolution.endpoint.policy.eventFilters.empty.unexisting.title": "イベントフィルターが存在しません", + "xpack.securitySolution.endpoint.policy.eventFilters.layout.about": "{count, plural, other {}} {count}個のイベント{count, plural, other {フィルター}}がこのポリシーに関連付けられています。ここをクリックすると、{link}", "xpack.securitySolution.endpoint.policy.eventFilters.layout.about.viewAllLinkLabel": "すべてのイベントフィルターを表示", "xpack.securitySolution.endpoint.policy.eventFilters.layout.assignToPolicy": "イベントフィルターをポリシーに割り当て", "xpack.securitySolution.endpoint.policy.eventFilters.layout.flyout.cancel": "キャンセル", @@ -24040,6 +24411,7 @@ "xpack.securitySolution.endpoint.policy.eventFilters.list.removeDialog.successToastTitle": "正常に削除されました", "xpack.securitySolution.endpoint.policy.eventFilters.list.removeDialog.title": "ポリシーからイベントフィルターを削除", "xpack.securitySolution.endpoint.policy.eventFilters.list.search.placeholder": "次のフィールドで検索:名前、説明、コメント、値", + "xpack.securitySolution.endpoint.policy.eventFilters.list.totalItemCount": "{totalItemsCount, plural, other {#個のイベントフィルター}}を表示中", "xpack.securitySolution.endpoint.policy.hostIsolationExceptions.empty.unassigned.backButtonLabel": "{policyName}ポリシー", "xpack.securitySolution.endpoint.policy.hostIsolationExceptions.empty.unassigned.content": "現在{policyName}に割り当てられたホスト分離例外はありません。今すぐ例外を割り当てるか、ホスト分離例外ページで追加して管理してください。", "xpack.securitySolution.endpoint.policy.hostIsolationExceptions.empty.unassigned.primaryAction": "ホスト分離例外の割り当て", @@ -24062,6 +24434,7 @@ "xpack.securitySolution.endpoint.policy.hostIsolationExceptions.layout.flyout.toastSuccess.textSingle": "\"{name}\"はホスト分離例外リストに追加されました。", "xpack.securitySolution.endpoint.policy.hostIsolationExceptions.layout.flyout.toastSuccess.title": "成功", "xpack.securitySolution.endpoint.policy.hostIsolationExceptions.layout.searh.label": "ホスト分離例外の検索", + "xpack.securitySolution.endpoint.policy.hostIsolationExceptions.list.about": "{count, plural, other {}} {count}個の{count, plural, other {ホスト分離例外}}がこのポリシーに関連付けられています。ここをクリックすると、{link}", "xpack.securitySolution.endpoint.policy.hostIsolationExceptions.list.fullDetailsAction": "詳細を表示", "xpack.securitySolution.endpoint.policy.hostIsolationExceptions.list.removeAction": "ポリシーから削除", "xpack.securitySolution.endpoint.policy.hostIsolationExceptions.list.removeActionNotAllowed": "グローバルで適用されたホスト分離例外はポリシーから削除できません。", @@ -24074,6 +24447,7 @@ "xpack.securitySolution.endpoint.policy.hostIsolationExceptions.list.removeDialog.title": "ポリシーからホスト分離例外を削除", "xpack.securitySolution.endpoint.policy.hostIsolationExceptions.list.search.placeholder": "次のフィールドで検索:名前、説明、IP", "xpack.securitySolution.endpoint.policy.hostIsolationExceptions.list.title": "割り当てられたホスト分離例外", + "xpack.securitySolution.endpoint.policy.hostIsolationExceptions.list.totalItemCount": "{totalItemsCount, plural, other {#個のホスト分離例外}}を表示中", "xpack.securitySolution.endpoint.policy.hostIsolationExceptions.list.viewAllLinkLabel": "ホスト分離例外を表示", "xpack.securitySolution.endpoint.policy.protections.behavior": "悪意ある動作に対する保護", "xpack.securitySolution.endpoint.policy.protections.malware": "マルウェア保護", @@ -24087,6 +24461,7 @@ "xpack.securitySolution.endpoint.policy.trustedApps.empty.unexisting.action": "信頼できるアプリケーションを追加", "xpack.securitySolution.endpoint.policy.trustedApps.empty.unexisting.content": "現在、エンドポイントには信頼できるアプリケーションが適用されていません。", "xpack.securitySolution.endpoint.policy.trustedApps.empty.unexisting.title": "信頼できるアプリケーションが存在しません", + "xpack.securitySolution.endpoint.policy.trustedApps.layout.about": "{count, plural, other {}} {count}個の信頼できる{count, plural, other {アプリケーション}}がこのポリシーに関連付けられています。\nここをクリックすると、{link}", "xpack.securitySolution.endpoint.policy.trustedApps.layout.about.viewAllLinkLabel": "信頼できるアプリケーションを表示", "xpack.securitySolution.endpoint.policy.trustedApps.layout.assignToPolicy": "信頼できるアプリケーションをポリシーに割り当てる", "xpack.securitySolution.endpoint.policy.trustedApps.layout.flyout.cancel": "キャンセル", @@ -24273,6 +24648,7 @@ "xpack.securitySolution.eventFilters.commentsSectionTitle": "コメント", "xpack.securitySolution.eventFilters.criteriaSectionDescription": "オペレーティングシステムを選択して、条件を追加します。", "xpack.securitySolution.eventFilters.criteriaSectionTitle": "条件", + "xpack.securitySolution.eventFilters.deletionDialog.calloutMessage": "このエントリを削除すると、{count}個の関連付けられた{count, plural, other {ポリシー}}から削除されます。", "xpack.securitySolution.eventFilters.deletionDialog.calloutTitle": "警告", "xpack.securitySolution.eventFilters.deletionDialog.cancelButton": "キャンセル", "xpack.securitySolution.eventFilters.deletionDialog.confirmButton": "削除", @@ -24296,6 +24672,7 @@ "xpack.securitySolution.eventFilters.list.cardAction.edit": "イベントフィルターを編集", "xpack.securitySolution.eventFilters.list.pageAddButton": "イベントフィルターを追加", "xpack.securitySolution.eventFilters.list.pageTitle": "イベントフィルター", + "xpack.securitySolution.eventFilters.list.totalCount": "{total, plural, other {# 個のイベントフィルター}}を表示中", "xpack.securitySolution.eventFilters.listEmpty.addButton": "イベントフィルターを追加", "xpack.securitySolution.eventFilters.listEmpty.message": "イベントフィルターを追加して、大量のイベントや不要なイベントがElasticsearchに書き込まれないように除外します。", "xpack.securitySolution.eventFilters.listEmpty.title": "最初のイベントフィルターを追加", @@ -24361,6 +24738,7 @@ "xpack.securitySolution.eventsViewer.errorFetchingEventsData": "イベントデータをクエリできませんでした", "xpack.securitySolution.eventsViewer.eventsLabel": "イベント", "xpack.securitySolution.eventsViewer.showingLabel": "表示中", + "xpack.securitySolution.eventsViewer.unit": "{totalCount, plural, other {イベント}}", "xpack.securitySolution.exceptions.addException.addEndpointException": "エンドポイント例外の追加", "xpack.securitySolution.exceptions.addException.addException": "ルール例外の追加", "xpack.securitySolution.exceptions.addException.bulkCloseLabel": "この例外一致し、このルールによって生成された、すべてのアラートを閉じる", @@ -24407,6 +24785,7 @@ "xpack.securitySolution.exceptions.fetch404Error": "関連付けられた例外リスト({listId})は存在しません。その他の例外を検出ルールに追加するには、見つからない例外リストを削除してください。", "xpack.securitySolution.exceptions.fetchError": "例外リストの取得エラー", "xpack.securitySolution.exceptions.fieldDescription": "フィールド", + "xpack.securitySolution.exceptions.hideCommentsLabel": "({comments}){comments, plural, other {件のコメント}}を非表示", "xpack.securitySolution.exceptions.modalErrorAccordionText": "ルール参照情報を表示:", "xpack.securitySolution.exceptions.modifiedByLabel": "変更者:", "xpack.securitySolution.exceptions.nameLabel": "名前", @@ -24421,10 +24800,13 @@ "xpack.securitySolution.exceptions.paginationNumberOfItemsLabel": "{items}個の項目", "xpack.securitySolution.exceptions.referenceModalCancelButton": "キャンセル", "xpack.securitySolution.exceptions.referenceModalDeleteButton": "例外リストを削除", + "xpack.securitySolution.exceptions.referenceModalDescription": "この例外リストは、({referenceCount}) {referenceCount, plural, other {個のルール}}に関連付けられています。この例外リストを削除すると、関連付けられたルールからの参照も削除されます。", "xpack.securitySolution.exceptions.referenceModalSuccessDescription": "例外リスト{listId}が正常に削除されました。", "xpack.securitySolution.exceptions.referenceModalTitle": "例外リストを削除", "xpack.securitySolution.exceptions.removeButtonLabel": "削除", "xpack.securitySolution.exceptions.searchPlaceholder": "例:例外リスト名", + "xpack.securitySolution.exceptions.showCommentsLabel": "({comments}){comments, plural, other {件のコメント}}を表示", + "xpack.securitySolution.exceptions.utilityNumberExceptionsLabel": "{items} {items, plural, other {件の例外}}を表示しています", "xpack.securitySolution.exceptions.utilityRefreshLabel": "更新", "xpack.securitySolution.exceptions.valueDescription": "値", "xpack.securitySolution.exceptions.viewer.addCommentPlaceholder": "新しいコメントを追加...", @@ -24532,6 +24914,7 @@ "xpack.securitySolution.host.details.overview.regionTitle": "地域", "xpack.securitySolution.host.details.versionLabel": "バージョン", "xpack.securitySolution.hostIsolation.agentStatuses.empty": "-", + "xpack.securitySolution.hostIsolationExceptions.deletionDialog.calloutMessage": "このエントリを削除すると、{count}個の関連付けられた{count, plural, other {ポリシー}}から削除されます。", "xpack.securitySolution.hostIsolationExceptions.deletionDialog.calloutTitle": "警告", "xpack.securitySolution.hostIsolationExceptions.deletionDialog.cancel": "キャンセル", "xpack.securitySolution.hostIsolationExceptions.deletionDialog.confirmation": "この操作は元に戻すことができません。続行していいですか?", @@ -24566,6 +24949,7 @@ "xpack.securitySolution.hostIsolationExceptions.list.addButton": "ホスト分離例外を追加", "xpack.securitySolution.hostIsolationExceptions.list.pageSubTitle": "ホスト分離例外を追加して、分離されたホストが特定のIPと通信することを許可します。", "xpack.securitySolution.hostIsolationExceptions.list.pageTitle": "ホスト分離例外", + "xpack.securitySolution.hostIsolationExceptions.list.totalCount": "{total, plural, other {#個のホスト分離例外}}を表示中", "xpack.securitySolution.hostIsolationExceptions.listEmpty.addButton": "ホスト分離例外を追加", "xpack.securitySolution.hostIsolationExceptions.listEmpty.message": "ホスト分離例外を追加して、分離されたホストが特定のIPと通信することを許可します。", "xpack.securitySolution.hostIsolationExceptions.listEmpty.title": "最初のホスト分離例外を追加", @@ -24597,6 +24981,7 @@ "xpack.securitySolution.hosts.navigation.eventsTitle": "イベント", "xpack.securitySolution.hosts.navigation.hostRisk": "リスク別ホスト", "xpack.securitySolution.hosts.navigation.uncommonProcessesTitle": "非共通プロセス", + "xpack.securitySolution.hosts.navigaton.eventsUnit": "{totalCount, plural, other {イベント}}", "xpack.securitySolution.hosts.navigaton.hostRisk.viewDashboardButtonLabel": "ソースダッシュボードを表示", "xpack.securitySolution.hosts.navigaton.matrixHistogram.errorFetchingAuthenticationsData": "認証データをクエリできませんでした", "xpack.securitySolution.hosts.navigaton.matrixHistogram.errorFetchingEventsData": "イベントデータをクエリできませんでした", @@ -24626,6 +25011,8 @@ "xpack.securitySolution.hostsTable.nameTitle": "ホスト名", "xpack.securitySolution.hostsTable.osLastSeenToolTip": "前回観察されたオペレーティングシステム", "xpack.securitySolution.hostsTable.osTitle": "オペレーティングシステム", + "xpack.securitySolution.hostsTable.rows": "{numRows} {numRows, plural, other {行}}", + "xpack.securitySolution.hostsTable.unit": "{totalCount, plural, other {ホスト}}", "xpack.securitySolution.hostsTable.versionTitle": "バージョン", "xpack.securitySolution.hoverActions.showTopTooltip": "上位の{fieldName}を表示", "xpack.securitySolution.indexPatterns.add": "インデックスパターンを追加", @@ -24685,7 +25072,9 @@ "xpack.securitySolution.kpiHost.hostRiskData": "ホストリスクデータ", "xpack.securitySolution.kpiHost.learnMore": "詳細", "xpack.securitySolution.kpiHosts.hosts.title": "ホスト", + "xpack.securitySolution.kpiHosts.riskyHosts.description": "{formattedQuantity} Risky {quantity, plural, other {ホスト}}", "xpack.securitySolution.kpiHosts.riskyHosts.errorMessage": "高リスクホストAPIの取得エラー", + "xpack.securitySolution.kpiHosts.riskyHosts.hostsCount": "{quantity} {quantity, plural, other {ホスト}}", "xpack.securitySolution.kpiHosts.riskyHosts.inspectTitle": "KPI高リスクホスト", "xpack.securitySolution.kpiHosts.riskyHosts.title": "高リスクホスト", "xpack.securitySolution.kpiHosts.uniqueIps.destinationChartLabel": "Dest.", @@ -24717,6 +25106,7 @@ "xpack.securitySolution.lists.detectionEngine.rules.uploadValueListsButtonTooltip": "値リストを使用して、フィールド値がリストの値と一致したときに例外を作成します", "xpack.securitySolution.lists.referenceModalCancelButton": "キャンセル", "xpack.securitySolution.lists.referenceModalDeleteButton": "値リストの削除", + "xpack.securitySolution.lists.referenceModalDescription": "この値リストは、({referenceCount})例外{referenceCount, plural, other {リスト}}に関連付けられています。このリストを削除すると、この値リストを参照するすべての例外アイテムが削除されます。", "xpack.securitySolution.lists.referenceModalTitle": "値リストの削除", "xpack.securitySolution.lists.uploadValueListDescription": "ルール例外の書き込み中に使用する単一値リストをアップロードします。", "xpack.securitySolution.lists.uploadValueListExtensionValidationMessage": "ファイルは次の種類のいずれかでなければなりません:[{fileTypes}]", @@ -24811,12 +25201,16 @@ "xpack.securitySolution.network.ipDetails.tlsTable.columns.sha1FingerPrintTitle": "SHA1フィンガープリント", "xpack.securitySolution.network.ipDetails.tlsTable.columns.subjectTitle": "件名", "xpack.securitySolution.network.ipDetails.tlsTable.columns.validUntilTitle": "有効期限:", + "xpack.securitySolution.network.ipDetails.tlsTable.rows": "{numRows} {numRows, plural, other {行}}", "xpack.securitySolution.network.ipDetails.tlsTable.transportLayerSecurityTitle": "トランスポートレイヤーセキュリティ", + "xpack.securitySolution.network.ipDetails.tlsTable.unit": "{totalCount, plural, other {サーバー証明書}}", "xpack.securitySolution.network.ipDetails.usersTable.columns.documentCountTitle": "ドキュメントカウント", "xpack.securitySolution.network.ipDetails.usersTable.columns.groupIdTitle": "グループ ID", "xpack.securitySolution.network.ipDetails.usersTable.columns.groupNameTitle": "グループ名", "xpack.securitySolution.network.ipDetails.usersTable.columns.userIdTitle": "ID", "xpack.securitySolution.network.ipDetails.usersTable.columns.userNameTitle": "ユーザー", + "xpack.securitySolution.network.ipDetails.usersTable.rows": "{numRows} {numRows, plural, other {行}}", + "xpack.securitySolution.network.ipDetails.usersTable.unit": "{totalCount, plural, other {ユーザー}}", "xpack.securitySolution.network.ipDetails.usersTable.usersTitle": "ユーザー", "xpack.securitySolution.network.kqlPlaceholder": "例:source.ip:\"foo\"", "xpack.securitySolution.network.navigation.alertsTitle": "外部アラート", @@ -24836,8 +25230,10 @@ "xpack.securitySolution.networkDnsTable.column.TotalQueriesTitle": "クエリ合計", "xpack.securitySolution.networkDnsTable.column.uniqueDomainsTitle": "固有のドメイン", "xpack.securitySolution.networkDnsTable.helperTooltip": "これは DNS プロトコルトラフィックのみを示しており、DNS データ逆浸出に使用されたドメインの調査に役立ちます。", + "xpack.securitySolution.networkDnsTable.rows": "{numRows} {numRows, plural, other {行}}", "xpack.securitySolution.networkDnsTable.select.includePtrRecords": "PTR 記録を含める", "xpack.securitySolution.networkDnsTable.title": "トップ DNS ドメイン", + "xpack.securitySolution.networkDnsTable.unit": "{totalCount, plural, other {ドメイン}}", "xpack.securitySolution.networkHttp.errorSearchDescription": "ネットワークHTTP検索でエラーが発生しました", "xpack.securitySolution.networkHttp.failSearchDescription": "ネットワークHTTPで検索を実行できませんでした", "xpack.securitySolution.networkHttpTable.column.domainTitle": "ドメイン", @@ -24847,7 +25243,9 @@ "xpack.securitySolution.networkHttpTable.column.pathTitle": "パス", "xpack.securitySolution.networkHttpTable.column.requestsTitle": "リクエスト", "xpack.securitySolution.networkHttpTable.column.statusTitle": "ステータス", + "xpack.securitySolution.networkHttpTable.rows": "{numRows} {numRows, plural, other {行}}", "xpack.securitySolution.networkHttpTable.title": "HTTPリクエスト", + "xpack.securitySolution.networkHttpTable.unit": "{totalCount, plural, other {リクエスト}}", "xpack.securitySolution.networkKpiDns.errorSearchDescription": "ネットワークKPI DNS検索でエラーが発生しました", "xpack.securitySolution.networkKpiDns.failSearchDescription": "ネットワークKPI DNSで検索を実行できませんでした", "xpack.securitySolution.networkKpiNetworkEvents.errorSearchDescription": "ネットワークKPIネットワークイベント検索でエラーが発生しました", @@ -24870,6 +25268,8 @@ "xpack.securitySolution.networkTopCountriesTable.column.sourceIps": "ソース IP", "xpack.securitySolution.networkTopCountriesTable.heading.destinationCountries": "デスティネーションの国", "xpack.securitySolution.networkTopCountriesTable.heading.sourceCountries": "ソースの国", + "xpack.securitySolution.networkTopCountriesTable.heading.unit": "{totalCount, plural, other {国}}", + "xpack.securitySolution.networkTopCountriesTable.rows": "{numRows} {numRows, plural, other {行}}", "xpack.securitySolution.networkTopNFlow.errorSearchDescription": "ネットワーク上位nフロー検索でエラーが発生しました", "xpack.securitySolution.networkTopNFlow.failSearchDescription": "ネットワーク上位nフローで検索を実行できませんでした", "xpack.securitySolution.networkTopNFlowTable.column.asTitle": "自動システム", @@ -24881,7 +25281,9 @@ "xpack.securitySolution.networkTopNFlowTable.column.sourceIpTitle": "ソース IP", "xpack.securitySolution.networkTopNFlowTable.destinationIps": "デスティネーション IP", "xpack.securitySolution.networkTopNFlowTable.flows": "Flow", + "xpack.securitySolution.networkTopNFlowTable.rows": "{numRows} {numRows, plural, other {行}}", "xpack.securitySolution.networkTopNFlowTable.sourceIps": "ソース IP", + "xpack.securitySolution.networkTopNFlowTable.unit": "{totalCount, plural, other {IP}}", "xpack.securitySolution.networkUsers.errorSearchDescription": "ネットワークユーザー検索でエラーが発生しました", "xpack.securitySolution.networkUsers.failSearchDescription": "ネットワークユーザーで検索を実行できませんでした", "xpack.securitySolution.newsFeed.advancedSettingsLinkTitle": "セキュリティソリューション詳細設定", @@ -24934,7 +25336,11 @@ "xpack.securitySolution.open.timeline.refreshTitle": "更新", "xpack.securitySolution.open.timeline.searchPlaceholder": "例:タイムライン名、または説明", "xpack.securitySolution.open.timeline.searchTemplatePlaceholder": "例:テンプレート名または説明", + "xpack.securitySolution.open.timeline.selectedTemplatesTitle": "{selectedTemplates} {selectedTemplates, plural, other {個のテンプレート}}を選択しました", + "xpack.securitySolution.open.timeline.selectedTimelinesTitle": "{selectedTimelines} {selectedTimelines, plural, other {タイムライン}}を選択しました", "xpack.securitySolution.open.timeline.showingLabel": "表示中:", + "xpack.securitySolution.open.timeline.showingNTemplatesLabel": "{totalSearchResultsCount}件の{totalSearchResultsCount, plural, other {個のテンプレート}} {with}", + "xpack.securitySolution.open.timeline.showingNTimelinesLabel": "{totalSearchResultsCount}件の {totalSearchResultsCount, plural, other {タイムライン}} {with}", "xpack.securitySolution.open.timeline.singleTemplateLabel": "テンプレート", "xpack.securitySolution.open.timeline.singleTimelineLabel": "タイムライン", "xpack.securitySolution.open.timeline.successfullyDeletedTimelinesTitle": "{totalTimelines, plural, =0 {すべてのタイムライン} other {{totalTimelines} 個のタイムライン}}の削除が正常に完了しました", @@ -24963,6 +25369,7 @@ "xpack.securitySolution.overview.ctiDashboardInfoPanelTitle": "ソースを表示するには、Kibanaダッシュボードを有効にします", "xpack.securitySolution.overview.ctiDashboardOtherDatasourceTitle": "その他", "xpack.securitySolution.overview.ctiDashboardSomeModulesDisabledTItle": "一部の脅威インテリジェンスソースが無効です", + "xpack.securitySolution.overview.ctiDashboardSubtitle": "{totalCount} {totalCount, plural, other {個の指標}}を表示しています", "xpack.securitySolution.overview.ctiDashboardTitle": "脅威インテリジェンス", "xpack.securitySolution.overview.ctiDashboardWarningPanelBody": "選択した時間範囲からデータが検出されませんでした。別の時間範囲の検索を試してください。", "xpack.securitySolution.overview.ctiDashboardWarningPanelTitle": "表示する脅威インテリジェンスデータがありません", @@ -24998,6 +25405,8 @@ "xpack.securitySolution.overview.networkStatGroupPacketbeat": "Packetbeat", "xpack.securitySolution.overview.networkTitle": "ネットワークイベント", "xpack.securitySolution.overview.newsFeedSidebarTitle": "セキュリティニュース", + "xpack.securitySolution.overview.overviewHost.hostsSubtitle": "表示中:{formattedHostEventsCount} {hostEventsCount, plural, other {イベント}}", + "xpack.securitySolution.overview.overviewNetwork.networkSubtitle": "表示中:{formattedNetworkEventsCount} {networkEventsCount, plural, other {イベント}}", "xpack.securitySolution.overview.packetBeatDnsTitle": "DNS", "xpack.securitySolution.overview.packetBeatFlowTitle": "フロー", "xpack.securitySolution.overview.packetbeatTLSTitle": "TLS", @@ -25007,6 +25416,7 @@ "xpack.securitySolution.overview.riskyHostsDashboardDangerPanelButton": "リスクスコアを有効にする", "xpack.securitySolution.overview.riskyHostsDashboardDangerPanelTitle": "表示するホストリスクスコアデータがありません", "xpack.securitySolution.overview.riskyHostsDashboardEnableThreatIntel": "リスクの高いホストのリストを表示するには、ホストリスクスコアモジュールを有効にしてください。", + "xpack.securitySolution.overview.riskyHostsDashboardSubtitle": "{totalCount} {totalCount, plural, other {個のホスト}}を表示しています", "xpack.securitySolution.overview.riskyHostsDashboardTitle": "現在のホストリスクスコア", "xpack.securitySolution.overview.riskyHostsDashboardWarningPanelBody": "選択した期間では、ご使用の環境のホストからホストリスクスコアデータが検出されませんでした。", "xpack.securitySolution.overview.riskyHostsDashboardWarningPanelTitle": "表示するホストリスクスコアデータがありません", @@ -25028,6 +25438,8 @@ "xpack.securitySolution.pages.common.emptyActionEndpointDescription": "脅威防御、検出、深いセキュリティデータの可視化を実現し、ホストを保護します。", "xpack.securitySolution.pages.common.emptyActionSecondary": "入門ガイドを表示します。", "xpack.securitySolution.pages.common.solutionName": "セキュリティ", + "xpack.securitySolution.pages.common.updateAlertStatusFailed": "{ conflicts } {conflicts, plural, other {アラート}}を更新できませんでした。", + "xpack.securitySolution.pages.common.updateAlertStatusFailedDetailed": "{ updated } {updated, plural, other {アラート}}が正常に更新されましたが、{ conflicts }は更新できませんでした。\n { conflicts, plural, other {}}すでに修正されています。", "xpack.securitySolution.pages.emptyPage.beatsCard.description": "Elasticエージェントを使用して、セキュリティイベントを収集し、エンドポイントを脅威から保護してください。", "xpack.securitySolution.pages.emptyPage.beatsCard.title": "セキュリティ統合を追加", "xpack.securitySolution.pages.fourohfour.pageNotFoundDescription": "ページが見つかりません", @@ -25264,6 +25676,7 @@ "xpack.securitySolution.timeline.destination": "送信先", "xpack.securitySolution.timeline.EqlQueryBarLabel": "EQL クエリ", "xpack.securitySolution.timeline.eventHasEventRendererScreenReaderOnly": "行{row}のイベントにはイベントレンダラーがあります。Shiftと下矢印を押すとフォーカスします。", + "xpack.securitySolution.timeline.eventHasNotesScreenReaderOnly": "行{row}のイベントには{notesCount, plural, other {{notesCount}個のメモ}}があります。Shiftと右矢印を押すとメモをフォーカスします。", "xpack.securitySolution.timeline.eventsSelect.actions.pinSelected": "選択項目にピン付け", "xpack.securitySolution.timeline.eventsSelect.actions.selectAll": "すべて", "xpack.securitySolution.timeline.eventsSelect.actions.selectNone": "なし", @@ -25389,11 +25802,13 @@ "xpack.securitySolution.timelines.allTimelines.importTimelineTitle": "インポート", "xpack.securitySolution.timelines.allTimelines.panelTitle": "すべてのタイムライン", "xpack.securitySolution.timelines.components.importTimelineModal.importFailedDetailedTitle": "{message}", + "xpack.securitySolution.timelines.components.importTimelineModal.importFailedTitle": "{totalTimelines} {totalTimelines, plural, other {個のルール}}をインポートできませんでした", "xpack.securitySolution.timelines.components.importTimelineModal.importTimelineTitle": "インポート", "xpack.securitySolution.timelines.components.importTimelineModal.importTitle": "インポート…", "xpack.securitySolution.timelines.components.importTimelineModal.initialPromptTextDescription": "有効な timelines_export.ndjson ファイルを選択するか、またはドラッグアンドドロップします", "xpack.securitySolution.timelines.components.importTimelineModal.overwriteDescription": "保存されたオブジェクトを同じタイムライン ID で自動的に上書きします", "xpack.securitySolution.timelines.components.importTimelineModal.selectTimelineDescription": "インポートするタイムラインまたはタイムラインテンプレートファイルを選択", + "xpack.securitySolution.timelines.components.importTimelineModal.successfullyImportedTimelinesTitle": "{totalCount} {totalCount, plural, other {個の項目}}のインポートが正常に完了しました", "xpack.securitySolution.timelines.components.tabs.templatesTitle": "テンプレート", "xpack.securitySolution.timelines.components.tabs.timelinesTitle": "タイムライン", "xpack.securitySolution.timelines.components.templateFilter.customizedTitle": "カスタムテンプレート", @@ -25433,6 +25848,7 @@ "xpack.securitySolution.trustedapps.createTrustedAppFlyout.successToastTitle": "\"{name}\"は信頼できるアプリケーションリストに追加されました。", "xpack.securitySolution.trustedapps.createTrustedAppFlyout.updateSuccessToastTitle": "\"{name}\"が更新されました。", "xpack.securitySolution.trustedapps.creationSuccess.title": "成功!", + "xpack.securitySolution.trustedapps.deletionDialog.calloutMessage": "このエントリを削除すると、{count}個の関連付けられた{count, plural, other {ポリシー}}から削除されます。", "xpack.securitySolution.trustedapps.deletionDialog.calloutTitle": "警告", "xpack.securitySolution.trustedapps.deletionDialog.cancelButton": "キャンセル", "xpack.securitySolution.trustedapps.deletionDialog.confirmButton": "削除", @@ -25498,6 +25914,8 @@ "xpack.securitySolution.uncommonProcessTable.nameTitle": "プロセス名", "xpack.securitySolution.uncommonProcessTable.numberOfHostsTitle": "ホスト", "xpack.securitySolution.uncommonProcessTable.numberOfInstances": "インスタンス", + "xpack.securitySolution.uncommonProcessTable.rows": "{numRows} {numRows, plural, other {行}}", + "xpack.securitySolution.uncommonProcessTable.unit": "{totalCount, plural, other {プロセス}}", "xpack.securitySolution.zeek.othDescription": "SYNが検出されません。ミッドストリームトラフィックのみです", "xpack.securitySolution.zeek.rejDescription": "接続試行が拒否されました", "xpack.securitySolution.zeek.rstoODescription": "接続が確立され、接続元が中断しました(RSTを送信)", @@ -25529,7 +25947,10 @@ "xpack.snapshotRestore.createPolicyButton": "ポリシーを作成", "xpack.snapshotRestore.dataPlaceholderLabel": "-", "xpack.snapshotRestore.dataStreamsList.allDataStreamsValue": "すべてのデータストリーム", + "xpack.snapshotRestore.dataStreamsList.dataStreamsCollapseAllLink": "{count, plural, other {# 件のデータストリーム}}を非表示", + "xpack.snapshotRestore.dataStreamsList.dataStreamsExpandAllLink": "{count, plural, other {# 件のデータストリーム}}を表示", "xpack.snapshotRestore.deletePolicy.confirmModal.cancelButtonLabel": "キャンセル", + "xpack.snapshotRestore.deletePolicy.confirmModal.confirmButtonLabel": "{count, plural, other {ポリシー}}を削除", "xpack.snapshotRestore.deletePolicy.confirmModal.deleteMultipleListDescription": "これらのポリシーを削除しようとしています:", "xpack.snapshotRestore.deletePolicy.confirmModal.deleteMultipleTitle": "{count} 件のポリシーを削除しますか?", "xpack.snapshotRestore.deletePolicy.confirmModal.deleteSingleTitle": "ポリシー「{name}」を削除しますか?", @@ -25550,6 +25971,7 @@ "xpack.snapshotRestore.deleteRepository.successMultipleNotificationTitle": "{count} 件のレポジトリが削除されました", "xpack.snapshotRestore.deleteRepository.successSingleNotificationTitle": "レポジトリ「{name}」が削除されました", "xpack.snapshotRestore.deleteSnapshot.confirmModal.cancelButtonLabel": "キャンセル", + "xpack.snapshotRestore.deleteSnapshot.confirmModal.confirmButtonLabel": "{count, plural, other {スナップショット}}を削除", "xpack.snapshotRestore.deleteSnapshot.confirmModal.deleteMultipleDescription": "{count, plural, one {このスナップショット} other {これらのスナップショット}}に関連付けられた復元処理は停止します。", "xpack.snapshotRestore.deleteSnapshot.confirmModal.deleteMultipleListDescription": "次のスナップショットを削除しようとしています。", "xpack.snapshotRestore.deleteSnapshot.confirmModal.deleteMultipleTitle": "{count}件のスナップショットを削除しますか?", @@ -25603,6 +26025,8 @@ "xpack.snapshotRestore.home.snapshotRestoreTitle": "スナップショットリポジドリ", "xpack.snapshotRestore.home.snapshotsTabTitle": "スナップショット", "xpack.snapshotRestore.indicesList.allIndicesValue": "すべてのインデックス", + "xpack.snapshotRestore.indicesList.indicesCollapseAllLink": "{count, plural, other {# インデックス}}を非表示", + "xpack.snapshotRestore.indicesList.indicesExpandAllLink": "{count, plural, other {# インデックス}}を表示", "xpack.snapshotRestore.licenseCheckErrorMessage": "ライセンス確認失敗", "xpack.snapshotRestore.policies.breadcrumbTitle": "ポリシー", "xpack.snapshotRestore.policyDetails.closeButtonLabel": "閉じる", @@ -25762,6 +26186,7 @@ "xpack.snapshotRestore.policyForm.stepSettings.partialIndicesToggleSwitch": "部分インデックスを許可", "xpack.snapshotRestore.policyForm.stepSettings.policyIncludeGlobalStateLabel": "グローバルステータスを含める", "xpack.snapshotRestore.policyForm.stepSettings.selectAllIndicesLink": "すべて選択", + "xpack.snapshotRestore.policyForm.stepSettings.selectDataStreamsIndicesHelpText": "{indicesCount} {indicesCount, plural, other {個のインデックス}} and {dataStreamsCount} {dataStreamsCount, plural, other {個のデータストリーム}}がバックアップされます。{deselectAllLink}", "xpack.snapshotRestore.policyForm.stepSettings.selectIndicesLabel": "インデックスとデータストリームを選択", "xpack.snapshotRestore.policyForm.stepSettingsTitle": "スナップショット設定", "xpack.snapshotRestore.policyList.deniedPrivilegeDescription": "スナップショットライフサイクルポリシーを管理するには、{privilegesCount, plural, one {このクラスター特権} other {これらのクラスター特権}}が必要です:{missingPrivileges}。", @@ -25781,6 +26206,7 @@ "xpack.snapshotRestore.policyList.table.addPolicyButton": "ポリシーを作成", "xpack.snapshotRestore.policyList.table.deleteManagedPolicySelectTooltip": "管理されているポリシーは削除できません。", "xpack.snapshotRestore.policyList.table.deleteManagedPolicyTableActionTooltip": "管理されているポリシーは削除できません。", + "xpack.snapshotRestore.policyList.table.deletePolicyButton": "{count, plural, other {ポリシー}}を削除", "xpack.snapshotRestore.policyList.table.inProgressTooltip": "スナップショット進行中", "xpack.snapshotRestore.policyList.table.lastSnapshotFailedTooltip": "前回失敗したスナップショットです", "xpack.snapshotRestore.policyList.table.managedPolicyBadgeLabel": "これは管理されているポリシーです", @@ -25837,6 +26263,7 @@ "xpack.snapshotRestore.repositoryDetails.repositoryNotFoundErrorMessage": "レポジトリ「{name}」は存在しません。", "xpack.snapshotRestore.repositoryDetails.repositoryTypeDocLink": "レポジトリドキュメント", "xpack.snapshotRestore.repositoryDetails.settingsTitle": "設定", + "xpack.snapshotRestore.repositoryDetails.snapshotsDescription": "{count} 件の {count, plural, other {スナップショット}}が見つかりました", "xpack.snapshotRestore.repositoryDetails.snapshotsTitle": "スナップショット", "xpack.snapshotRestore.repositoryDetails.typeAzure.basePathLabel": "ベースパス", "xpack.snapshotRestore.repositoryDetails.typeAzure.chunkSizeLabel": "チャンクサイズ", @@ -26068,6 +26495,7 @@ "xpack.snapshotRestore.restoreForm.backButtonLabel": "戻る", "xpack.snapshotRestore.restoreForm.dataStreamsWarningCallOut.body": "各データストリームには、一致するインデックステンプレートが必要です。復元されたすべてのデータストリームに一致するインデックステンプレートがあることを確認してください。インデックステンプレートを復元するには、グローバルクラスター状態を復元します。ただし、既存のテンプレート、クラスター設定、入力パイプライン、ライフサイクルポリシーが上書きされる場合があります。データストリームを含むスナップショットの復元については、{learnMoreLink}。", "xpack.snapshotRestore.restoreForm.dataStreamsWarningCallOut.body.learnMoreLink": "詳細", + "xpack.snapshotRestore.restoreForm.dataStreamsWarningCallOut.title": "このスナップショットには、{count, plural, other {件のデータストリーム}}が含まれます", "xpack.snapshotRestore.restoreForm.navigation.stepLogisticsName": "ロジスティクス", "xpack.snapshotRestore.restoreForm.navigation.stepReviewName": "見直し", "xpack.snapshotRestore.restoreForm.navigation.stepSettingsName": "インデックス設定", @@ -26101,6 +26529,7 @@ "xpack.snapshotRestore.restoreForm.stepLogistics.renamePatternLabel": "取り込みパターン", "xpack.snapshotRestore.restoreForm.stepLogistics.renameReplacementLabel": "置換パターン", "xpack.snapshotRestore.restoreForm.stepLogistics.selectAllIndicesLink": "すべて選択", + "xpack.snapshotRestore.restoreForm.stepLogistics.selectDataStreamsAndIndicesHelpText": "{indicesCount} {indicesCount, plural, other {個のインデックス}} and {dataStreamsCount} {dataStreamsCount, plural, other {個のデータストリーム}}が復元されます。{deselectAllLink}", "xpack.snapshotRestore.restoreForm.stepLogistics.selectDataStreamsAndIndicesLabel": "データストリームとインデックスを選択", "xpack.snapshotRestore.restoreForm.stepLogistics.systemIndicesCallOut.title": "このスナップショットが復元されるときに、システムインデックスはスナップショットのデータで上書きされます。", "xpack.snapshotRestore.restoreForm.stepLogisticsTitle": "詳細を復元", @@ -26146,11 +26575,14 @@ "xpack.snapshotRestore.restoreList.emptyPromptDescription": "{snapshotsLink}に移動して、復元を開始します。", "xpack.snapshotRestore.restoreList.emptyPromptDescriptionLink": "スナップショット", "xpack.snapshotRestore.restoreList.emptyPromptTitle": "復元されたスナップショットはありません", + "xpack.snapshotRestore.restoreList.intervalMenu.minutesIntervalValue": "{minutes} {minutes, plural, other {分}}", + "xpack.snapshotRestore.restoreList.intervalMenu.secondsIntervalValue": "{seconds} {seconds, plural, other {秒}}", "xpack.snapshotRestore.restoreList.intervalMenuButtonText": "{interval}ごとにデータを更新", "xpack.snapshotRestore.restoreList.loadingRestoresDescription": "復元を読み込んでいます…", "xpack.snapshotRestore.restoreList.loadingRestoresErrorMessage": "リソースの読み込みエラー", "xpack.snapshotRestore.restoreList.shardTable.bytesColumnTitle": "バイト", "xpack.snapshotRestore.restoreList.shardTable.durationColumnTitle": "期間", + "xpack.snapshotRestore.restoreList.shardTable.durationValue": "{seconds} {seconds, plural, other {秒}}", "xpack.snapshotRestore.restoreList.shardTable.endTimeColumnTitle": "終了時刻", "xpack.snapshotRestore.restoreList.shardTable.filesColumnTitle": "ファイル", "xpack.snapshotRestore.restoreList.shardTable.indexColumnTitle": "ID", @@ -26198,6 +26630,7 @@ "xpack.snapshotRestore.snapshotDetails.failuresTabTitle": "失敗したインデックス({failuresCount})", "xpack.snapshotRestore.snapshotDetails.itemDataStreamsLabel": "データストリーム({dataStreamsCount})", "xpack.snapshotRestore.snapshotDetails.itemDurationLabel": "期間", + "xpack.snapshotRestore.snapshotDetails.itemDurationValueLabel": "{seconds} {seconds, plural, other {秒}}", "xpack.snapshotRestore.snapshotDetails.itemEndTimeLabel": "終了時刻", "xpack.snapshotRestore.snapshotDetails.itemIncludeGlobalStateLabel": "グローバルステータスを含める", "xpack.snapshotRestore.snapshotDetails.itemIncludeGlobalStateNoLabel": "いいえ", @@ -26236,6 +26669,7 @@ "xpack.snapshotRestore.snapshotList.table.actionsColumnTitle": "アクション", "xpack.snapshotRestore.snapshotList.table.deleteManagedRepositorySnapshotDescription": "管理されたリポジトリに最後の成功したスナップショットを保持する必要があります。", "xpack.snapshotRestore.snapshotList.table.deleteManagedRepositorySnapshotTooltip": "管理されたリポジトリに最後の成功したスナップショットを格納する必要があります。", + "xpack.snapshotRestore.snapshotList.table.deleteSnapshotButton": "{count, plural, other {スナップショット}}を削除", "xpack.snapshotRestore.snapshotList.table.durationColumnTitle": "期間", "xpack.snapshotRestore.snapshotList.table.durationColumnValueLabel": "{seconds}秒", "xpack.snapshotRestore.snapshotList.table.failedShardsColumnTitle": "シャードの失敗", @@ -26313,6 +26747,7 @@ "xpack.spaces.management.copyToSpace.copyStatusSummary.failedMessage": "{space}スペースへのコピーに失敗しました。詳細はこのセクションを展開してください。", "xpack.spaces.management.copyToSpace.copyStatusSummary.missingReferencesMessage": "{space}スペースで見つからない参照が検出されました。詳細はこのセクションを展開してください。", "xpack.spaces.management.copyToSpace.copyStatusSummary.successMessage": "正常に{space}スペースにコピーされました。", + "xpack.spaces.management.copyToSpace.copyToSpacesButton": "{spaceCount} {spaceCount, plural, other {スペース}}にコピー", "xpack.spaces.management.copyToSpace.createNewCopiesLabel": "ランダムIDで新しいオブジェクトを作成", "xpack.spaces.management.copyToSpace.disabledCopyToSpacesButton": "コピー", "xpack.spaces.management.copyToSpace.dontCreateNewCopiesLabel": "既存のオブジェクトを確認", @@ -26416,6 +26851,7 @@ "xpack.spaces.navControl.spacesMenu.noSpacesFoundTitle": " スペースが見つかりません ", "xpack.spaces.redirectLegacyUrlToast.text": "検索している{objectNoun}は新しい場所にあります。今後はこのURLを使用してください。", "xpack.spaces.redirectLegacyUrlToast.title": "新しいURLに移動しました", + "xpack.spaces.shareToSpace.aliasTableCalloutBody": "{aliasesToDisableCount, plural, other {# 個のレガシーURL}}が無効になります。", "xpack.spaces.shareToSpace.aliasTableCalloutTitle": "レガシーURL競合", "xpack.spaces.shareToSpace.allSpacesTarget": "すべてのスペース", "xpack.spaces.shareToSpace.cancelButton": "キャンセル", @@ -26431,6 +26867,7 @@ "xpack.spaces.shareToSpace.privilegeWarningBody": "この{objectNoun}のスペースを編集するには、すべてのスペースで{readAndWritePrivilegesLink}が必要です。", "xpack.spaces.shareToSpace.privilegeWarningLink": "読み書き権限", "xpack.spaces.shareToSpace.privilegeWarningTitle": "追加の権限が必要です", + "xpack.spaces.shareToSpace.relativesControl.description": "{relativesCount}個の関連する{relativesCount, plural, other {objects}}も変更されます。", "xpack.spaces.shareToSpace.saveButton": "保存して閉じる", "xpack.spaces.shareToSpace.shareErrorTitle": "{objectNoun}の更新エラー", "xpack.spaces.shareToSpace.shareModeControl.buttonGroupLegend": "この共有方法を選択", @@ -26442,11 +26879,15 @@ "xpack.spaces.shareToSpace.shareModeControl.shareToAllSpaces.text": "現在と将来のすべてのスペースで{objectNoun}を使用可能にします。", "xpack.spaces.shareToSpace.shareModeControl.shareToExplicitSpaces.buttonLabel": "スペースを選択", "xpack.spaces.shareToSpace.shareModeControl.shareToExplicitSpaces.text": "選択したスペースでのみ{objectNoun}を使用可能にします。", + "xpack.spaces.shareToSpace.shareSuccessAddRemoveText": "'{object}' {relativesCount, plural, other {および{relativesCount}個の関連するオブジェクト}}が{spacesTargetAdd}に追加され、{spacesTargetRemove}から削除されました。", + "xpack.spaces.shareToSpace.shareSuccessAddText": "'{object}' {relativesCount, plural, other {および{relativesCount}個の関連するオブジェクト}}が{spacesTarget}に追加されました。", + "xpack.spaces.shareToSpace.shareSuccessRemoveText": "'{object}' {relativesCount, plural, other {および{relativesCount}個の関連するオブジェクト}}が{spacesTarget}から削除されました。", "xpack.spaces.shareToSpace.shareSuccessTitle": "{objectNoun}を更新しました", "xpack.spaces.shareToSpace.shareWarningBody": "変更は選択した各スペースに表示されます。変更を同期しない場合は、{makeACopyLink}。", "xpack.spaces.shareToSpace.shareWarningLink": "コピーを作成", "xpack.spaces.shareToSpace.shareWarningTitle": "変更はスペース全体で同期されます", "xpack.spaces.shareToSpace.spacesLoadErrorTitle": "利用可能なスペースを読み込み中にエラーが発生", + "xpack.spaces.shareToSpace.spacesTarget": "{spacesCount, plural, other {# 個のスペース}}", "xpack.spaces.shareToSpace.unknownSpacesLabel.additionalPrivilegesLink": "追加権限", "xpack.spaces.shareToSpace.unknownSpacesLabel.text": "非表示のスペースを表示するには、{additionalPrivilegesLink}が必要です。", "xpack.spaces.spaceList.allSpacesLabel": "*すべてのスペース", @@ -26623,14 +27064,17 @@ "xpack.timelines.emptyString.emptyStringDescription": "空の文字列", "xpack.timelines.eventDetails.copyToClipboardTooltip": "クリップボードにコピー", "xpack.timelines.exitFullScreenButton": "全画面を終了", + "xpack.timelines.fieldBrowser.categoriesCountTitle": "{totalCount} {totalCount, plural, other {カテゴリ}}", "xpack.timelines.fieldBrowser.categoriesTitle": "カテゴリー", "xpack.timelines.fieldBrowser.categoryFieldsTableCaption": "カテゴリ {categoryId} フィールド", "xpack.timelines.fieldBrowser.categoryLabel": "カテゴリー", + "xpack.timelines.fieldBrowser.categoryLinkAriaLabel": "{category} {totalCount} {totalCount, plural, other {フィールド}}このボタンをクリックすると、{category} カテゴリを選択します。", "xpack.timelines.fieldBrowser.closeButton": "閉じる", "xpack.timelines.fieldBrowser.descriptionForScreenReaderOnly": "フィールド {field} の説明:", "xpack.timelines.fieldBrowser.descriptionLabel": "説明", "xpack.timelines.fieldBrowser.fieldBrowserTitle": "フィールド", "xpack.timelines.fieldBrowser.fieldLabel": "フィールド", + "xpack.timelines.fieldBrowser.fieldsCountTitle": "{totalCount} {totalCount, plural, other {フィールド}}", "xpack.timelines.fieldBrowser.fieldsTitle": "フィールド", "xpack.timelines.fieldBrowser.filterPlaceholder": "フィールド名", "xpack.timelines.fieldBrowser.noFieldsMatchInputLabel": "{searchInput} に一致するフィールドがありません", @@ -26674,10 +27118,11 @@ "xpack.timelines.tGrid.eventsLabel": "イベント", "xpack.timelines.tGrid.footer.loadingEventsDataLabel": "イベントを読み込み中", "xpack.timelines.tGrid.footer.totalCountOfEvents": "イベント", - "xpack.timelines.tGrid.unit": "{totalCount, plural, other {アラート}}", + "xpack.timelines.tGrid.unit": "{totalCount, plural, other {アラート}}", "xpack.timelines.timeline.acknowledgedAlertFailedToastMessage": "アラートを確認済みに設定できませんでした", + "xpack.timelines.timeline.acknowledgedAlertSuccessToastMessage": "{totalAlerts} {totalAlerts, plural, other {件のアラート}}を確認済みに設定しました。", "xpack.timelines.timeline.acknowledgedSelectedTitle": "確認済みに設定", - "xpack.timelines.timeline.alertsUnit": "{totalCount, plural, other {アラート}}", + "xpack.timelines.timeline.alertsUnit": "{totalCount, plural, other {アラート}}", "xpack.timelines.timeline.body.actions.addNotesForRowAriaLabel": "行 {ariaRowindex}、列 {columnValues} のイベントのメモをタイムラインに追加", "xpack.timelines.timeline.body.actions.attachAlertToCaseForRowAriaLabel": "行 {ariaRowindex}、列 {columnValues} のアラートまたはイベントをケースに追加", "xpack.timelines.timeline.body.actions.checkboxForRowAriaLabel": "行 {ariaRowindex}、列 {columnValues} のアラートまたはイベントのチェックボックスを{checked, select, false {オフ} true {オン}}", @@ -26704,25 +27149,32 @@ "xpack.timelines.timeline.body.sort.sortedDescendingTooltip": "降順で並べ替えます", "xpack.timelines.timeline.categoryTooltip": "カテゴリー", "xpack.timelines.timeline.closedAlertFailedToastMessage": "アラートをクローズできませんでした。", + "xpack.timelines.timeline.closedAlertSuccessToastMessage": "{totalAlerts} {totalAlerts, plural, other {件のアラート}}を正常にクローズしました。", "xpack.timelines.timeline.closeSelectedTitle": "クローズ済みに設定", "xpack.timelines.timeline.descriptionTooltip": "説明", "xpack.timelines.timeline.eventHasEventRendererScreenReaderOnly": "行{row}のイベントにはイベントレンダラーがあります。Shiftと下矢印を押すとフォーカスします。", + "xpack.timelines.timeline.eventHasNotesScreenReaderOnly": "行{row}のイベントには{notesCount, plural, other {{notesCount}個のメモ}}があります。Shiftと右矢印を押すとメモをフォーカスします。", "xpack.timelines.timeline.eventsTableAriaLabel": "イベント; {activePage}/{totalPages} ページ", "xpack.timelines.timeline.fieldTooltip": "フィールド", "xpack.timelines.timeline.flyout.pane.removeColumnButtonLabel": "列を削除", "xpack.timelines.timeline.fullScreenButton": "全画面", "xpack.timelines.timeline.openedAlertFailedToastMessage": "アラートを開けませんでした", + "xpack.timelines.timeline.openedAlertSuccessToastMessage": "{totalAlerts} {totalAlerts, plural, other {件のアラート}}を正常に開きました。", "xpack.timelines.timeline.openSelectedTitle": "開封済みに設定", "xpack.timelines.timeline.properties.timelineToggleButtonAriaLabel": "タイムライン {title} を{isOpen, select, false {開く} true {閉じる} other {切り替える}}", "xpack.timelines.timeline.sortAZLabel": "A-Zの昇順で並べ替え", "xpack.timelines.timeline.sortFieldsButton": "フィールドの並べ替え", "xpack.timelines.timeline.sortZALabel": "ZーAの降順で並べ替え", "xpack.timelines.timeline.typeTooltip": "型", + "xpack.timelines.timeline.updateAlertStatusFailed": "{ conflicts } {conflicts, plural, other {アラート}}を更新できませんでした。", + "xpack.timelines.timeline.updateAlertStatusFailedDetailed": "{ updated } {updated, plural, other {アラート}}が正常に更新されましたが、{ conflicts }は更新できませんでした。\n { conflicts, plural, other {}}すでに修正されています。", "xpack.timelines.timeline.updateAlertStatusFailedSingleAlert": "アラートを更新できませんでした。アラートはすでに修正されています。", "xpack.timelines.timeline.youAreInAnEventRendererScreenReaderOnly": "行 {row} のイベントレンダラーを表示しています。上矢印キーを押すと、終了して現在の行に戻ります。下矢印キーを押すと、終了して次の行に進みます。", "xpack.timelines.timeline.youAreInATableCellScreenReaderOnly": "表セルの行 {row}、列 {column} にいます", "xpack.timelines.timelineEvents.errorSearchDescription": "タイムラインイベント検索でエラーが発生しました", "xpack.timelines.toolbar.bulkActions.clearSelectionTitle": "選択した項目をクリア", + "xpack.timelines.toolbar.bulkActions.selectAllAlertsTitle": "すべての{totalAlertsFormatted} {totalAlerts, plural, other {件のアラート}}を選択", + "xpack.timelines.toolbar.bulkActions.selectedAlertsTitle": "Selected {selectedAlertsFormatted} {selectedAlerts, plural, other {件のアラート}}", "xpack.transform.actionDeleteTransform.bulkDeleteDestDataViewTitle": "ディスティネーションデータビューの削除", "xpack.transform.actionDeleteTransform.bulkDeleteDestinationIndexTitle": "ディスティネーションインデックスの削除", "xpack.transform.actionDeleteTransform.deleteDestDataViewTitle": "データビュー{destinationIndex}を削除", @@ -26760,6 +27212,7 @@ "xpack.transform.alertTypes.transformHealth.includeTransformsLabel": "変換を含む", "xpack.transform.alertTypes.transformHealth.notStartedCheckDescription": "変換が開始しないか、データにインデックスを作成していないときにアラートを受信します。", "xpack.transform.alertTypes.transformHealth.notStartedCheckName": "変換が開始していません", + "xpack.transform.alertTypes.transformHealth.notStartedMessage": "{count, plural, other {変換}} {transformsString} {count, plural, other {が}}開始していません。", "xpack.transform.alertTypes.transformHealth.testsSelection.enableTestLabel": "有効にする", "xpack.transform.app.checkingPrivilegesDescription": "権限を確認中…", "xpack.transform.app.checkingPrivilegesErrorMessage": "サーバーからユーザー特権を取得しているときにエラーが発生しました", @@ -26780,6 +27233,7 @@ "xpack.transform.clone.noDataViewErrorPromptText": "変換{transformId}を複製できません。{dataViewTitle}のデータビューは存在しません。", "xpack.transform.cloneTransform.breadcrumbTitle": "クローン変換", "xpack.transform.createTransform.breadcrumbTitle": "変換の作成", + "xpack.transform.danglingTasksError": "{count} {count, plural, other {個の変換}}に構成の詳細がありません。[{transformIds}] {count, plural, one {それ} other {それら}}を回復することはできず、削除されます。", "xpack.transform.deleteTransform.deleteAnalyticsWithDataViewErrorMessage": "データビュー{destinationIndex}の削除中にエラーが発生しました", "xpack.transform.deleteTransform.deleteAnalyticsWithDataViewSuccessMessage": "データビュー{destinationIndex}を削除する要求が確認されました。", "xpack.transform.deleteTransform.deleteAnalyticsWithIndexErrorMessage": "ディスティネーションインデックス{destinationIndex}の削除中にエラーが発生しました", @@ -26788,6 +27242,7 @@ "xpack.transform.description": "説明", "xpack.transform.edit.errorPromptText": "ソースデータビューが存在するかどうかを確認するときにエラーが発生しました", "xpack.transform.edit.noDataViewErrorPromptText": "変換{transformId}のデータビューを取得できません。{dataViewTitle}のデータビューは存在しません。", + "xpack.transform.forceDeleteTransformMessage": "{count} {count, plural, other {個の変換}}を削除します", "xpack.transform.groupby.popoverForm.aggLabel": "アグリゲーション", "xpack.transform.groupBy.popoverForm.aggNameAlreadyUsedError": "別のグループ分けの構成がすでにこの名前を使用しています。", "xpack.transform.groupBy.popoverForm.aggNameInvalidCharError": "無効な名前です。「[」、「]」「>」は使用できず、名前の始めと終わりにはスペースを使用できません。", @@ -26817,6 +27272,7 @@ "xpack.transform.models.transformService.allOtherRequestsCancelledDescription": "他のすべてのリクエストはキャンセルされました。", "xpack.transform.models.transformService.requestToActionTimedOutErrorMessage": "「{id}」を{action}するリクエストがタイムアウトしました。{extra}", "xpack.transform.multiTransformActionsMenu.managementActionsAriaLabel": "管理アクション", + "xpack.transform.multiTransformActionsMenu.transformsCount": "{count} {count, plural, other {個の変換}}が選択されました", "xpack.transform.newTransform.chooseSourceTitle": "ソースの選択", "xpack.transform.newTransform.newTransformTitle": "新規変換", "xpack.transform.newTransform.searchSelection.notFoundLabel": "一致インデックスまたは保存した検索が見つかりません。", @@ -26997,6 +27453,15 @@ "xpack.transform.toastText.openModalButtonText": "詳細を表示", "xpack.transform.transformForm.sizeNotationPlaceholder": "例:{example1}、{example2}、{example3}、{example4}", "xpack.transform.transformList.alertingRules.screenReaderDescription": "アラートルールがが変換に関連付けられているときには、この列にアイコンが表示されます", + "xpack.transform.transformList.alertingRules.tooltipContent": "変換{rulesCount}はアラート{rulesCount, plural, other { ルール}}に関連付けられています", + "xpack.transform.transformList.bulkDeleteDestDataViewSuccessMessage": "{count}個のディスティネーションデータ{count, plural, other {ビュー}}を正常に削除しました。", + "xpack.transform.transformList.bulkDeleteDestIndexSuccessMessage": "{count}個のディスティネーション{count, plural, other {インデックス}}を正常に削除しました。", + "xpack.transform.transformList.bulkDeleteModalTitle": "{count} {count, plural, other {個の変換}}を削除しますか?", + "xpack.transform.transformList.bulkDeleteTransformSuccessMessage": "{count} {count, plural, other {個の変換}}を正常に削除しました。", + "xpack.transform.transformList.bulkResetModalTitle": "{count} {count, plural, other {個の変換}}をリセットしますか?", + "xpack.transform.transformList.bulkResetTransformSuccessMessage": "{count} {count, plural, other {個の変換}}を正常にリセットしました。", + "xpack.transform.transformList.bulkStartModalTitle": "{count} {count, plural, other {個の変換}}を開始しますか?", + "xpack.transform.transformList.bulkStopModalTitle": "{count} {count, plural, other {個の変換}}を停止しますか?", "xpack.transform.transformList.cloneActionNameText": "クローンを作成", "xpack.transform.transformList.completeBatchTransformBulkActionToolTip": "1 つまたは複数の変換が完了済みの一斉変換で、再度開始できません。", "xpack.transform.transformList.completeBatchTransformToolTip": "{transformId} は完了済みのバッチジョブで、再度開始できません。", @@ -27249,6 +27714,7 @@ "xpack.triggersActionsUI.components.builtinActionTypes.jira.unableToGetIssuesMessage": "問題を取得できません", "xpack.triggersActionsUI.components.builtinActionTypes.jira.unableToGetIssueTypesMessage": "問題タイプを取得できません", "xpack.triggersActionsUI.components.builtinActionTypes.jira.urgencySelectFieldLabel": "問題タイプ", + "xpack.triggersActionsUI.components.builtinActionTypes.missingSecretsValuesLabel": "機密情報はインポートされません。次のフィールド{encryptedFieldsLength, plural, other {}}の値{encryptedFieldsLength, plural, other {}}入力してください。", "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.actionTypeTitle": "PagerDuty に送信", "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.apiUrlTextFieldLabel": "API URL(任意)", "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.classFieldLabel": "クラス(任意)", @@ -27690,6 +28156,7 @@ "xpack.triggersActionsUI.sections.alertsList.alertStatusOk": "OK", "xpack.triggersActionsUI.sections.alertsList.alertStatusPending": "保留中", "xpack.triggersActionsUI.sections.alertsList.alertStatusUnknown": "不明", + "xpack.triggersActionsUI.sections.alertsList.attentionBannerTitle": "{totalStatusesError, plural, other {# 件のルール}}でエラーが見つかりました。", "xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.buttonTitle": "ルールの管理", "xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.deleteAllTitle": "削除", "xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.disableAllTitle": "無効にする", @@ -27803,6 +28270,10 @@ "xpack.triggersActionsUI.sections.testConnectorForm.runTestHeader": "テストを実行", "xpack.triggersActionsUI.sections.testConnectorForm.tabText": "テスト", "xpack.triggersActionsUI.sections.testConnectorForm.testResultsHeader": "結果", + "xpack.triggersActionsUI.timeUnits.dayLabel": "{timeValue, plural, other {日}}", + "xpack.triggersActionsUI.timeUnits.hourLabel": "{timeValue, plural, other {時間}}", + "xpack.triggersActionsUI.timeUnits.minuteLabel": "{timeValue, plural, other {分}}", + "xpack.triggersActionsUI.timeUnits.secondLabel": "{timeValue, plural, other {秒}}", "xpack.triggersActionsUI.typeRegistry.get.missingActionTypeErrorMessage": "オブジェクトタイプ「{id}」は登録されていません。", "xpack.triggersActionsUI.typeRegistry.register.duplicateObjectTypeErrorMessage": "オブジェクトタイプ「{id}」はすでに登録されています。", "xpack.uiActionsEnhanced.components.actionWizard.betaActionLabel": "ベータ", @@ -27867,6 +28338,7 @@ "xpack.uiActionsEnhanced.drilldowns.components.FlyoutFrame.CloseButtonLabel": "閉じる", "xpack.uiActionsEnhanced.drilldowns.containers.createDrilldownForm.primaryButton": "ドリルダウンを作成", "xpack.uiActionsEnhanced.drilldowns.containers.createDrilldownForm.title": "ドリルダウンを作成", + "xpack.uiActionsEnhanced.drilldowns.containers.drilldownList.copyingNotification.body": "{count, number} {count, plural, other {個のドリルダウン}}がコピーされました。", "xpack.uiActionsEnhanced.drilldowns.containers.drilldownList.copyingNotification.dismiss": "閉じる", "xpack.uiActionsEnhanced.drilldowns.containers.DrilldownManager.createNew": "新規作成", "xpack.uiActionsEnhanced.drilldowns.containers.DrilldownManager.manage": "管理", @@ -28011,6 +28483,7 @@ "xpack.upgradeAssistant.esDeprecations.reindex.resolutionTooltipLabel": "このインデックスを再インデックスして、この問題を解決します。この問題は自動的に解決できます。", "xpack.upgradeAssistant.esDeprecations.removeSettingsFlyout.closeButtonLabel": "閉じる", "xpack.upgradeAssistant.esDeprecations.removeSettingsFlyout.deleteErrorTitle": "インデックス設定の削除エラー", + "xpack.upgradeAssistant.esDeprecations.removeSettingsFlyout.description": "次の廃止予定のインデックス{indexSettingsCount, plural, other {設定}}を削除しますか?", "xpack.upgradeAssistant.esDeprecations.removeSettingsFlyout.removeButtonLabel": "廃止予定の設定を削除", "xpack.upgradeAssistant.esDeprecations.removeSettingsFlyout.resolvedButtonLabel": "解決済み", "xpack.upgradeAssistant.esDeprecations.removeSettingsFlyout.retryRemoveButtonLabel": "廃止予定の設定の削除を再試行", @@ -28080,6 +28553,7 @@ "xpack.upgradeAssistant.overview.cloudBackup.retryButton": "再試行", "xpack.upgradeAssistant.overview.cloudBackup.snapshotsLink": "スナップショットの作成", "xpack.upgradeAssistant.overview.deprecationLogs.buttonLabel": "廃止予定ログ収集を有効にする", + "xpack.upgradeAssistant.overview.deprecationLogs.deniedPrivilegeDescription": "廃止予定ログのインデックスは続行されますが、次の読み取りインデックス{privilegesCount, plural, other {権限}}が付与されるまでは分析できません:{missingPrivileges}", "xpack.upgradeAssistant.overview.deprecationLogs.deniedPrivilegeTitle": "廃止予定ログを分析するには、インデックス権限が必要です", "xpack.upgradeAssistant.overview.deprecationLogs.deprecationWarningBody": "ログディレクトリに移動して廃止予定ログを表示するか、廃止予定ログ収集を有効にしてKibanaに表示してください。", "xpack.upgradeAssistant.overview.deprecationLogs.deprecationWarningTitle": "ログはログディレクトリに書き込まれています", @@ -28129,7 +28603,7 @@ "xpack.upgradeAssistant.overview.upgradeStepDescriptionForCloud": "重要な問題をすべて解決し、アプリケーションの準備を確認した後に、Elastic 8.xにアップグレードできます。アップグレードする前に、必ずもう一度データをバックアップしたことを確認してください。Elastic Cloudでデプロイをアップグレードします。", "xpack.upgradeAssistant.overview.upgradeStepTitle": "Elastic 8.xへのアップグレード", "xpack.upgradeAssistant.overview.verifyChanges.calloutBody": "変更した後、カウンターをリセットして監視を続け、廃止予定の機能を使用していないことを確認します。", - "xpack.upgradeAssistant.overview.verifyChanges.calloutTitle": "{previousCheck}以降{warningsCount, plural, =0 {0} other {{warningsCount}}}件の廃止予定{warningsCount, plural, other {件の問題}}", + "xpack.upgradeAssistant.overview.verifyChanges.calloutTitle": "{previousCheck}以降{warningsCount, plural, =0 {0} other {{warningsCount}}}件の廃止予定{warningsCount, plural , other {件の問題}}", "xpack.upgradeAssistant.overview.verifyChanges.errorToastTitle": "廃止予定ログキャッシュを削除できませんでした", "xpack.upgradeAssistant.overview.verifyChanges.loadingError": "廃止予定件数の取得中にエラーが発生しました", "xpack.upgradeAssistant.overview.verifyChanges.resetCounterButton": "カウンターのリセット", @@ -28139,6 +28613,7 @@ "xpack.upgradeAssistant.overview.whatsNewLink": "8.xの新機能", "xpack.upgradeAssistant.reindex.reindexPrivilegesErrorBatch": "「{indexName}」に再インデックスするための権限が不十分です。", "xpack.upgradeAssistant.status.allDeprecationsResolvedMessage": "すべての廃止予定の警告が解決されました。", + "xpack.upgradeAssistant.status.deprecationsUnresolvedMessage": "アップグレード前に移行が必要な{notMigratedSystemIndices}システム{notMigratedSystemIndices, plural, other {インデックス}}と、解決が必要な{esTotalCriticalDeps} Elasticsearch廃止予定{esTotalCriticalDeps, plural, other {問題}}および{kibanaTotalCriticalDeps} Kibana廃止予定{kibanaTotalCriticalDeps, plural, other {問題}}があります。", "xpack.upgradeAssistant.upgradedDescription": "すべての Elasticsearch ノードがアップグレードされました。Kibana をアップデートする準備ができました。", "xpack.upgradeAssistant.upgradedTitle": "クラスターがアップグレードされました", "xpack.upgradeAssistant.upgradingDescription": "1 つまたは複数の Elasticsearch ノードに、 Kibana よりも新しいバージョンの Elasticsearch があります。すべてのノードがアップグレードされた後で Kibana をアップグレードしてください。", @@ -28700,6 +29175,7 @@ "xpack.uptime.monitorManagement.runTest": "テストの実行", "xpack.uptime.monitorManagement.saveMonitorLabel": "モニターを保存", "xpack.uptime.monitorManagement.serviceLocationsValidationError": "1つ以上のサービスの場所を指定する必要があります", + "xpack.uptime.monitorManagement.stepCompleted": "{stepCount, number} {stepCount, plural, other {個のステップ}}が完了しました", "xpack.uptime.monitorManagement.testResult": "テスト結果", "xpack.uptime.monitorManagement.timeTaken": "かかった時間{timeTaken}", "xpack.uptime.monitorManagement.updateMonitorLabel": "モニターの更新", @@ -29017,6 +29493,8 @@ "xpack.watcher.data.parseEsInterval.invalidEsCalendarIntervalErrorMessage": "無効なカレンダー間隔:{interval}、1よりも大きな値が必要です", "xpack.watcher.data.parseEsInterval.invalidEsIntervalFormatErrorMessage": "無効な間隔形式:{interval}", "xpack.watcher.deleteSelectedWatchesConfirmModal.cancelButtonLabel": "キャンセル", + "xpack.watcher.deleteSelectedWatchesConfirmModal.deleteButtonLabel": "{numWatchesToDelete, plural, other {# ウォッチ}}を削除 ", + "xpack.watcher.deleteSelectedWatchesConfirmModal.descriptionText": "{numWatchesToDelete, plural, other {削除されたウォッチ}}は回復できません。", "xpack.watcher.models.actionStatus.actionStatusJsonPropertyMissingBadRequestMessage": "JSON引数には\"{missingProperty}\"プロパティが含まれている必要があります", "xpack.watcher.models.baseAction.selectMessageText": "アクションを実行します。", "xpack.watcher.models.baseAction.simulateButtonLabel": "今すぐこのアクションをシミュレート", @@ -29120,11 +29598,13 @@ "xpack.watcher.sections.watchDetail.watchTable.ackActionCellTooltipTitle": "ウォッチアクションを承認します。", "xpack.watcher.sections.watchDetail.watchTable.ackActionErrorMessage": "アクション{actionId}の承認中にエラーが発生しました", "xpack.watcher.sections.watchDetail.watchTable.actionHeader": "名前", + "xpack.watcher.sections.watchDetail.watchTable.errorsCellText": "{total, number} {total, plural, other {エラー}}", "xpack.watcher.sections.watchDetail.watchTable.errorsHeader": "エラー", "xpack.watcher.sections.watchDetail.watchTable.noWatchesMessage": "表示するアクションがありません", "xpack.watcher.sections.watchDetail.watchTable.stateHeader": "ステータス", "xpack.watcher.sections.watchEdit.actions.addActionButtonLabel": "アクションの追加", "xpack.watcher.sections.watchEdit.actions.disabledOptionLabel": "無効です。elasticsearch.ymlを構成します。", + "xpack.watcher.sections.watchEdit.actions.title": "条件が満たされたら、{watchActionsCount, plural, other {# アクション}}を実行します", "xpack.watcher.sections.watchEdit.errorLoadingWatchVisualizationTitle": "ウォッチビジュアライゼーションを読み込めません。", "xpack.watcher.sections.watchEdit.errorTitle": "ウォッチの読み込み中にエラーが発生しました", "xpack.watcher.sections.watchEdit.json.cancelButtonLabel": "キャンセル", @@ -29270,6 +29750,8 @@ "xpack.watcher.sections.watchList.createThresholdAlertButtonTooltip": "特定の条件でアラートを送信します", "xpack.watcher.sections.watchList.createWatchButtonLabel": "作成", "xpack.watcher.sections.watchList.deleteMultipleWatchesButtonLabel": "ウォッチを削除", + "xpack.watcher.sections.watchList.deleteSelectedWatchesErrorNotification.descriptionText": "{numErrors, number} {numErrors, plural, other {ウォッチ}}を削除できませんでした", + "xpack.watcher.sections.watchList.deleteSelectedWatchesSuccessNotification.descriptionText": "{numSuccesses, number} {numSuccesses, plural, other {ウォッチ}}を削除しました", "xpack.watcher.sections.watchList.deleteSingleWatchButtonLabel": "ウォッチを削除", "xpack.watcher.sections.watchList.emptyPromptTitle": "まだウォッチがありません", "xpack.watcher.sections.watchList.errorTitle": "ウォッチの読み込み中にエラーが発生しました", @@ -29312,6 +29794,10 @@ "xpack.watcher.thresholdWatchExpression.thresholdLevel.secondValueMustBeGreaterMessage": "値は{lowerBound}よりも大きい値でなければなりません。", "xpack.watcher.thresholdWatchExpression.thresholdLevel.valueIsRequiredValidationMessage": "値が必要です。", "xpack.watcher.thresholdWatchExpression.timeWindow.durationSizeIsRequiredValidationMessage": "ウィンドウ期間サイズが必要です。", + "xpack.watcher.timeUnits.dayLabel": "{timeValue, plural, other {日}}", + "xpack.watcher.timeUnits.hourLabel": "{timeValue, plural, other {時間}}", + "xpack.watcher.timeUnits.minuteLabel": "{timeValue, plural, other {分}}", + "xpack.watcher.timeUnits.secondLabel": "{timeValue, plural, other {秒}}", "xpack.watcher.watchActions.email.emailRecipientIsRequiredValidationMessage": "送信先メールアドレスが必要です。", "xpack.watcher.watchActions.jira.issueTypeNameIsRequiredValidationMessage": "Jira問題タイプが必要です。", "xpack.watcher.watchActions.jira.projectKeyIsRequiredValidationMessage": "Jiraプロジェクトキーが必要です。", From 1b75f2e705eaf75065bc7816109511125dc24f03 Mon Sep 17 00:00:00 2001 From: Julia Bardi <90178898+juliaElastic@users.noreply.github.com> Date: Wed, 23 Feb 2022 18:07:06 +0100 Subject: [PATCH 019/137] added support for dimension field (#126257) --- .../elasticsearch/template/template.test.ts | 25 +++++++++++++++++++ .../epm/elasticsearch/template/template.ts | 3 +++ .../fleet/server/services/epm/fields/field.ts | 1 + 3 files changed, 29 insertions(+) diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.test.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.test.ts index 7999cfa40a11aa..5d144435bbee11 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.test.ts @@ -689,6 +689,31 @@ describe('EPM template', () => { expect(JSON.stringify(mappings)).toEqual(JSON.stringify(constantKeywordMapping)); }); + it('tests processing dimension field', () => { + const literalYml = ` +- name: example.id + type: keyword + dimension: true + `; + const expectedMapping = { + properties: { + example: { + properties: { + id: { + ignore_above: 1024, + time_series_dimension: true, + type: 'keyword', + }, + }, + }, + }, + }; + const fields: Field[] = safeLoad(literalYml); + const processedFields = processFields(fields); + const mappings = generateMappings(processedFields); + expect(mappings).toEqual(expectedMapping); + }); + it('processes meta fields', () => { const metaFieldLiteralYaml = ` - name: fieldWithMetas diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.ts index a144663546ef31..f88f5aeb1c727b 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.ts @@ -264,6 +264,9 @@ function generateKeywordMapping(field: Field): IndexTemplateMapping { if (field.normalizer) { mapping.normalizer = field.normalizer; } + if (field.dimension) { + mapping.time_series_dimension = field.dimension; + } return mapping; } diff --git a/x-pack/plugins/fleet/server/services/epm/fields/field.ts b/x-pack/plugins/fleet/server/services/epm/fields/field.ts index 06ff858df67862..f1ad96504594e9 100644 --- a/x-pack/plugins/fleet/server/services/epm/fields/field.ts +++ b/x-pack/plugins/fleet/server/services/epm/fields/field.ts @@ -35,6 +35,7 @@ export interface Field { include_in_parent?: boolean; include_in_root?: boolean; null_value?: string; + dimension?: boolean; // Meta fields metric_type?: string; From 7c1e67cafb5aa7b532d9bbffa9c5131e0b6bad02 Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Wed, 23 Feb 2022 12:20:26 -0500 Subject: [PATCH 020/137] [Alerting] Track number of queries, query and search time during each rule execution. (#125096) * Wrapping es scoped cluster client with no abortcontroller * Change code for wrapping * Adding basic logging and metrics * Keeping track of number of queries, total search time, total query time and debug logging queries * Persisting to event log * Adding to functional tests * Renaming some things * Cleanup * Cleanup * Cleanup * Renaming things * Adding functional tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/alerting/common/alert.ts | 2 + .../alerting/common/rule_task_instance.ts | 10 +- x-pack/plugins/alerting/server/lib/index.ts | 1 + .../server/lib/rule_execution_status.test.ts | 21 ++- .../server/lib/rule_execution_status.ts | 5 +- .../lib/wrap_scoped_cluster_client.test.ts | 155 ++++++++++++++++ .../server/lib/wrap_scoped_cluster_client.ts | 169 ++++++++++++++++++ x-pack/plugins/alerting/server/plugin.ts | 23 +-- .../server/task_runner/task_runner.test.ts | 97 ++++++++-- .../server/task_runner/task_runner.ts | 107 +++++++---- .../task_runner/task_runner_cancel.test.ts | 36 +++- .../task_runner/task_runner_factory.test.ts | 11 +- .../server/task_runner/task_runner_factory.ts | 6 +- x-pack/plugins/alerting/server/types.ts | 23 ++- .../plugins/event_log/generated/mappings.json | 6 + x-pack/plugins/event_log/generated/schemas.ts | 2 + x-pack/plugins/event_log/scripts/mappings.js | 6 + .../triggers_actions_ui/public/types.ts | 2 - .../plugins/alerts/server/alert_types.ts | 52 ++++++ .../tests/alerting/alerts.ts | 3 + .../spaces_only/tests/alerting/event_log.ts | 116 +++++++++++- 21 files changed, 746 insertions(+), 107 deletions(-) create mode 100644 x-pack/plugins/alerting/server/lib/wrap_scoped_cluster_client.test.ts create mode 100644 x-pack/plugins/alerting/server/lib/wrap_scoped_cluster_client.ts diff --git a/x-pack/plugins/alerting/common/alert.ts b/x-pack/plugins/alerting/common/alert.ts index 933e51d6a8174c..35058aa343b1a1 100644 --- a/x-pack/plugins/alerting/common/alert.ts +++ b/x-pack/plugins/alerting/common/alert.ts @@ -10,6 +10,7 @@ import { SavedObjectAttributes, SavedObjectsResolveResponse, } from 'kibana/server'; +import { RuleExecutionMetrics } from '.'; import { AlertNotifyWhenType } from './alert_notify_when_type'; export type AlertTypeState = Record; @@ -37,6 +38,7 @@ export enum AlertExecutionStatusErrorReasons { export interface AlertExecutionStatus { status: AlertExecutionStatuses; numberOfTriggeredActions?: number; + metrics?: RuleExecutionMetrics; lastExecutionDate: Date; lastDuration?: number; error?: { diff --git a/x-pack/plugins/alerting/common/rule_task_instance.ts b/x-pack/plugins/alerting/common/rule_task_instance.ts index 10d71591e6448f..59ed9097f16755 100644 --- a/x-pack/plugins/alerting/common/rule_task_instance.ts +++ b/x-pack/plugins/alerting/common/rule_task_instance.ts @@ -22,8 +22,16 @@ export const ruleStateSchema = t.partial({ previousStartedAt: t.union([t.null, DateFromString]), }); +const ruleExecutionMetricsSchema = t.partial({ + numSearches: t.number, + totalSearchDurationMs: t.number, + esSearchDurationMs: t.number, +}); + +export type RuleExecutionMetrics = t.TypeOf; export type RuleTaskState = t.TypeOf; -export type RuleTaskStateWithActions = RuleTaskState & { +export type RuleExecutionState = RuleTaskState & { + metrics: RuleExecutionMetrics; triggeredActions: Array>; }; diff --git a/x-pack/plugins/alerting/server/lib/index.ts b/x-pack/plugins/alerting/server/lib/index.ts index a5fa1b29c3044a..d9692bac86c164 100644 --- a/x-pack/plugins/alerting/server/lib/index.ts +++ b/x-pack/plugins/alerting/server/lib/index.ts @@ -25,3 +25,4 @@ export { ruleExecutionStatusFromRaw, } from './rule_execution_status'; export { getRecoveredAlerts } from './get_recovered_alerts'; +export { createWrappedScopedClusterClientFactory } from './wrap_scoped_cluster_client'; diff --git a/x-pack/plugins/alerting/server/lib/rule_execution_status.test.ts b/x-pack/plugins/alerting/server/lib/rule_execution_status.test.ts index 2251d9b0b25615..be1ce11bad1325 100644 --- a/x-pack/plugins/alerting/server/lib/rule_execution_status.test.ts +++ b/x-pack/plugins/alerting/server/lib/rule_execution_status.test.ts @@ -6,7 +6,7 @@ */ import { loggingSystemMock } from '../../../../../src/core/server/mocks'; -import { AlertAction, AlertExecutionStatusErrorReasons, RuleTaskStateWithActions } from '../types'; +import { AlertAction, AlertExecutionStatusErrorReasons, RuleExecutionState } from '../types'; import { executionStatusFromState, executionStatusFromError, @@ -16,6 +16,7 @@ import { import { ErrorWithReason } from './error_with_reason'; const MockLogger = loggingSystemMock.create().get(); +const metrics = { numSearches: 1, esSearchDurationMs: 10, totalSearchDurationMs: 20 }; describe('RuleExecutionStatus', () => { beforeEach(() => { @@ -24,7 +25,7 @@ describe('RuleExecutionStatus', () => { describe('executionStatusFromState()', () => { test('empty task state', () => { - const status = executionStatusFromState({} as RuleTaskStateWithActions); + const status = executionStatusFromState({} as RuleExecutionState); checkDateIsNearNow(status.lastExecutionDate); expect(status.numberOfTriggeredActions).toBe(0); expect(status.status).toBe('ok'); @@ -32,30 +33,42 @@ describe('RuleExecutionStatus', () => { }); test('task state with no instances', () => { - const status = executionStatusFromState({ alertInstances: {}, triggeredActions: [] }); + const status = executionStatusFromState({ + alertInstances: {}, + triggeredActions: [], + metrics, + }); checkDateIsNearNow(status.lastExecutionDate); expect(status.numberOfTriggeredActions).toBe(0); expect(status.status).toBe('ok'); expect(status.error).toBe(undefined); + expect(status.metrics).toBe(metrics); }); test('task state with one instance', () => { - const status = executionStatusFromState({ alertInstances: { a: {} }, triggeredActions: [] }); + const status = executionStatusFromState({ + alertInstances: { a: {} }, + triggeredActions: [], + metrics, + }); checkDateIsNearNow(status.lastExecutionDate); expect(status.numberOfTriggeredActions).toBe(0); expect(status.status).toBe('active'); expect(status.error).toBe(undefined); + expect(status.metrics).toBe(metrics); }); test('task state with numberOfTriggeredActions', () => { const status = executionStatusFromState({ triggeredActions: [{ group: '1' } as AlertAction], alertInstances: { a: {} }, + metrics, }); checkDateIsNearNow(status.lastExecutionDate); expect(status.numberOfTriggeredActions).toBe(1); expect(status.status).toBe('active'); expect(status.error).toBe(undefined); + expect(status.metrics).toBe(metrics); }); }); diff --git a/x-pack/plugins/alerting/server/lib/rule_execution_status.ts b/x-pack/plugins/alerting/server/lib/rule_execution_status.ts index 5de59dd5172299..129da2b8478aa6 100644 --- a/x-pack/plugins/alerting/server/lib/rule_execution_status.ts +++ b/x-pack/plugins/alerting/server/lib/rule_execution_status.ts @@ -6,14 +6,15 @@ */ import { Logger } from 'src/core/server'; -import { AlertExecutionStatus, RawRuleExecutionStatus, RuleTaskStateWithActions } from '../types'; +import { AlertExecutionStatus, RawRuleExecutionStatus, RuleExecutionState } from '../types'; import { getReasonFromError } from './error_with_reason'; import { getEsErrorMessage } from './errors'; import { AlertExecutionStatuses } from '../../common'; -export function executionStatusFromState(state: RuleTaskStateWithActions): AlertExecutionStatus { +export function executionStatusFromState(state: RuleExecutionState): AlertExecutionStatus { const alertIds = Object.keys(state.alertInstances ?? {}); return { + metrics: state.metrics, numberOfTriggeredActions: state.triggeredActions?.length ?? 0, lastExecutionDate: new Date(), status: alertIds.length === 0 ? 'ok' : 'active', diff --git a/x-pack/plugins/alerting/server/lib/wrap_scoped_cluster_client.test.ts b/x-pack/plugins/alerting/server/lib/wrap_scoped_cluster_client.test.ts new file mode 100644 index 00000000000000..94daa0030cd600 --- /dev/null +++ b/x-pack/plugins/alerting/server/lib/wrap_scoped_cluster_client.test.ts @@ -0,0 +1,155 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Client } from '@elastic/elasticsearch'; +import { loggingSystemMock } from 'src/core/server/mocks'; +import { elasticsearchServiceMock } from '../../../../../src/core/server/mocks'; +import { createWrappedScopedClusterClientFactory } from './wrap_scoped_cluster_client'; +import { ElasticsearchClientWithChild } from '../types'; + +const esQuery = { + body: { query: { bool: { filter: { range: { '@timestamp': { gte: 0 } } } } } }, +}; + +const logger = loggingSystemMock.create().get(); + +const rule = { + name: 'test-rule', + alertTypeId: '.test-rule-type', + id: 'abcdefg', + spaceId: 'my-space', +}; + +describe('wrapScopedClusterClient', () => { + beforeAll(() => { + jest.useFakeTimers(); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + test('searches with asInternalUser when specified', async () => { + const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + const childClient = elasticsearchServiceMock.createElasticsearchClient(); + + ( + scopedClusterClient.asInternalUser as unknown as jest.Mocked + ).child.mockReturnValue(childClient as unknown as Client); + const asInternalUserWrappedSearchFn = childClient.search; + + const wrappedSearchClient = createWrappedScopedClusterClientFactory({ + scopedClusterClient, + rule, + logger, + }).client(); + await wrappedSearchClient.asInternalUser.search(esQuery); + + expect(asInternalUserWrappedSearchFn).toHaveBeenCalledWith(esQuery, {}); + expect(scopedClusterClient.asInternalUser.search).not.toHaveBeenCalled(); + expect(scopedClusterClient.asCurrentUser.search).not.toHaveBeenCalled(); + }); + + test('searches with asCurrentUser when specified', async () => { + const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + const childClient = elasticsearchServiceMock.createElasticsearchClient(); + + ( + scopedClusterClient.asCurrentUser as unknown as jest.Mocked + ).child.mockReturnValue(childClient as unknown as Client); + const asCurrentUserWrappedSearchFn = childClient.search; + + const wrappedSearchClient = createWrappedScopedClusterClientFactory({ + scopedClusterClient, + rule, + logger, + }).client(); + await wrappedSearchClient.asCurrentUser.search(esQuery); + + expect(asCurrentUserWrappedSearchFn).toHaveBeenCalledWith(esQuery, {}); + expect(scopedClusterClient.asInternalUser.search).not.toHaveBeenCalled(); + expect(scopedClusterClient.asCurrentUser.search).not.toHaveBeenCalled(); + }); + + test('uses search options when specified', async () => { + const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + const childClient = elasticsearchServiceMock.createElasticsearchClient(); + + ( + scopedClusterClient.asInternalUser as unknown as jest.Mocked + ).child.mockReturnValue(childClient as unknown as Client); + const asInternalUserWrappedSearchFn = childClient.search; + + const wrappedSearchClient = createWrappedScopedClusterClientFactory({ + scopedClusterClient, + rule, + logger, + }).client(); + await wrappedSearchClient.asInternalUser.search(esQuery, { ignore: [404] }); + + expect(asInternalUserWrappedSearchFn).toHaveBeenCalledWith(esQuery, { + ignore: [404], + }); + expect(scopedClusterClient.asInternalUser.search).not.toHaveBeenCalled(); + expect(scopedClusterClient.asCurrentUser.search).not.toHaveBeenCalled(); + }); + + test('re-throws error when search throws error', async () => { + const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + const childClient = elasticsearchServiceMock.createElasticsearchClient(); + + ( + scopedClusterClient.asInternalUser as unknown as jest.Mocked + ).child.mockReturnValue(childClient as unknown as Client); + const asInternalUserWrappedSearchFn = childClient.search; + + asInternalUserWrappedSearchFn.mockRejectedValueOnce(new Error('something went wrong!')); + const wrappedSearchClient = createWrappedScopedClusterClientFactory({ + scopedClusterClient, + rule, + logger, + }).client(); + + await expect( + wrappedSearchClient.asInternalUser.search + ).rejects.toThrowErrorMatchingInlineSnapshot(`"something went wrong!"`); + }); + + test('keeps track of number of queries', async () => { + const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + const childClient = elasticsearchServiceMock.createElasticsearchClient(); + + ( + scopedClusterClient.asInternalUser as unknown as jest.Mocked + ).child.mockReturnValue(childClient as unknown as Client); + const asInternalUserWrappedSearchFn = childClient.search; + // @ts-ignore incomplete return type + asInternalUserWrappedSearchFn.mockResolvedValue({ took: 333 }); + + const wrappedSearchClientFactory = createWrappedScopedClusterClientFactory({ + scopedClusterClient, + rule, + logger, + }); + const wrappedSearchClient = wrappedSearchClientFactory.client(); + await wrappedSearchClient.asInternalUser.search(esQuery); + await wrappedSearchClient.asInternalUser.search(esQuery); + await wrappedSearchClient.asInternalUser.search(esQuery); + + expect(asInternalUserWrappedSearchFn).toHaveBeenCalledTimes(3); + expect(scopedClusterClient.asInternalUser.search).not.toHaveBeenCalled(); + expect(scopedClusterClient.asCurrentUser.search).not.toHaveBeenCalled(); + + const stats = wrappedSearchClientFactory.getMetrics(); + expect(stats.numSearches).toEqual(3); + expect(stats.esSearchDurationMs).toEqual(999); + + expect(logger.debug).toHaveBeenCalledWith( + `executing query for rule .test-rule-type:abcdefg in space my-space - {\"body\":{\"query\":{\"bool\":{\"filter\":{\"range\":{\"@timestamp\":{\"gte\":0}}}}}}} - with options {}` + ); + }); +}); diff --git a/x-pack/plugins/alerting/server/lib/wrap_scoped_cluster_client.ts b/x-pack/plugins/alerting/server/lib/wrap_scoped_cluster_client.ts new file mode 100644 index 00000000000000..00ee3302c88c51 --- /dev/null +++ b/x-pack/plugins/alerting/server/lib/wrap_scoped_cluster_client.ts @@ -0,0 +1,169 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + TransportRequestOptions, + TransportResult, + TransportRequestOptionsWithMeta, + TransportRequestOptionsWithOutMeta, +} from '@elastic/elasticsearch'; +import type { + SearchRequest, + SearchResponse, + AggregateName, +} from '@elastic/elasticsearch/lib/api/types'; +import type { + SearchRequest as SearchRequestWithBody, + AggregationsAggregate, +} from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { IScopedClusterClient, ElasticsearchClient, Logger } from 'src/core/server'; +import { ElasticsearchClientWithChild, RuleExecutionMetrics } from '../types'; +import { Alert as Rule } from '../types'; + +type RuleInfo = Pick & { spaceId: string }; +interface WrapScopedClusterClientFactoryOpts { + scopedClusterClient: IScopedClusterClient; + rule: RuleInfo; + logger: Logger; +} + +type WrapScopedClusterClientOpts = WrapScopedClusterClientFactoryOpts & { + logMetricsFn: LogSearchMetricsFn; +}; + +type WrapEsClientOpts = Omit & { + esClient: ElasticsearchClient; +}; + +interface LogSearchMetricsOpts { + esSearchDuration: number; + totalSearchDuration: number; +} +type LogSearchMetricsFn = (metrics: LogSearchMetricsOpts) => void; + +export function createWrappedScopedClusterClientFactory(opts: WrapScopedClusterClientFactoryOpts) { + let numSearches: number = 0; + let esSearchDurationMs: number = 0; + let totalSearchDurationMs: number = 0; + + function logMetrics(metrics: LogSearchMetricsOpts) { + numSearches++; + esSearchDurationMs += metrics.esSearchDuration; + totalSearchDurationMs += metrics.totalSearchDuration; + } + + const wrappedClient = wrapScopedClusterClient({ ...opts, logMetricsFn: logMetrics }); + + return { + client: () => wrappedClient, + getMetrics: (): RuleExecutionMetrics => { + return { + esSearchDurationMs, + totalSearchDurationMs, + numSearches, + }; + }, + }; +} + +function wrapScopedClusterClient(opts: WrapScopedClusterClientOpts): IScopedClusterClient { + const { scopedClusterClient, ...rest } = opts; + return { + asInternalUser: wrapEsClient({ + ...rest, + esClient: scopedClusterClient.asInternalUser, + }), + asCurrentUser: wrapEsClient({ + ...rest, + esClient: scopedClusterClient.asCurrentUser, + }), + }; +} + +function wrapEsClient(opts: WrapEsClientOpts): ElasticsearchClient { + const { esClient, ...rest } = opts; + + // Core hides access to .child via TS + const wrappedClient = (esClient as ElasticsearchClientWithChild).child({}); + + // Mutating the functions we want to wrap + wrappedClient.search = getWrappedSearchFn({ esClient: wrappedClient, ...rest }); + + return wrappedClient; +} + +function getWrappedSearchFn(opts: WrapEsClientOpts) { + const originalSearch = opts.esClient.search; + + // A bunch of overloads to make TypeScript happy + async function search< + TDocument = unknown, + TAggregations = Record + >( + params?: SearchRequest | SearchRequestWithBody, + options?: TransportRequestOptionsWithOutMeta + ): Promise>; + async function search< + TDocument = unknown, + TAggregations = Record + >( + params?: SearchRequest | SearchRequestWithBody, + options?: TransportRequestOptionsWithMeta + ): Promise, unknown>>; + async function search< + TDocument = unknown, + TAggregations = Record + >( + params?: SearchRequest | SearchRequestWithBody, + options?: TransportRequestOptions + ): Promise>; + async function search< + TDocument = unknown, + TAggregations = Record + >( + params?: SearchRequest | SearchRequestWithBody, + options?: TransportRequestOptions + ): Promise< + | TransportResult, unknown> + | SearchResponse + > { + try { + const searchOptions = options ?? {}; + const start = Date.now(); + opts.logger.debug( + `executing query for rule ${opts.rule.alertTypeId}:${opts.rule.id} in space ${ + opts.rule.spaceId + } - ${JSON.stringify(params)} - with options ${JSON.stringify(searchOptions)}` + ); + const result = (await originalSearch.call(opts.esClient, params, { + ...searchOptions, + })) as + | TransportResult, unknown> + | SearchResponse; + + const end = Date.now(); + const durationMs = end - start; + + let took = 0; + if (searchOptions.meta) { + // when meta: true, response is TransportResult, unknown> + took = (result as TransportResult, unknown>).body + .took; + } else { + // when meta: false, response is SearchResponse + took = (result as SearchResponse).took; + } + + opts.logMetricsFn({ esSearchDuration: took, totalSearchDuration: durationMs }); + return result; + } catch (e) { + throw e; + } + } + + return search; +} diff --git a/x-pack/plugins/alerting/server/plugin.ts b/x-pack/plugins/alerting/server/plugin.ts index ac3253346138a2..b68ee6f436d6f4 100644 --- a/x-pack/plugins/alerting/server/plugin.ts +++ b/x-pack/plugins/alerting/server/plugin.ts @@ -27,9 +27,7 @@ import { PluginInitializerContext, CoreSetup, CoreStart, - SavedObjectsServiceStart, IContextProvider, - ElasticsearchServiceStart, StatusServiceSetup, ServiceStatus, SavedObjectsBulkGetObject, @@ -49,7 +47,6 @@ import { RuleType, AlertTypeParams, AlertTypeState, - Services, } from './types'; import { registerAlertingUsageCollector } from './usage'; import { initializeAlertingTelemetry, scheduleAlertingTelemetry } from './usage/task'; @@ -381,7 +378,8 @@ export class AlertingPlugin { this.config.then((config) => { taskRunnerFactory.initialize({ logger, - getServices: this.getServicesFactory(core.savedObjects, core.elasticsearch), + savedObjects: core.savedObjects, + elasticsearch: core.elasticsearch, getRulesClientWithRequest, spaceIdToNamespace, actionsPlugin: plugins.actions, @@ -444,23 +442,6 @@ export class AlertingPlugin { }; }; - private getServicesFactory( - savedObjects: SavedObjectsServiceStart, - elasticsearch: ElasticsearchServiceStart - ): (request: KibanaRequest) => Services { - return (request) => ({ - savedObjectsClient: this.getScopedClientWithAlertSavedObjectType(savedObjects, request), - scopedClusterClient: elasticsearch.client.asScoped(request), - }); - } - - private getScopedClientWithAlertSavedObjectType( - savedObjects: SavedObjectsServiceStart, - request: KibanaRequest - ) { - return savedObjects.getScopedClient(request, { includedHiddenTypes: ['alert', 'action'] }); - } - public stop() { if (this.licenseState) { this.licenseState.clean(); diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts index 679531695c31ba..7496cdf7fd3367 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts @@ -29,6 +29,8 @@ import { savedObjectsRepositoryMock, httpServiceMock, executionContextServiceMock, + savedObjectsServiceMock, + elasticsearchServiceMock, } from '../../../../../src/core/server/mocks'; import { PluginStartContract as ActionsPluginStart } from '../../../actions/server'; import { actionsMock, actionsClientMock } from '../../../actions/server/mocks'; @@ -46,6 +48,9 @@ import moment from 'moment'; jest.mock('uuid', () => ({ v4: () => '5f6aa57d-3e22-484e-bae8-cbed868f4d28', })); +jest.mock('../lib/wrap_scoped_cluster_client', () => ({ + createWrappedScopedClusterClientFactory: jest.fn(), +})); const ruleType: jest.Mocked = { id: 'test', @@ -95,6 +100,8 @@ describe('Task Runner', () => { const actionsClient = actionsClientMock.create(); const rulesClient = rulesClientMock.create(); const ruleTypeRegistry = ruleTypeRegistryMock.create(); + const savedObjectsService = savedObjectsServiceMock.createInternalStartContract(); + const elasticsearchService = elasticsearchServiceMock.createInternalStart(); type TaskRunnerFactoryInitializerParamsType = jest.Mocked & { actionsPlugin: jest.Mocked; @@ -105,7 +112,8 @@ describe('Task Runner', () => { type EnqueueFunction = (options: ExecuteOptions) => Promise; const taskRunnerFactoryInitializerParams: TaskRunnerFactoryInitializerParamsType = { - getServices: jest.fn().mockReturnValue(services), + savedObjects: savedObjectsService, + elasticsearch: elasticsearchService, actionsPlugin: actionsMock.createStart(), getRulesClientWithRequest: jest.fn().mockReturnValue(rulesClient), encryptedSavedObjectsClient, @@ -193,7 +201,18 @@ describe('Task Runner', () => { beforeEach(() => { jest.resetAllMocks(); - taskRunnerFactoryInitializerParams.getServices.mockReturnValue(services); + jest + .requireMock('../lib/wrap_scoped_cluster_client') + .createWrappedScopedClusterClientFactory.mockReturnValue({ + client: () => services.scopedClusterClient, + getMetrics: () => ({ + numSearches: 3, + esSearchDurationMs: 33, + totalSearchDurationMs: 23423, + }), + }); + savedObjectsService.getScopedClient.mockReturnValue(services.savedObjectsClient); + elasticsearchService.client.asScoped.mockReturnValue(services.scopedClusterClient); taskRunnerFactoryInitializerParams.getRulesClientWithRequest.mockReturnValue(rulesClient); taskRunnerFactoryInitializerParams.actionsPlugin.getActionsClientWithRequest.mockResolvedValue( actionsClient @@ -319,7 +338,7 @@ describe('Task Runner', () => { expect(logger.debug).nthCalledWith(1, 'executing rule test:1 at 1970-01-01T00:00:00.000Z'); expect(logger.debug).nthCalledWith( 2, - 'ruleExecutionStatus for test:1: {"numberOfTriggeredActions":0,"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"ok"}' + 'ruleExecutionStatus for test:1: {"metrics":{"numSearches":3,"esSearchDurationMs":33,"totalSearchDurationMs":23423},"numberOfTriggeredActions":0,"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"ok"}' ); const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; @@ -406,6 +425,9 @@ describe('Task Runner', () => { expect.any(Function) ); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); + expect( + jest.requireMock('../lib/wrap_scoped_cluster_client').createWrappedScopedClusterClientFactory + ).toHaveBeenCalled(); }); test.each(ephemeralTestParams)( @@ -488,7 +510,7 @@ describe('Task Runner', () => { ); expect(logger.debug).nthCalledWith( 3, - 'ruleExecutionStatus for test:1: {"numberOfTriggeredActions":1,"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"active"}' + 'ruleExecutionStatus for test:1: {"metrics":{"numSearches":3,"esSearchDurationMs":33,"totalSearchDurationMs":23423},"numberOfTriggeredActions":1,"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"active"}' ); // ruleExecutionStatus for test:1: {\"lastExecutionDate\":\"1970-01-01T00:00:00.000Z\",\"status\":\"error\",\"error\":{\"reason\":\"unknown\",\"message\":\"Cannot read property 'catch' of undefined\"}} @@ -661,7 +683,10 @@ describe('Task Runner', () => { execution: { uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', metrics: { + number_of_searches: 3, number_of_triggered_actions: 1, + es_search_duration_ms: 33, + total_search_duration_ms: 23423, }, }, }, @@ -746,7 +771,7 @@ describe('Task Runner', () => { ); expect(logger.debug).nthCalledWith( 4, - 'ruleExecutionStatus for test:1: {"numberOfTriggeredActions":0,"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"active"}' + 'ruleExecutionStatus for test:1: {"metrics":{"numSearches":3,"esSearchDurationMs":33,"totalSearchDurationMs":23423},"numberOfTriggeredActions":0,"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"active"}' ); const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; @@ -881,7 +906,10 @@ describe('Task Runner', () => { execution: { uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', metrics: { + number_of_searches: 3, number_of_triggered_actions: 0, + es_search_duration_ms: 33, + total_search_duration_ms: 23423, }, }, }, @@ -973,7 +1001,7 @@ describe('Task Runner', () => { ); expect(logger.debug).nthCalledWith( 4, - 'ruleExecutionStatus for test:1: {"numberOfTriggeredActions":1,"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"active"}' + 'ruleExecutionStatus for test:1: {"metrics":{"numSearches":3,"esSearchDurationMs":33,"totalSearchDurationMs":23423},"numberOfTriggeredActions":1,"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"active"}' ); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); } @@ -1257,7 +1285,10 @@ describe('Task Runner', () => { "rule": Object { "execution": Object { "metrics": Object { + "es_search_duration_ms": 33, + "number_of_searches": 3, "number_of_triggered_actions": 0, + "total_search_duration_ms": 23423, }, "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", }, @@ -1358,7 +1389,12 @@ describe('Task Runner', () => { alert: expect.objectContaining({ rule: expect.objectContaining({ execution: expect.objectContaining({ - metrics: expect.objectContaining({ number_of_triggered_actions: 1 }), + metrics: expect.objectContaining({ + number_of_searches: 3, + number_of_triggered_actions: 1, + es_search_duration_ms: 33, + total_search_duration_ms: 23423, + }), }), }), }), @@ -1440,7 +1476,12 @@ describe('Task Runner', () => { alert: expect.objectContaining({ rule: expect.objectContaining({ execution: expect.objectContaining({ - metrics: expect.objectContaining({ number_of_triggered_actions: 1 }), + metrics: expect.objectContaining({ + number_of_searches: 3, + number_of_triggered_actions: 1, + es_search_duration_ms: 33, + total_search_duration_ms: 23423, + }), }), }), }), @@ -1734,7 +1775,10 @@ describe('Task Runner', () => { "rule": Object { "execution": Object { "metrics": Object { + "es_search_duration_ms": 33, + "number_of_searches": 3, "number_of_triggered_actions": 1, + "total_search_duration_ms": 23423, }, "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", }, @@ -1868,7 +1912,7 @@ describe('Task Runner', () => { ); expect(logger.debug).nthCalledWith( 4, - 'ruleExecutionStatus for test:1: {"numberOfTriggeredActions":2,"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"active"}' + 'ruleExecutionStatus for test:1: {"metrics":{"numSearches":3,"esSearchDurationMs":33,"totalSearchDurationMs":23423},"numberOfTriggeredActions":2,"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"active"}' ); const eventLogger = customTaskRunnerFactoryInitializerParams.eventLogger; @@ -2111,7 +2155,10 @@ describe('Task Runner', () => { "rule": Object { "execution": Object { "metrics": Object { + "es_search_duration_ms": 33, + "number_of_searches": 3, "number_of_triggered_actions": 2, + "total_search_duration_ms": 23423, }, "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", }, @@ -2264,7 +2311,7 @@ describe('Task Runner', () => { ); expect(logger.debug).nthCalledWith( 4, - `ruleExecutionStatus for test:${alertId}: {"numberOfTriggeredActions":2,"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"active"}` + `ruleExecutionStatus for test:${alertId}: {"metrics":{"numSearches":3,"esSearchDurationMs":33,"totalSearchDurationMs":23423},"numberOfTriggeredActions":2,"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"active"}` ); const eventLogger = customTaskRunnerFactoryInitializerParams.eventLogger; @@ -2628,7 +2675,10 @@ describe('Task Runner', () => { "rule": Object { "execution": Object { "metrics": Object { + "es_search_duration_ms": 33, + "number_of_searches": 3, "number_of_triggered_actions": 0, + "total_search_duration_ms": 23423, }, "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", }, @@ -2741,7 +2791,7 @@ describe('Task Runner', () => { }); await taskRunner.run(); - expect(taskRunnerFactoryInitializerParams.getServices).toHaveBeenCalledWith( + expect(taskRunnerFactoryInitializerParams.getRulesClientWithRequest).toHaveBeenCalledWith( expect.objectContaining({ headers: { // base64 encoded "123:abc" @@ -2749,7 +2799,7 @@ describe('Task Runner', () => { }, }) ); - const [request] = taskRunnerFactoryInitializerParams.getServices.mock.calls[0]; + const [request] = taskRunnerFactoryInitializerParams.getRulesClientWithRequest.mock.calls[0]; expect(taskRunnerFactoryInitializerParams.basePathService.set).toHaveBeenCalledWith( request, @@ -2776,13 +2826,13 @@ describe('Task Runner', () => { await taskRunner.run(); - expect(taskRunnerFactoryInitializerParams.getServices).toHaveBeenCalledWith( + expect(taskRunnerFactoryInitializerParams.getRulesClientWithRequest).toHaveBeenCalledWith( expect.objectContaining({ headers: {}, }) ); - const [request] = taskRunnerFactoryInitializerParams.getServices.mock.calls[0]; + const [request] = taskRunnerFactoryInitializerParams.getRulesClientWithRequest.mock.calls[0]; expect(taskRunnerFactoryInitializerParams.basePathService.set).toHaveBeenCalledWith( request, @@ -3276,7 +3326,7 @@ describe('Task Runner', () => { }); test('recovers gracefully when the Alert Task Runner throws an exception when getting internal Services', async () => { - taskRunnerFactoryInitializerParams.getServices.mockImplementation(() => { + taskRunnerFactoryInitializerParams.getRulesClientWithRequest.mockImplementation(() => { throw new Error('OMG'); }); @@ -4077,7 +4127,10 @@ describe('Task Runner', () => { "rule": Object { "execution": Object { "metrics": Object { + "es_search_duration_ms": 33, + "number_of_searches": 3, "number_of_triggered_actions": 0, + "total_search_duration_ms": 23423, }, "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", }, @@ -4322,7 +4375,10 @@ describe('Task Runner', () => { "rule": Object { "execution": Object { "metrics": Object { + "es_search_duration_ms": 33, + "number_of_searches": 3, "number_of_triggered_actions": 0, + "total_search_duration_ms": 23423, }, "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", }, @@ -4555,7 +4611,10 @@ describe('Task Runner', () => { "rule": Object { "execution": Object { "metrics": Object { + "es_search_duration_ms": 33, + "number_of_searches": 3, "number_of_triggered_actions": 0, + "total_search_duration_ms": 23423, }, "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", }, @@ -4787,7 +4846,10 @@ describe('Task Runner', () => { "rule": Object { "execution": Object { "metrics": Object { + "es_search_duration_ms": 33, + "number_of_searches": 3, "number_of_triggered_actions": 0, + "total_search_duration_ms": 23423, }, "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", }, @@ -5015,7 +5077,10 @@ describe('Task Runner', () => { "rule": Object { "execution": Object { "metrics": Object { + "es_search_duration_ms": 33, + "number_of_searches": 3, "number_of_triggered_actions": 0, + "total_search_duration_ms": 23423, }, "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", }, @@ -5166,7 +5231,7 @@ describe('Task Runner', () => { expect(logger.debug).nthCalledWith(1, 'executing rule test:1 at 1970-01-01T00:00:00.000Z'); expect(logger.debug).nthCalledWith( 2, - 'ruleExecutionStatus for test:1: {"numberOfTriggeredActions":0,"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"ok"}' + 'ruleExecutionStatus for test:1: {"metrics":{"numSearches":3,"esSearchDurationMs":33,"totalSearchDurationMs":23423},"numberOfTriggeredActions":0,"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"ok"}' ); const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index 8bc4ad280873ef..c5651dcf4f57b0 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -5,7 +5,6 @@ * 2.0. */ import apm from 'elastic-apm-node'; -import type { PublicMethodsOf } from '@kbn/utility-types'; import { Dictionary, pickBy, mapValues, without, cloneDeep, concat, set, omit } from 'lodash'; import type { Request } from '@hapi/hapi'; import { UsageCounter } from 'src/plugins/usage_collection/server'; @@ -27,7 +26,6 @@ import { import { RawRule, IntervalSchedule, - Services, RawAlertInstance, RuleTaskState, Alert, @@ -39,7 +37,7 @@ import { RuleMonitoringHistory, RawRuleExecutionStatus, AlertAction, - RuleTaskStateWithActions, + RuleExecutionState, } from '../types'; import { promiseResult, map, Resultable, asOk, asErr, resolveErr } from '../lib/result_type'; import { getExecutionSuccessRatio, getExecutionDurationPercentiles } from '../lib/monitoring'; @@ -47,7 +45,6 @@ import { taskInstanceToAlertTaskInstance } from './alert_task_instance'; import { EVENT_LOG_ACTIONS } from '../plugin'; import { IEvent, IEventLogger, SAVED_OBJECT_REL_PRIMARY } from '../../../event_log/server'; import { isAlertSavedObjectNotFoundError, isEsUnavailableError } from '../lib/is_alerting_error'; -import { RulesClient } from '../rules_client'; import { partiallyUpdateAlert } from '../saved_objects'; import { ActionGroup, @@ -66,6 +63,7 @@ import { Event, } from '../lib/create_alert_event_log_record_object'; import { createAbortableEsClientFactory } from '../lib/create_abortable_es_client_factory'; +import { createWrappedScopedClusterClientFactory } from '../lib'; import { getRecoveredAlerts } from '../lib'; const FALLBACK_RETRY_INTERVAL = '5m'; @@ -83,8 +81,8 @@ export const getDefaultRuleMonitoring = (): RuleMonitoring => ({ }, }); -interface RuleTaskRunResultWithActions { - state: RuleTaskStateWithActions; +interface RuleExecutionRunResult { + state: RuleExecutionState; monitoring: RuleMonitoring | undefined; schedule: IntervalSchedule | undefined; } @@ -198,14 +196,6 @@ export class TaskRunner< return fakeRequest; } - private getServicesWithSpaceLevelPermissions( - spaceId: string, - apiKey: RawRule['apiKey'] - ): [Services, PublicMethodsOf] { - const request = this.getFakeKibanaRequest(spaceId, apiKey); - return [this.context.getServices(request), this.context.getRulesClientWithRequest(request)]; - } - private getExecutionHandler( ruleId: string, ruleName: string, @@ -214,7 +204,8 @@ export class TaskRunner< apiKey: RawRule['apiKey'], kibanaBaseUrl: string | undefined, actions: Alert['actions'], - ruleParams: Params + ruleParams: Params, + request: KibanaRequest ) { return createExecutionHandler< Params, @@ -237,7 +228,7 @@ export class TaskRunner< ruleType: this.ruleType, kibanaBaseUrl, eventLogger: this.context.eventLogger, - request: this.getFakeKibanaRequest(spaceId, apiKey), + request, ruleParams, supportsEphemeralTasks: this.context.supportsEphemeralTasks, maxEphemeralActionsPerRule: this.context.maxEphemeralActionsPerRule, @@ -301,13 +292,13 @@ export class TaskRunner< } async executeAlerts( - services: Services, + fakeRequest: KibanaRequest, rule: SanitizedAlert, params: Params, executionHandler: ExecutionHandler, spaceId: string, event: Event - ): Promise { + ): Promise { const { alertTypeId, consumer, @@ -346,6 +337,18 @@ export class TaskRunner< const eventLogger = this.context.eventLogger; const ruleLabel = `${this.ruleType.id}:${ruleId}: '${name}'`; + const scopedClusterClient = this.context.elasticsearch.client.asScoped(fakeRequest); + const wrappedScopedClusterClient = createWrappedScopedClusterClientFactory({ + scopedClusterClient, + rule: { + name: rule.name, + alertTypeId: rule.alertTypeId, + id: rule.id, + spaceId, + }, + logger: this.logger, + }); + let updatedRuleTypeState: void | Record; try { const ctx = { @@ -362,7 +365,10 @@ export class TaskRunner< alertId: ruleId, executionId: this.executionId, services: { - ...services, + savedObjectsClient: this.context.savedObjects.getScopedClient(fakeRequest, { + includedHiddenTypes: ['alert', 'action'], + }), + scopedClusterClient: wrappedScopedClusterClient.client(), alertFactory: createAlertFactory< InstanceState, InstanceContext, @@ -375,7 +381,7 @@ export class TaskRunner< shouldWriteAlerts: () => this.shouldLogAndScheduleActionsForAlerts(), shouldStopExecution: () => this.cancelled, search: createAbortableEsClientFactory({ - scopedClusterClient: services.scopedClusterClient, + scopedClusterClient, abortController: this.searchAbortController, }), }, @@ -426,6 +432,8 @@ export class TaskRunner< name: rule.name, }; + const searchMetrics = wrappedScopedClusterClient.getMetrics(); + // Cleanup alerts that are no longer scheduling actions to avoid over populating the alertInstances object const alertsWithScheduledActions = pickBy( alerts, @@ -535,6 +543,7 @@ export class TaskRunner< } return { + metrics: searchMetrics, triggeredActions, alertTypeState: updatedRuleTypeState || undefined, alertInstances: mapValues< @@ -545,7 +554,7 @@ export class TaskRunner< } async validateAndExecuteRule( - services: Services, + fakeRequest: KibanaRequest, apiKey: RawRule['apiKey'], rule: SanitizedAlert, event: Event @@ -564,14 +573,13 @@ export class TaskRunner< apiKey, this.context.kibanaBaseUrl, rule.actions, - rule.params + rule.params, + fakeRequest ); - return this.executeAlerts(services, rule, validatedParams, executionHandler, spaceId, event); + return this.executeAlerts(fakeRequest, rule, validatedParams, executionHandler, spaceId, event); } - async loadRuleAttributesAndRun( - event: Event - ): Promise> { + async loadRuleAttributesAndRun(event: Event): Promise> { const { params: { alertId: ruleId, spaceId }, } = this.taskInstance; @@ -592,7 +600,10 @@ export class TaskRunner< ); } - const [services, rulesClient] = this.getServicesWithSpaceLevelPermissions(spaceId, apiKey); + const fakeRequest = this.getFakeKibanaRequest(spaceId, apiKey); + + // Get rules client with space level permissions + const rulesClient = this.context.getRulesClientWithRequest(fakeRequest); let rule: SanitizedAlert; @@ -630,8 +641,8 @@ export class TaskRunner< } return { monitoring: asOk(rule.monitoring), - state: await promiseResult( - this.validateAndExecuteRule(services, apiKey, rule, event) + state: await promiseResult( + this.validateAndExecuteRule(fakeRequest, apiKey, rule, event) ), schedule: asOk( // fetch the rule again to ensure we return the correct schedule as it may have @@ -705,9 +716,9 @@ export class TaskRunner< return getDefaultRuleMonitoring(); }) ?? getDefaultRuleMonitoring(); - const executionStatus = map( + const executionStatus = map( state, - (ruleTaskState) => executionStatusFromState(ruleTaskState), + (ruleExecutionState) => executionStatusFromState(ruleExecutionState), (err: ElasticsearchError) => executionStatusFromError(err) ); @@ -760,6 +771,25 @@ export class TaskRunner< ); } + // Copy search stats into event log + if (executionStatus.metrics) { + set( + event, + 'kibana.alert.rule.execution.metrics.number_of_searches', + executionStatus.metrics.numSearches ?? 0 + ); + set( + event, + 'kibana.alert.rule.execution.metrics.es_search_duration_ms', + executionStatus.metrics.esSearchDurationMs ?? 0 + ); + set( + event, + 'kibana.alert.rule.execution.metrics.total_search_duration_ms', + executionStatus.metrics.totalSearchDurationMs ?? 0 + ); + } + ruleMonitoring.execution.history.push(monitoringHistory); ruleMonitoring.execution.calculated_metrics = { success_ratio: getExecutionSuccessRatio(ruleMonitoring), @@ -780,20 +810,19 @@ export class TaskRunner< }); } - const transformStateForTaskState = ( - stateWithActions: RuleTaskStateWithActions + const transformExecutionStateToTaskState = ( + executionState: RuleExecutionState ): RuleTaskState => { return { - ...omit(stateWithActions, 'triggeredActions'), + ...omit(executionState, ['triggeredActions', 'metrics']), previousStartedAt: startedAt, }; }; return { - state: map( + state: map( state, - (stateWithActions: RuleTaskStateWithActions) => - transformStateForTaskState(stateWithActions), + (executionState: RuleExecutionState) => transformExecutionStateToTaskState(executionState), (err: ElasticsearchError) => { const message = `Executing Rule ${spaceId}:${ this.ruleType.id @@ -1242,8 +1271,8 @@ function logActiveAndRecoveredAlerts< * so that we can treat each field independantly */ async function errorAsRuleTaskRunResult( - future: Promise> -): Promise> { + future: Promise> +): Promise> { try { return await future; } catch (e) { diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts index f4b67935f7249a..d70b36ff48a8f3 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts @@ -23,6 +23,8 @@ import { savedObjectsRepositoryMock, httpServiceMock, executionContextServiceMock, + savedObjectsServiceMock, + elasticsearchServiceMock, } from '../../../../../src/core/server/mocks'; import { PluginStartContract as ActionsPluginStart } from '../../../actions/server'; import { actionsMock, actionsClientMock } from '../../../actions/server/mocks'; @@ -36,6 +38,9 @@ import { ruleTypeRegistryMock } from '../rule_type_registry.mock'; jest.mock('uuid', () => ({ v4: () => '5f6aa57d-3e22-484e-bae8-cbed868f4d28', })); +jest.mock('../lib/wrap_scoped_cluster_client', () => ({ + createWrappedScopedClusterClientFactory: jest.fn(), +})); const ruleType: jest.Mocked = { id: 'test', @@ -87,6 +92,8 @@ describe('Task Runner Cancel', () => { const actionsClient = actionsClientMock.create(); const rulesClient = rulesClientMock.create(); const ruleTypeRegistry = ruleTypeRegistryMock.create(); + const savedObjectsService = savedObjectsServiceMock.createInternalStartContract(); + const elasticsearchService = elasticsearchServiceMock.createInternalStart(); type TaskRunnerFactoryInitializerParamsType = jest.Mocked & { actionsPlugin: jest.Mocked; @@ -95,7 +102,8 @@ describe('Task Runner Cancel', () => { }; const taskRunnerFactoryInitializerParams: TaskRunnerFactoryInitializerParamsType = { - getServices: jest.fn().mockReturnValue(services), + savedObjects: savedObjectsService, + elasticsearch: elasticsearchService, actionsPlugin: actionsMock.createStart(), getRulesClientWithRequest: jest.fn().mockReturnValue(rulesClient), encryptedSavedObjectsClient, @@ -162,7 +170,18 @@ describe('Task Runner Cancel', () => { beforeEach(() => { jest.resetAllMocks(); - taskRunnerFactoryInitializerParams.getServices.mockReturnValue(services); + jest + .requireMock('../lib/wrap_scoped_cluster_client') + .createWrappedScopedClusterClientFactory.mockReturnValue({ + client: () => services.scopedClusterClient, + getMetrics: () => ({ + numSearches: 3, + esSearchDurationMs: 33, + totalSearchDurationMs: 23423, + }), + }); + savedObjectsService.getScopedClient.mockReturnValue(services.savedObjectsClient); + elasticsearchService.client.asScoped.mockReturnValue(services.scopedClusterClient); taskRunnerFactoryInitializerParams.getRulesClientWithRequest.mockReturnValue(rulesClient); taskRunnerFactoryInitializerParams.actionsPlugin.getActionsClientWithRequest.mockResolvedValue( actionsClient @@ -288,7 +307,10 @@ describe('Task Runner Cancel', () => { rule: { execution: { metrics: { + number_of_searches: 3, number_of_triggered_actions: 0, + es_search_duration_ms: 33, + total_search_duration_ms: 23423, }, uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', }, @@ -466,7 +488,7 @@ describe('Task Runner Cancel', () => { ); expect(logger.debug).nthCalledWith( 7, - 'ruleExecutionStatus for test:1: {"numberOfTriggeredActions":0,"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"active"}' + 'ruleExecutionStatus for test:1: {"metrics":{"numSearches":3,"esSearchDurationMs":33,"totalSearchDurationMs":23423},"numberOfTriggeredActions":0,"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"active"}' ); const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; @@ -551,7 +573,10 @@ describe('Task Runner Cancel', () => { rule: { execution: { metrics: { + number_of_searches: 3, number_of_triggered_actions: 0, + es_search_duration_ms: 33, + total_search_duration_ms: 23423, }, uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', }, @@ -613,7 +638,7 @@ describe('Task Runner Cancel', () => { ); expect(logger.debug).nthCalledWith( 6, - 'ruleExecutionStatus for test:1: {"numberOfTriggeredActions":1,"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"active"}' + 'ruleExecutionStatus for test:1: {"metrics":{"numSearches":3,"esSearchDurationMs":33,"totalSearchDurationMs":23423},"numberOfTriggeredActions":1,"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"active"}' ); const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; @@ -808,7 +833,10 @@ describe('Task Runner Cancel', () => { rule: { execution: { metrics: { + number_of_searches: 3, number_of_triggered_actions: 1, + es_search_duration_ms: 33, + total_search_duration_ms: 23423, }, uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', }, diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts index 2e321f73544580..6dea8df475503c 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts @@ -14,9 +14,11 @@ import { loggingSystemMock, savedObjectsRepositoryMock, httpServiceMock, + savedObjectsServiceMock, + elasticsearchServiceMock, } from '../../../../../src/core/server/mocks'; import { actionsMock } from '../../../actions/server/mocks'; -import { alertsMock, rulesClientMock } from '../mocks'; +import { rulesClientMock } from '../mocks'; import { eventLoggerMock } from '../../../event_log/server/event_logger.mock'; import { UntypedNormalizedRuleType } from '../rule_type_registry'; import { ruleTypeRegistryMock } from '../rule_type_registry.mock'; @@ -25,6 +27,8 @@ import { executionContextServiceMock } from '../../../../../src/core/server/mock const executionContext = executionContextServiceMock.createSetupContract(); const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); +const savedObjectsService = savedObjectsServiceMock.createInternalStartContract(); +const elasticsearchService = elasticsearchServiceMock.createInternalStart(); const ruleType: UntypedNormalizedRuleType = { id: 'test', name: 'My test alert', @@ -69,11 +73,11 @@ describe('Task Runner Factory', () => { afterAll(() => fakeTimer.restore()); const encryptedSavedObjectsPlugin = encryptedSavedObjectsMock.createStart(); - const services = alertsMock.createAlertServices(); const rulesClient = rulesClientMock.create(); const taskRunnerFactoryInitializerParams: jest.Mocked = { - getServices: jest.fn().mockReturnValue(services), + savedObjects: savedObjectsService, + elasticsearch: elasticsearchService, getRulesClientWithRequest: jest.fn().mockReturnValue(rulesClient), actionsPlugin: actionsMock.createStart(), encryptedSavedObjectsClient: encryptedSavedObjectsPlugin.getClient(), @@ -93,7 +97,6 @@ describe('Task Runner Factory', () => { beforeEach(() => { jest.resetAllMocks(); - taskRunnerFactoryInitializerParams.getServices.mockReturnValue(services); }); test(`throws an error if factory isn't initialized`, () => { diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts index f410e55eaab1c2..f60370dd7daf7f 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts @@ -13,6 +13,8 @@ import type { ISavedObjectsRepository, IBasePath, ExecutionContextStart, + SavedObjectsServiceStart, + ElasticsearchServiceStart, } from '../../../../../src/core/server'; import { RunContext } from '../../../task_manager/server'; import { EncryptedSavedObjectsClient } from '../../../encrypted_saved_objects/server'; @@ -20,7 +22,6 @@ import { PluginStartContract as ActionsPluginStartContract } from '../../../acti import { AlertTypeParams, RuleTypeRegistry, - GetServicesFunction, SpaceIdToNamespaceFunction, AlertTypeState, AlertInstanceState, @@ -33,7 +34,8 @@ import { NormalizedRuleType } from '../rule_type_registry'; export interface TaskRunnerContext { logger: Logger; - getServices: GetServicesFunction; + savedObjects: SavedObjectsServiceStart; + elasticsearch: ElasticsearchServiceStart; getRulesClientWithRequest(request: KibanaRequest): PublicMethodsOf; actionsPlugin: ActionsPluginStartContract; eventLogger: IEventLogger; diff --git a/x-pack/plugins/alerting/server/types.ts b/x-pack/plugins/alerting/server/types.ts index 50acb67a3de479..b2818c670f0370 100644 --- a/x-pack/plugins/alerting/server/types.ts +++ b/x-pack/plugins/alerting/server/types.ts @@ -5,7 +5,13 @@ * 2.0. */ -import type { IRouter, RequestHandlerContext, SavedObjectReference } from 'src/core/server'; +import { Client } from '@elastic/elasticsearch'; +import type { + IRouter, + RequestHandlerContext, + SavedObjectReference, + ElasticsearchClient, +} from 'src/core/server'; import type { PublicMethodsOf } from '@kbn/utility-types'; import { AlertFactoryDoneUtils, PublicAlert } from './alert'; import { RuleTypeRegistry as OrigruleTypeRegistry } from './rule_type_registry'; @@ -14,7 +20,6 @@ import { RulesClient } from './rules_client'; export * from '../common'; import { IScopedClusterClient, - KibanaRequest, SavedObjectAttributes, SavedObjectsClientContract, } from '../../../../src/core/server'; @@ -39,9 +44,12 @@ import { LicenseType } from '../../licensing/server'; import { IAbortableClusterClient } from './lib/create_abortable_es_client_factory'; export type WithoutQueryAndParams = Pick>; -export type GetServicesFunction = (request: KibanaRequest) => Services; export type SpaceIdToNamespaceFunction = (spaceId?: string) => string | undefined; +export interface ElasticsearchClientWithChild extends ElasticsearchClient { + child: Client['child']; +} + /** * @public */ @@ -64,16 +72,13 @@ export interface AlertingRequestHandlerContext extends RequestHandlerContext { */ export type AlertingRouter = IRouter; -export interface Services { - savedObjectsClient: SavedObjectsClientContract; - scopedClusterClient: IScopedClusterClient; -} - export interface AlertServices< InstanceState extends AlertInstanceState = AlertInstanceState, InstanceContext extends AlertInstanceContext = AlertInstanceContext, ActionGroupIds extends string = never -> extends Services { +> { + savedObjectsClient: SavedObjectsClientContract; + scopedClusterClient: IScopedClusterClient; alertFactory: { create: (id: string) => PublicAlert; done: () => AlertFactoryDoneUtils; diff --git a/x-pack/plugins/event_log/generated/mappings.json b/x-pack/plugins/event_log/generated/mappings.json index e9f030ffbc8861..e879cbf4053658 100644 --- a/x-pack/plugins/event_log/generated/mappings.json +++ b/x-pack/plugins/event_log/generated/mappings.json @@ -292,9 +292,15 @@ "number_of_triggered_actions": { "type": "long" }, + "number_of_searches": { + "type": "long" + }, "total_indexing_duration_ms": { "type": "long" }, + "es_search_duration_ms": { + "type": "long" + }, "total_search_duration_ms": { "type": "long" }, diff --git a/x-pack/plugins/event_log/generated/schemas.ts b/x-pack/plugins/event_log/generated/schemas.ts index d61689d6238e49..19856d89e99318 100644 --- a/x-pack/plugins/event_log/generated/schemas.ts +++ b/x-pack/plugins/event_log/generated/schemas.ts @@ -129,7 +129,9 @@ export const EventSchema = schema.maybe( metrics: schema.maybe( schema.object({ number_of_triggered_actions: ecsNumber(), + number_of_searches: ecsNumber(), total_indexing_duration_ms: ecsNumber(), + es_search_duration_ms: ecsNumber(), total_search_duration_ms: ecsNumber(), execution_gap_duration_s: ecsNumber(), }) diff --git a/x-pack/plugins/event_log/scripts/mappings.js b/x-pack/plugins/event_log/scripts/mappings.js index 091b50eceea6cc..7bb6a69f5ab6d7 100644 --- a/x-pack/plugins/event_log/scripts/mappings.js +++ b/x-pack/plugins/event_log/scripts/mappings.js @@ -74,9 +74,15 @@ exports.EcsCustomPropertyMappings = { number_of_triggered_actions: { type: 'long', }, + number_of_searches: { + type: 'long', + }, total_indexing_duration_ms: { type: 'long', }, + es_search_duration_ms: { + type: 'long', + }, total_search_duration_ms: { type: 'long', }, diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 718a637518cbec..67c2da627bf396 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -28,7 +28,6 @@ import { AlertAction, AlertAggregations, RuleTaskState, - RuleTaskStateWithActions, AlertSummary, ExecutionDuration, AlertStatus, @@ -50,7 +49,6 @@ export type { AlertAction, AlertAggregations, RuleTaskState, - RuleTaskStateWithActions, AlertSummary, ExecutionDuration, AlertStatus, diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts index b1ad23170ae072..4e026c20b579a9 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts @@ -706,6 +706,57 @@ export function defineAlertTypes( async executor() {}, producer: 'alertsFixture', }; + const multipleSearchesRuleType: RuleType< + { numSearches: number; delay: string }, + {}, + {}, + {}, + {}, + 'default' + > = { + id: 'test.multipleSearches', + name: 'Test: MultipleSearches', + actionGroups: [ + { + id: 'default', + name: 'Default', + }, + ], + producer: 'alertsFixture', + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + isExportable: true, + async executor(ruleExecutorOptions) { + const { services, params } = ruleExecutorOptions; + const numSearches = params.numSearches ?? 1; + const delay = params.delay ?? '10s'; + + const query = { + index: ES_TEST_INDEX_NAME, + body: { + query: { + bool: { + filter: { + match_all: {}, + }, + }, + }, + aggs: { + delay: { + shard_delay: { + value: delay, + }, + }, + }, + }, + }; + + let i: number = 0; + for (i = 0; i < numSearches; ++i) { + await services.scopedClusterClient.asCurrentUser.search(query as any); + } + }, + }; alerting.registerType(getAlwaysFiringAlertType()); alerting.registerType(getCumulativeFiringAlertType()); @@ -721,6 +772,7 @@ export function defineAlertTypes( alerting.registerType(longRunningAlertType); alerting.registerType(goldNoopAlertType); alerting.registerType(exampleAlwaysFiringAlertType); + alerting.registerType(multipleSearchesRuleType); alerting.registerType(getLongRunningPatternRuleType()); alerting.registerType(getLongRunningPatternRuleType(false)); alerting.registerType(getCancellableRuleType()); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts index 9b0e7de7f8baaa..c3e3c4fc930054 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts @@ -1332,6 +1332,9 @@ instanceStateValue: true ]); expect(event?.kibana?.alert?.rule?.execution?.metrics?.number_of_triggered_actions).to.be(1); + expect(event?.kibana?.alert?.rule?.execution?.metrics?.number_of_searches).to.be(0); + expect(event?.kibana?.alert?.rule?.execution?.metrics?.es_search_duration_ms).to.be(0); + expect(event?.kibana?.alert?.rule?.execution?.metrics?.total_search_duration_ms).to.be(0); expect(event?.rule).to.eql({ id: alertId, diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log.ts index 2816fd68fcbb06..c36f9a0da75bcd 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log.ts @@ -8,7 +8,13 @@ import expect from '@kbn/expect'; import uuid from 'uuid'; import { Spaces } from '../../scenarios'; -import { getUrlPrefix, getTestAlertData, ObjectRemover, getEventLog } from '../../../common/lib'; +import { + getUrlPrefix, + getTestAlertData, + ObjectRemover, + getEventLog, + ESTestIndexTool, +} from '../../../common/lib'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; import { IValidatedEvent } from '../../../../../plugins/event_log/server'; @@ -18,11 +24,20 @@ const NANOS_IN_MILLIS = 1000 * 1000; export default function eventLogTests({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const retry = getService('retry'); + const es = getService('es'); + const esTestIndexTool = new ESTestIndexTool(es, retry); describe('eventLog', () => { const objectRemover = new ObjectRemover(supertest); - after(() => objectRemover.removeAll()); + beforeEach(async () => { + await esTestIndexTool.destroy(); + await esTestIndexTool.setup(); + }); + + afterEach(async () => { + await objectRemover.removeAll(); + }); for (const space of [Spaces.default, Spaces.space1]) { describe(`in space ${space.id}`, () => { @@ -277,6 +292,97 @@ export default function eventLogTests({ getService }: FtrProviderContext) { } }); + it('should generate expected events for rules with multiple searches', async () => { + const numSearches = 4; + const delaySeconds = 2; + + const response = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestAlertData({ + rule_type_id: 'test.multipleSearches', + schedule: { interval: '1s' }, + throttle: null, + params: { + numSearches, + delay: `${delaySeconds}s`, + }, + actions: [], + }) + ); + + expect(response.status).to.eql(200); + const ruleId = response.body.id; + objectRemover.add(space.id, ruleId, 'rule', 'alerting'); + + // get the events we're expecting + const events = await retry.try(async () => { + return await getEventLog({ + getService, + spaceId: space.id, + type: 'alert', + id: ruleId, + provider: 'alerting', + actions: new Map([ + // make sure the counts of the # of events per type are as expected + ['execute', { gte: 4 }], + ]), + }); + }); + + // validate each event + let currentExecutionId; + for (const event of events) { + switch (event?.event?.action) { + case 'execute-start': + currentExecutionId = event?.kibana?.alert?.rule?.execution?.uuid; + break; + case 'execute': + validateEvent(event, { + spaceId: space.id, + savedObjects: [ + { type: 'alert', id: ruleId, rel: 'primary', type_id: 'test.multipleSearches' }, + ], + outcome: 'success', + message: `rule executed: test.multipleSearches:${ruleId}: 'abc'`, + status: 'ok', + shouldHaveTask: true, + executionId: currentExecutionId, + numTriggeredActions: 0, + rule: { + id: ruleId, + category: response.body.rule_type_id, + license: 'basic', + ruleset: 'alertsFixture', + name: response.body.name, + }, + }); + expect(event?.kibana?.alert?.rule?.execution?.metrics?.number_of_searches).to.be( + numSearches + ); + const esSearchDuration = + event?.kibana?.alert?.rule?.execution?.metrics?.es_search_duration_ms; + const totalSearchDuration = + event?.kibana?.alert?.rule?.execution?.metrics?.total_search_duration_ms; + + expect(esSearchDuration).not.to.be(undefined); + expect(totalSearchDuration).not.to.be(undefined); + + // Expect these searches to take time + expect(esSearchDuration! > 0).to.be(true); + expect(totalSearchDuration! > 0).to.be(true); + + // Total search duration should be greater since it includes any network latency + expect(totalSearchDuration! - esSearchDuration! > 0).to.be(true); + break; + // this will get triggered as we add new event actions + default: + throw new Error(`unexpected event action "${event?.event?.action}"`); + } + } + }); + it('should generate expected events for normal operation with subgroups', async () => { const { body: createdAction } = await supertest .post(`${getUrlPrefix(space.id)}/api/actions/connector`) @@ -584,6 +690,7 @@ interface ValidateEventLogParams { instanceId?: string; reason?: string; executionId?: string; + numTriggeredActions?: number; rule?: { id: string; name?: string; @@ -607,11 +714,14 @@ export function validateEvent(event: IValidatedEvent, params: ValidateEventLogPa rule, shouldHaveTask, executionId, + numTriggeredActions = 1, } = params; const { status, actionGroupId, instanceId, reason, shouldHaveEventEnd } = params; if (event?.event?.action === 'execute' && status === 'active') { - expect(event?.kibana?.alert?.rule?.execution?.metrics?.number_of_triggered_actions).to.be(1); + expect(event?.kibana?.alert?.rule?.execution?.metrics?.number_of_triggered_actions).to.be( + numTriggeredActions + ); } if (status) { From 448436f89fe29473ed9c34765342486ddb952ba1 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Wed, 23 Feb 2022 14:17:34 -0500 Subject: [PATCH 021/137] [Fleet] Reset preconfiguration delete preconfiguration deletion record (#126273) --- .../reset_preconfiguration.test.ts | 35 ++++++++++++++++++ .../preconfiguration/reset_agent_policies.ts | 36 +++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/x-pack/plugins/fleet/server/integration_tests/reset_preconfiguration.test.ts b/x-pack/plugins/fleet/server/integration_tests/reset_preconfiguration.test.ts index 4a2b4f2495e965..3b92102657803b 100644 --- a/x-pack/plugins/fleet/server/integration_tests/reset_preconfiguration.test.ts +++ b/x-pack/plugins/fleet/server/integration_tests/reset_preconfiguration.test.ts @@ -13,6 +13,7 @@ import * as kbnTestServer from 'src/core/test_helpers/kbn_server'; import type { HttpMethod } from 'src/core/test_helpers/kbn_server'; import type { AgentPolicySOAttributes } from '../types'; +import { PRECONFIGURATION_DELETION_RECORD_SAVED_OBJECT_TYPE } from '../../common'; import { useDockerRegistry } from './docker_registry_helper'; @@ -304,5 +305,39 @@ describe('Fleet preconfiguration reset', () => { ]) ); }); + + it('Works and reset one preconfigured policies if the policy was deleted with a preconfiguration deletion record', async () => { + const soClient = kbnServer.coreStart.savedObjects.createInternalRepository(); + + await soClient.delete('ingest-agent-policies', POLICY_ID); + await soClient.create(PRECONFIGURATION_DELETION_RECORD_SAVED_OBJECT_TYPE, { + id: POLICY_ID, + }); + + const resetAPI = getSupertestWithAdminUser( + kbnServer.root, + 'post', + `/internal/fleet/reset_preconfigured_agent_policies/${POLICY_ID}` + ); + await resetAPI.set('kbn-sxrf', 'xx').expect(200).send(); + + const agentPolicies = await kbnServer.coreStart.savedObjects + .createInternalRepository() + .find({ + type: 'ingest-agent-policies', + perPage: 10000, + }); + expect(agentPolicies.saved_objects).toHaveLength(2); + expect(agentPolicies.saved_objects.map((ap) => ({ ...ap.attributes }))).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + name: 'Elastic Cloud agent policy 0001', + }), + expect.objectContaining({ + name: 'Second preconfigured policy', + }), + ]) + ); + }); }); }); diff --git a/x-pack/plugins/fleet/server/services/preconfiguration/reset_agent_policies.ts b/x-pack/plugins/fleet/server/services/preconfiguration/reset_agent_policies.ts index 1c4ac5c5a7a9a7..513d061d3cf0dd 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration/reset_agent_policies.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration/reset_agent_policies.ts @@ -14,6 +14,7 @@ import { AGENT_POLICY_SAVED_OBJECT_TYPE, SO_SEARCH_LIMIT, PACKAGE_POLICY_SAVED_OBJECT_TYPE, + PRECONFIGURATION_DELETION_RECORD_SAVED_OBJECT_TYPE, } from '../../constants'; import { agentPolicyService } from '../agent_policy'; import { packagePolicyService } from '../package_policy'; @@ -30,6 +31,7 @@ export async function resetPreconfiguredAgentPolicies( logger.warn('Reseting Fleet preconfigured agent policies'); await _deleteExistingData(soClient, esClient, logger, agentPolicyId); await _deleteGhostPackagePolicies(soClient, esClient, logger); + await _deletePreconfigurationDeleteRecord(soClient, logger, agentPolicyId); await setupFleet(soClient, esClient); } @@ -78,6 +80,40 @@ async function _deleteGhostPackagePolicies( ); } +async function _deletePreconfigurationDeleteRecord( + soClient: SavedObjectsClientContract, + logger: Logger, + agentPolicyId?: string +) { + const existingDeletionRecord = await soClient.find<{ id: string }>({ + type: PRECONFIGURATION_DELETION_RECORD_SAVED_OBJECT_TYPE, + perPage: SO_SEARCH_LIMIT, + }); + + const deletionRecordSavedObjects = agentPolicyId + ? existingDeletionRecord.saved_objects.filter((so) => so.attributes.id === agentPolicyId) + : existingDeletionRecord.saved_objects; + + if (deletionRecordSavedObjects.length > 0) { + await pMap( + deletionRecordSavedObjects, + (savedObject) => + soClient + .delete(PRECONFIGURATION_DELETION_RECORD_SAVED_OBJECT_TYPE, savedObject.id) + .catch((err) => { + if (soClient.errors.isNotFoundError(err)) { + return undefined; + } + throw err; + }), + + { + concurrency: 20, + } + ); + } +} + async function _deleteExistingData( soClient: SavedObjectsClientContract, esClient: ElasticsearchClient, From b0cdbd945292386ce792cfe570c7b357a2019585 Mon Sep 17 00:00:00 2001 From: Angela Chuang <6295984+angorayc@users.noreply.github.com> Date: Wed, 23 Feb 2022 19:56:22 +0000 Subject: [PATCH 022/137] Embeddable enhancement (#125299) * attach charts to cases * takes lens attributes * update view mode * fix unit test * fix types * add singleMetric component * fix types * fix types * styling * review * styling * unit tests * rename owner to caseOwner * fix unit test * unit test Co-authored-by: shahzad31 Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/cases/public/index.tsx | 2 + x-pack/plugins/observability/kibana.json | 1 + .../embeddable/embeddable.test.tsx | 229 ++++++++++++++++++ .../embeddable/embeddable.tsx | 157 ++++++++---- .../exploratory_view/embeddable/index.tsx | 8 +- .../embeddable/single_metric.test.tsx | 48 ++++ .../embeddable/single_metric.tsx | 130 ++++++++++ .../embeddable/use_actions.ts | 53 +++- .../header/add_to_case_action.test.tsx | 9 +- .../header/add_to_case_action.tsx | 19 +- .../exploratory_view/hooks/use_add_to_case.ts | 30 ++- x-pack/plugins/observability/public/index.ts | 3 +- 12 files changed, 615 insertions(+), 74 deletions(-) create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.test.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/single_metric.test.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/single_metric.tsx diff --git a/x-pack/plugins/cases/public/index.tsx b/x-pack/plugins/cases/public/index.tsx index 13b9c64b13475d..0190df8204fc1a 100644 --- a/x-pack/plugins/cases/public/index.tsx +++ b/x-pack/plugins/cases/public/index.tsx @@ -12,6 +12,8 @@ export function plugin(initializerContext: PluginInitializerContext) { return new CasesUiPlugin(initializerContext); } +export { DRAFT_COMMENT_STORAGE_ID } from './components/markdown_editor/plugins/lens/constants'; + export type { CasesUiPlugin }; export type { CasesUiStart } from './types'; export type { GetCasesProps } from './methods/get_cases'; diff --git a/x-pack/plugins/observability/kibana.json b/x-pack/plugins/observability/kibana.json index 650d437a229ff5..8eb86125017b34 100644 --- a/x-pack/plugins/observability/kibana.json +++ b/x-pack/plugins/observability/kibana.json @@ -36,6 +36,7 @@ "requiredBundles": [ "data", "dataViews", + "embeddable", "kibanaReact", "kibanaUtils", "lens" diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.test.tsx new file mode 100644 index 00000000000000..a21eeca9dcb45b --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.test.tsx @@ -0,0 +1,229 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import Embeddable from './embeddable'; +import { LensPublicStart } from '../../../../../../lens/public'; +import { IndexPatternState } from '../hooks/use_app_index_pattern'; +import { render } from '../rtl_helpers'; +import { AddToCaseAction } from '../header/add_to_case_action'; +import { ActionTypes } from './use_actions'; + +jest.mock('../header/add_to_case_action', () => ({ + AddToCaseAction: jest.fn(() =>
mockAddToCaseAction
), +})); + +const mockLensAttrs = { + title: '[Host] KPI Hosts - metric 1', + description: '', + visualizationType: 'lnsMetric', + state: { + visualization: { + accessor: 'b00c65ea-32be-4163-bfc8-f795b1ef9d06', + layerId: '416b6fad-1923-4f6a-a2df-b223bb287e30', + layerType: 'data', + }, + query: { + language: 'kuery', + query: '', + }, + filters: [], + datasourceStates: { + indexpattern: { + layers: { + '416b6fad-1923-4f6a-a2df-b223bb287e30': { + columnOrder: ['b00c65ea-32be-4163-bfc8-f795b1ef9d06'], + columns: { + 'b00c65ea-32be-4163-bfc8-f795b1ef9d06': { + customLabel: true, + dataType: 'number', + isBucketed: false, + label: ' ', + operationType: 'unique_count', + scale: 'ratio', + sourceField: 'host.name', + }, + }, + incompleteColumns: {}, + }, + }, + }, + }, + }, + references: [ + { + type: 'index-pattern', + id: 'security-solution-default', + name: 'indexpattern-datasource-current-indexpattern', + }, + { + type: 'index-pattern', + id: 'security-solution-default', + name: 'indexpattern-datasource-layer-416b6fad-1923-4f6a-a2df-b223bb287e30', + }, + { + type: 'tag', + id: 'security-solution-default', + name: 'tag-ref-security-solution-default', + }, + ], +}; +const mockTimeRange = { + from: '2022-02-15T16:00:00.000Z', + to: '2022-02-16T15:59:59.999Z', +}; +const mockOwner = 'securitySolution'; +const mockAppId = 'securitySolutionUI'; +const mockIndexPatterns = {} as IndexPatternState; +const mockReportType = 'kpi-over-time'; +const mockTitle = 'mockTitle'; +const mockLens = { + EmbeddableComponent: jest.fn((props) => { + return ( +
+ mockEmbeddableComponent +
+ ); + }), + SaveModalComponent: jest.fn(() =>
mockSaveModalComponent
), +} as unknown as LensPublicStart; +const mockActions: ActionTypes[] = ['addToCase', 'openInLens']; + +describe('Embeddable', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders title', async () => { + const { container, getByText } = render( + + ); + expect(container.querySelector(`[data-test-subj="exploratoryView-title"]`)).toBeInTheDocument(); + expect(getByText(mockTitle)).toBeInTheDocument(); + }); + + it('renders no title if it is not given', async () => { + const { container } = render( + + ); + expect( + container.querySelector(`[data-test-subj="exploratoryView-title"]`) + ).not.toBeInTheDocument(); + }); + + it('renders lens component', () => { + const { container } = render( + + ); + + expect( + container.querySelector(`[data-test-subj="exploratoryView-singleMetric"]`) + ).not.toBeInTheDocument(); + expect(container.querySelector(`[data-test-subj="exploratoryView"]`)).toBeInTheDocument(); + expect((mockLens.EmbeddableComponent as jest.Mock).mock.calls[0][0].id).toEqual( + 'exploratoryView' + ); + expect((mockLens.EmbeddableComponent as jest.Mock).mock.calls[0][0].attributes).toEqual( + mockLensAttrs + ); + expect((mockLens.EmbeddableComponent as jest.Mock).mock.calls[0][0].timeRange).toEqual( + mockTimeRange + ); + expect((mockLens.EmbeddableComponent as jest.Mock).mock.calls[0][0].timeRange).toEqual( + mockTimeRange + ); + expect((mockLens.EmbeddableComponent as jest.Mock).mock.calls[0][0].withDefaultActions).toEqual( + true + ); + }); + + it('renders single metric', () => { + const { container } = render( + + ); + expect( + container.querySelector(`[data-test-subj="exploratoryView-singleMetric"]`) + ).toBeInTheDocument(); + expect(container.querySelector(`[data-test-subj="exploratoryView"]`)).not.toBeInTheDocument(); + expect((mockLens.EmbeddableComponent as jest.Mock).mock.calls[0][0].id).toEqual( + 'exploratoryView-singleMetric' + ); + expect((mockLens.EmbeddableComponent as jest.Mock).mock.calls[0][0].attributes).toEqual( + mockLensAttrs + ); + expect((mockLens.EmbeddableComponent as jest.Mock).mock.calls[0][0].timeRange).toEqual( + mockTimeRange + ); + expect((mockLens.EmbeddableComponent as jest.Mock).mock.calls[0][0].withDefaultActions).toEqual( + true + ); + }); + + it('renders AddToCaseAction', () => { + render( + + ); + + expect((AddToCaseAction as jest.Mock).mock.calls[0][0].timeRange).toEqual(mockTimeRange); + expect((AddToCaseAction as jest.Mock).mock.calls[0][0].appId).toEqual(mockAppId); + expect((AddToCaseAction as jest.Mock).mock.calls[0][0].lensAttributes).toEqual(mockLensAttrs); + expect((AddToCaseAction as jest.Mock).mock.calls[0][0].owner).toEqual(mockOwner); + }); +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx index 92ef0834880d50..026f7ab04d68b8 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx @@ -19,19 +19,29 @@ import { ReportConfigMap } from '../contexts/exploratory_view_config'; import { obsvReportConfigMap } from '../obsv_exploratory_view'; import { ActionTypes, useActions } from './use_actions'; import { AddToCaseAction } from '../header/add_to_case_action'; +import { ViewMode } from '../../../../../../../../src/plugins/embeddable/common'; +import { observabilityFeatureId } from '../../../../../common'; +import { SingleMetric, SingleMetricOptions } from './single_metric'; export interface ExploratoryEmbeddableProps { - reportType: ReportViewType; - attributes: AllSeries; + appId?: 'securitySolutionUI' | 'observability'; appendTitle?: JSX.Element; - title: string | JSX.Element; - showCalculationMethod?: boolean; + attributes?: AllSeries; axisTitlesVisibility?: XYState['axisTitlesVisibilitySettings']; - legendIsVisible?: boolean; + customHeight?: string | number; + customLensAttrs?: any; // Takes LensAttributes + customTimeRange?: { from: string; to: string }; // requred if rendered with LensAttributes dataTypesIndexPatterns?: Partial>; + isSingleMetric?: boolean; + legendIsVisible?: boolean; + onBrushEnd?: (param: { range: number[] }) => void; + caseOwner?: string; reportConfigMap?: ReportConfigMap; + reportType: ReportViewType; + showCalculationMethod?: boolean; + singleMetricOptions?: SingleMetricOptions; + title?: string | JSX.Element; withActions?: boolean | ActionTypes[]; - appId?: 'security' | 'observability'; } export interface ExploratoryEmbeddableComponentProps extends ExploratoryEmbeddableProps { @@ -41,18 +51,25 @@ export interface ExploratoryEmbeddableComponentProps extends ExploratoryEmbeddab // eslint-disable-next-line import/no-default-export export default function Embeddable({ - reportType, - attributes, - title, - appendTitle, - indexPatterns, - lens, appId, + appendTitle, + attributes = [], axisTitlesVisibility, + customHeight, + customLensAttrs, + customTimeRange, + indexPatterns, + isSingleMetric = false, legendIsVisible, - withActions = true, + lens, + onBrushEnd, + caseOwner = observabilityFeatureId, reportConfigMap = {}, + reportType, showCalculationMethod = false, + singleMetricOptions, + title, + withActions = true, }: ExploratoryEmbeddableComponentProps) { const LensComponent = lens?.EmbeddableComponent; const LensSaveModalComponent = lens?.SaveModalComponent; @@ -60,18 +77,10 @@ export default function Embeddable({ const [isSaveOpen, setIsSaveOpen] = useState(false); const [isAddToCaseOpen, setAddToCaseOpen] = useState(false); - const series = Object.entries(attributes)[0][1]; + const series = Object.entries(attributes)[0]?.[1]; const [operationType, setOperationType] = useState(series?.operationType); const theme = useTheme(); - const actions = useActions({ - withActions, - attributes, - reportType, - appId, - setIsSaveOpen, - setAddToCaseOpen, - }); const layerConfigs: LayerConfig[] = getLayerConfigs( attributes, @@ -81,32 +90,52 @@ export default function Embeddable({ { ...reportConfigMap, ...obsvReportConfigMap } ); - if (layerConfigs.length < 1) { - return null; + let lensAttributes; + try { + lensAttributes = new LensAttributes(layerConfigs); + // eslint-disable-next-line no-empty + } catch (error) {} + + const attributesJSON = customLensAttrs ?? lensAttributes?.getJSON(); + const timeRange = customTimeRange ?? series?.time; + if (typeof axisTitlesVisibility !== 'undefined') { + (attributesJSON.state.visualization as XYState).axisTitlesVisibilitySettings = + axisTitlesVisibility; } - const lensAttributes = new LensAttributes(layerConfigs); - if (!LensComponent) { - return No lens component; + if (typeof legendIsVisible !== 'undefined') { + (attributesJSON.state.visualization as XYState).legend.isVisible = legendIsVisible; } - const attributesJSON = lensAttributes.getJSON(); + const actions = useActions({ + withActions, + attributes, + reportType, + appId, + setIsSaveOpen, + setAddToCaseOpen, + lensAttributes: attributesJSON, + timeRange, + }); - (attributesJSON.state.visualization as XYState).axisTitlesVisibilitySettings = - axisTitlesVisibility; + if (!attributesJSON && layerConfigs.length < 1) { + return null; + } - if (typeof legendIsVisible !== 'undefined') { - (attributesJSON.state.visualization as XYState).legend.isVisible = legendIsVisible; + if (!LensComponent) { + return No lens component; } return ( - - - - -

{title}

-
-
+ + + {title && ( + + +

{title}

+
+
+ )} {showCalculationMethod && ( )} - {appendTitle} + {appendTitle && appendTitle}
- {}} - withDefaultActions={Boolean(withActions)} - extraActions={actions} - /> + + {isSingleMetric && ( + + + + )} + {!isSingleMetric && ( + + )} {isSaveOpen && attributesJSON && (
); } -const Wrapper = styled.div` +const Wrapper = styled.div<{ + $customHeight?: string | number; +}>` height: 100%; &&& { > :nth-child(2) { - height: calc(100% - 32px); + height: ${(props) => + props.$customHeight ? `${props.$customHeight};` : `calc(100% - 32px);`}; } .embPanel--editing { border-style: initial !important; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/index.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/index.tsx index 4b68e9d5f6fa3c..521e7f746fdc9e 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/index.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/index.tsx @@ -33,7 +33,7 @@ export function getExploratoryViewEmbeddable( const [indexPatterns, setIndexPatterns] = useState({} as IndexPatternState); const [loading, setLoading] = useState(false); - const series = props.attributes[0]; + const series = props.attributes && props.attributes[0]; const isDarkMode = core.uiSettings.get('theme:darkMode'); @@ -59,8 +59,10 @@ export function getExploratoryViewEmbeddable( ); useEffect(() => { - loadIndexPattern({ dataType: series.dataType }); - }, [series.dataType, loadIndexPattern]); + if (series?.dataType) { + loadIndexPattern({ dataType: series.dataType }); + } + }, [series?.dataType, loadIndexPattern]); if (Object.keys(indexPatterns).length === 0 || loading) { return ; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/single_metric.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/single_metric.test.tsx new file mode 100644 index 00000000000000..8767223d69638e --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/single_metric.test.tsx @@ -0,0 +1,48 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { render } from '@testing-library/react'; +import React from 'react'; +import { SingleMetric } from './single_metric'; + +describe('SingleMetric', () => { + it('renders SingleMetric without icon or postfix', async () => { + const { container } = render(); + expect( + container.querySelector(`[data-test-subj="single-metric-icon"]`) + ).not.toBeInTheDocument(); + expect( + container.querySelector(`[data-test-subj="single-metric"]`)?.style?.maxWidth + ).toEqual(`calc(100%)`); + expect( + container.querySelector(`[data-test-subj="single-metric-postfix"]`) + ).not.toBeInTheDocument(); + }); + + it('renders SingleMetric icon', async () => { + const { container } = render(); + expect( + container.querySelector(`[data-test-subj="single-metric"]`)?.style?.maxWidth + ).toEqual(`calc(100% - 30px)`); + expect(container.querySelector(`[data-test-subj="single-metric-icon"]`)).toBeInTheDocument(); + }); + + it('renders SingleMetric postfix', async () => { + const { container, getByText } = render( + + ); + expect(getByText('Host')).toBeInTheDocument(); + expect( + container.querySelector(`[data-test-subj="single-metric"]`)?.style?.maxWidth + ).toEqual(`calc(100% - 30px - 150px)`); + expect(container.querySelector(`[data-test-subj="single-metric-postfix"]`)).toBeInTheDocument(); + expect( + container.querySelector(`[data-test-subj="single-metric-postfix"]`)?.style + ?.maxWidth + ).toEqual(`150px`); + }); +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/single_metric.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/single_metric.tsx new file mode 100644 index 00000000000000..3ada5b78753856 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/single_metric.tsx @@ -0,0 +1,130 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiTitle, IconType } from '@elastic/eui'; +import styled from 'styled-components'; + +export interface SingleMetricOptions { + alignLnsMetric?: string; + disableBorder?: boolean; + disableShadow?: boolean; + metricIcon?: IconType; + metricIconColor?: string; + metricIconWidth?: string; + metricPostfix?: string; + metricPostfixWidth?: string; +} + +type SingleMetricProps = SingleMetricOptions & { + children?: JSX.Element; +}; + +export function SingleMetric({ + alignLnsMetric = 'flex-start', + children, + disableBorder = true, + disableShadow = true, + metricIcon, + metricIconColor, + metricIconWidth = '30px', + metricPostfix, + metricPostfixWidth = '150px', +}: SingleMetricProps) { + let metricMaxWidth = '100%'; + metricMaxWidth = metricIcon ? `${metricMaxWidth} - ${metricIconWidth}` : metricMaxWidth; + metricMaxWidth = metricPostfix ? `${metricMaxWidth} - ${metricPostfixWidth}` : metricMaxWidth; + + return ( + + {metricIcon && ( + + + + )} + + {children} + + {metricPostfix && ( + + +

{metricPostfix}

+
+
+ )} +
+ ); +} + +const StyledTitle = styled(EuiTitle)` + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +`; + +const LensWrapper = styled(EuiFlexGroup)<{ + $alignLnsMetric?: string; + $disableBorder?: boolean; + $disableShadow?: boolean; +}>` + .embPanel__optionsMenuPopover { + visibility: collapse; + } + .embPanel--editing { + background-color: transparent; + } + ${(props) => + props.$disableBorder + ? `.embPanel--editing { + border: 0; + }` + : ''} + &&&:hover { + .embPanel__optionsMenuPopover { + visibility: visible; + } + ${(props) => + props.$disableShadow + ? `.embPanel--editing { + box-shadow: none; + }` + : ''} + } + .embPanel__title { + display: none; + } + ${(props) => + props.$alignLnsMetric + ? `.lnsMetricExpression__container { + align-items: ${props.$alignLnsMetric ?? 'flex-start'}; + }` + : ''} +`; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/use_actions.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/use_actions.ts index 5c5c22ddf7c539..26190637d84ce7 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/use_actions.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/use_actions.ts @@ -15,8 +15,9 @@ import { Action, ActionExecutionContext, } from '../../../../../../../../src/plugins/ui_actions/public'; +import { ObservabilityAppServices } from '../../../../application/types'; -export type ActionTypes = 'explore' | 'save' | 'addToCase'; +export type ActionTypes = 'explore' | 'save' | 'addToCase' | 'openInLens'; export function useActions({ withActions, @@ -25,14 +26,22 @@ export function useActions({ setIsSaveOpen, setAddToCaseOpen, appId = 'observability', + timeRange, + lensAttributes, }: { withActions?: boolean | ActionTypes[]; reportType: ReportViewType; attributes: AllSeries; - appId?: 'security' | 'observability'; + appId?: 'securitySolutionUI' | 'observability'; setIsSaveOpen: (val: boolean) => void; setAddToCaseOpen: (val: boolean) => void; + timeRange: { from: string; to: string }; + lensAttributes: any; }) { + const kServices = useKibana().services; + + const { lens } = kServices; + const [defaultActions, setDefaultActions] = useState(['explore', 'save', 'addToCase']); useEffect(() => { @@ -54,6 +63,21 @@ export function useActions({ const routePath = createExploratoryViewRoutePath({ reportType, allSeries: attributes }); + const openInLensCallback = useCallback(() => { + if (lensAttributes) { + lens.navigateToPrefilledEditor( + { + id: '', + timeRange, + attributes: lensAttributes, + }, + { + openInNewTab: true, + } + ); + } + }, [lens, lensAttributes, timeRange]); + const exploreCallback = useCallback(() => { application?.navigateToApp(appId, { path: routePath }); }, [appId, application, routePath]); @@ -73,10 +97,35 @@ export function useActions({ if (action === 'addToCase') { return getAddToCaseAction({ callback: addToCaseCallback }); } + if (action === 'openInLens') { + return getOpenInLensAction({ callback: openInLensCallback }); + } return getExploreAction({ href, callback: exploreCallback }); }); } +const getOpenInLensAction = ({ callback }: { callback: () => void }): Action => { + return { + id: 'expViewOpenInLens', + getDisplayName(context: ActionExecutionContext): string { + return i18n.translate('xpack.observability.expView.openInLens', { + defaultMessage: 'Open in Lens', + }); + }, + getIconType(context: ActionExecutionContext): string | undefined { + return 'visArea'; + }, + type: 'link', + async isCompatible(context: ActionExecutionContext): Promise { + return true; + }, + async execute(context: ActionExecutionContext): Promise { + callback(); + return; + }, + }; +}; + const getExploreAction = ({ href, callback }: { href: string; callback: () => void }): Action => { return { id: 'expViewExplore', diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/add_to_case_action.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/add_to_case_action.test.tsx index 93e07d3ae84dfb..f3cc41d50676d8 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/add_to_case_action.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/add_to_case_action.test.tsx @@ -57,7 +57,12 @@ describe('AddToCaseAction', function () { const useAddToCaseHook = jest.spyOn(useCaseHook, 'useAddToCase'); const { getByText } = render( - + ); expect(await forNearestButton(getByText)('Add to case')).toBeDisabled(); @@ -69,6 +74,8 @@ describe('AddToCaseAction', function () { from: '', to: '', }, + appId: 'securitySolutionUI', + owner: 'security', }) ); }); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/add_to_case_action.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/add_to_case_action.tsx index 50f44f2d89b9b8..f91b74d6ed9324 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/add_to_case_action.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/add_to_case_action.tsx @@ -21,19 +21,21 @@ import { observabilityFeatureId, observabilityAppId } from '../../../../../commo import { parseRelativeDate } from '../components/date_range_picker'; export interface AddToCaseProps { + appId?: 'securitySolutionUI' | 'observability'; autoOpen?: boolean; + lensAttributes: TypedLensByValueInput['attributes'] | null; + owner?: string; setAutoOpen?: (val: boolean) => void; timeRange: { from: string; to: string }; - appId?: 'security' | 'observability'; - lensAttributes: TypedLensByValueInput['attributes'] | null; } export function AddToCaseAction({ - lensAttributes, - timeRange, + appId, autoOpen, + lensAttributes, + owner = observabilityFeatureId, setAutoOpen, - appId, + timeRange, }: AddToCaseProps) { const kServices = useKibana().services; @@ -47,14 +49,14 @@ export function AddToCaseAction({ (theCase) => toMountPoint( , { theme$: theme?.theme$ } ), - [getUrlForApp, theme?.theme$] + [appId, getUrlForApp, theme?.theme$] ); const absoluteFromDate = parseRelativeDate(timeRange.from); @@ -68,12 +70,13 @@ export function AddToCaseAction({ to: absoluteToDate?.toISOString() ?? '', }, appId, + owner, }); const getAllCasesSelectorModalProps: GetAllCasesSelectorModalProps = { onRowClick: onCaseClicked, userCanCrud: true, - owner: [observabilityFeatureId], + owner: [owner], onClose: () => { setIsCasesOpen(false); }, diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_add_to_case.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_add_to_case.ts index 7c73bd81e4f2a3..793e5d39653dfd 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_add_to_case.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_add_to_case.ts @@ -13,13 +13,14 @@ import { Case } from '../../../../../../cases/common'; import { TypedLensByValueInput } from '../../../../../../lens/public'; import { AddToCaseProps } from '../header/add_to_case_action'; import { observabilityFeatureId } from '../../../../../common'; -import { CasesDeepLinkId } from '../../../../../../cases/public'; +import { CasesDeepLinkId, DRAFT_COMMENT_STORAGE_ID } from '../../../../../../cases/public'; async function addToCase( http: HttpSetup, theCase: Case, attributes: TypedLensByValueInput['attributes'], - timeRange?: { from: string; to: string } + timeRange?: { from: string; to: string }, + owner?: string ) { const apiPath = `/api/cases/${theCase?.id}/comments`; @@ -31,7 +32,7 @@ async function addToCase( const payload = { comment: `!{lens${JSON.stringify(vizPayload)}}`, type: 'user', - owner: observabilityFeatureId, + owner: owner ?? observabilityFeatureId, }; return http.post(apiPath, { body: JSON.stringify(payload) }); @@ -42,8 +43,9 @@ export const useAddToCase = ({ getToastText, timeRange, appId, + owner = observabilityFeatureId, }: AddToCaseProps & { - appId?: 'security' | 'observability'; + appId?: 'securitySolutionUI' | 'observability'; getToastText: (thaCase: Case) => MountPoint; }) => { const [isSaving, setIsSaving] = useState(false); @@ -53,6 +55,7 @@ export const useAddToCase = ({ http, application: { navigateToApp }, notifications: { toasts }, + storage, } = useKibana().services; const onCaseClicked = useCallback( @@ -60,7 +63,7 @@ export const useAddToCase = ({ if (theCase && lensAttributes) { setIsCasesOpen(false); setIsSaving(true); - addToCase(http, theCase, lensAttributes, timeRange).then( + addToCase(http, theCase, lensAttributes, timeRange, owner).then( () => { setIsSaving(false); toasts.addSuccess( @@ -91,13 +94,26 @@ export const useAddToCase = ({ } ); } else { - navigateToApp(appId || observabilityFeatureId, { + /* save lens attributes and timerange to local storage, + ** so the description field will be automatically filled on case creation page. + */ + storage?.set(DRAFT_COMMENT_STORAGE_ID, { + commentId: 'description', + comment: `!{lens${JSON.stringify({ + timeRange, + attributes: lensAttributes, + })}}`, + position: '', + caseTitle: '', + caseTags: '', + }); + navigateToApp(appId ?? observabilityFeatureId, { deepLinkId: CasesDeepLinkId.casesCreate, openInNewTab: true, }); } }, - [appId, getToastText, http, lensAttributes, navigateToApp, timeRange, toasts] + [appId, getToastText, http, lensAttributes, navigateToApp, owner, storage, timeRange, toasts] ); return { diff --git a/x-pack/plugins/observability/public/index.ts b/x-pack/plugins/observability/public/index.ts index 3efbe4bef55e1a..0fa1469180da59 100644 --- a/x-pack/plugins/observability/public/index.ts +++ b/x-pack/plugins/observability/public/index.ts @@ -90,7 +90,7 @@ export { getApmTraceUrl } from './utils/get_apm_trace_url'; export { createExploratoryViewUrl } from './components/shared/exploratory_view/configurations/utils'; export { ALL_VALUES_SELECTED } from './components/shared/field_value_suggestions/field_value_combobox'; export type { AllSeries } from './components/shared/exploratory_view/hooks/use_series_storage'; -export type { SeriesUrl } from './components/shared/exploratory_view/types'; +export type { SeriesUrl, ReportViewType } from './components/shared/exploratory_view/types'; export type { ObservabilityRuleTypeFormatter, @@ -99,6 +99,7 @@ export type { } from './rules/create_observability_rule_type_registry'; export { createObservabilityRuleTypeRegistryMock } from './rules/observability_rule_type_registry_mock'; export type { ExploratoryEmbeddableProps } from './components/shared/exploratory_view/embeddable/embeddable'; +export type { ActionTypes } from './components/shared/exploratory_view/embeddable/use_actions'; export type { AddInspectorRequest } from './context/inspector/inspector_context'; export { InspectorContextProvider } from './context/inspector/inspector_context'; From 78103870bbbf6848206e1c5244e3253749b48393 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Wed, 23 Feb 2022 21:09:05 +0100 Subject: [PATCH 023/137] [Discover] Change error message when no indices match a given data view (#126158) --- src/plugins/data_views/server/fetcher/lib/errors.ts | 2 +- .../apis/management/rollup/index_patterns_extensions.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/data_views/server/fetcher/lib/errors.ts b/src/plugins/data_views/server/fetcher/lib/errors.ts index 4bc3f01c5d4188..c69e177535b76f 100644 --- a/src/plugins/data_views/server/fetcher/lib/errors.ts +++ b/src/plugins/data_views/server/fetcher/lib/errors.ts @@ -29,7 +29,7 @@ export function isEsIndexNotFoundError(err: any) { * @return {Boom} */ export function createNoMatchingIndicesError(pattern: string[] | string) { - const err = Boom.notFound(`No indices match pattern "${pattern}"`); + const err = Boom.notFound(`No indices match "${pattern}"`); (err.output.payload as any).code = ERR_NO_MATCHING_INDICES; return err; } diff --git a/x-pack/test/api_integration/apis/management/rollup/index_patterns_extensions.js b/x-pack/test/api_integration/apis/management/rollup/index_patterns_extensions.js index 1e13d58263ec70..be5eb76433b733 100644 --- a/x-pack/test/api_integration/apis/management/rollup/index_patterns_extensions.js +++ b/x-pack/test/api_integration/apis/management/rollup/index_patterns_extensions.js @@ -43,7 +43,7 @@ export default function ({ getService }) { { sort: false } )}`; ({ body } = await supertest.get(uri).expect(404)); - expect(body.message).to.contain('No indices match pattern "foo"'); + expect(body.message).to.contain('No indices match "foo"'); }); }); From f0cfbca6735e7c250236c9b3973e89f5c92c7882 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Wed, 23 Feb 2022 14:24:58 -0600 Subject: [PATCH 024/137] [cft] Enable enterprise search, integrations server, and ml (#125847) * wip * cleanup * fix syntax * update all versions * newline --- .../scripts/steps/cloud/build_and_deploy.sh | 13 +-- .buildkite/scripts/steps/cloud/deploy.json | 86 ++++++++++++++++--- 2 files changed, 82 insertions(+), 17 deletions(-) diff --git a/.buildkite/scripts/steps/cloud/build_and_deploy.sh b/.buildkite/scripts/steps/cloud/build_and_deploy.sh index 7227d3a8a5e57f..0d18ca7207b1a5 100755 --- a/.buildkite/scripts/steps/cloud/build_and_deploy.sh +++ b/.buildkite/scripts/steps/cloud/build_and_deploy.sh @@ -42,7 +42,9 @@ if [ -z "${CLOUD_DEPLOYMENT_ID}" ]; then .resources.kibana[0].plan.kibana.docker_image = "'$CLOUD_IMAGE'" | .name = "'$CLOUD_DEPLOYMENT_NAME'" | .resources.kibana[0].plan.kibana.version = "'$VERSION'" | - .resources.elasticsearch[0].plan.elasticsearch.version = "'$VERSION'" + .resources.elasticsearch[0].plan.elasticsearch.version = "'$VERSION'" | + .resources.enterprise_search[0].plan.enterprise_search.version = "'$VERSION'" | + .resources.integrations_server[0].plan.integrations_server.version = "'$VERSION'" ' .buildkite/scripts/steps/cloud/deploy.json > /tmp/deploy.json ecctl deployment create --track --output json --file /tmp/deploy.json &> "$JSON_FILE" @@ -59,11 +61,10 @@ if [ -z "${CLOUD_DEPLOYMENT_ID}" ]; then retry 5 5 vault write "secret/kibana-issues/dev/cloud-deploy/$CLOUD_DEPLOYMENT_NAME" username="$CLOUD_DEPLOYMENT_USERNAME" password="$CLOUD_DEPLOYMENT_PASSWORD" else - ecctl deployment show "$CLOUD_DEPLOYMENT_ID" --generate-update-payload | jq ' - .resources.kibana[0].plan.kibana.docker_image = "'$CLOUD_IMAGE'" | - .resources.kibana[0].plan.kibana.version = "'$VERSION'" | - .resources.elasticsearch[0].plan.elasticsearch.version = "'$VERSION'" - ' > /tmp/deploy.json +ecctl deployment show "$CLOUD_DEPLOYMENT_ID" --generate-update-payload | jq ' + .resources.kibana[0].plan.kibana.docker_image = "'$CLOUD_IMAGE'" | + (.. | select(.version? != null).version) = "'$VERSION'" + ' > /tmp/deploy.json ecctl deployment update "$CLOUD_DEPLOYMENT_ID" --track --output json --file /tmp/deploy.json &> "$JSON_FILE" fi diff --git a/.buildkite/scripts/steps/cloud/deploy.json b/.buildkite/scripts/steps/cloud/deploy.json index 37768144f138ad..62edb7b6a11c92 100644 --- a/.buildkite/scripts/steps/cloud/deploy.json +++ b/.buildkite/scripts/steps/cloud/deploy.json @@ -1,5 +1,27 @@ { "resources": { + "integrations_server": [ + { + "elasticsearch_cluster_ref_id": "main-elasticsearch", + "region": "gcp-us-west2", + "plan": { + "cluster_topology": [ + { + "instance_configuration_id": "gcp.integrationsserver.1", + "zone_count": 1, + "size": { + "value": 512, + "resource": "memory" + } + } + ], + "integrations_server": { + "version": null + } + }, + "ref_id": "main-integrations_server" + } + ], "elasticsearch": [ { "region": "gcp-us-west2", @@ -12,7 +34,10 @@ { "zone_count": 2, "instance_configuration_id": "gcp.coordinating.1", - "node_roles": ["ingest", "remote_cluster_client"], + "node_roles": [ + "ingest", + "remote_cluster_client" + ], "id": "coordinating", "size": { "resource": "memory", @@ -54,7 +79,10 @@ "enabled_built_in_plugins": [] }, "instance_configuration_id": "gcp.data.highstorage.1", - "node_roles": ["data_warm", "remote_cluster_client"], + "node_roles": [ + "data_warm", + "remote_cluster_client" + ], "id": "warm", "size": { "resource": "memory", @@ -70,7 +98,10 @@ "enabled_built_in_plugins": [] }, "instance_configuration_id": "gcp.data.highstorage.1", - "node_roles": ["data_cold", "remote_cluster_client"], + "node_roles": [ + "data_cold", + "remote_cluster_client" + ], "id": "cold", "size": { "resource": "memory", @@ -86,7 +117,9 @@ "enabled_built_in_plugins": [] }, "instance_configuration_id": "gcp.es.datafrozen.n1.64x10x95", - "node_roles": ["data_frozen"], + "node_roles": [ + "data_frozen" + ], "id": "frozen", "size": { "resource": "memory", @@ -96,7 +129,10 @@ { "zone_count": 3, "instance_configuration_id": "gcp.master.1", - "node_roles": ["master", "remote_cluster_client"], + "node_roles": [ + "master", + "remote_cluster_client" + ], "id": "master", "size": { "resource": "memory", @@ -109,11 +145,14 @@ { "zone_count": 1, "instance_configuration_id": "gcp.ml.1", - "node_roles": ["ml", "remote_cluster_client"], + "node_roles": [ + "ml", + "remote_cluster_client" + ], "id": "ml", "size": { - "resource": "memory", - "value": 0 + "value": 1024, + "resource": "memory" }, "elasticsearch": { "enabled_built_in_plugins": [] @@ -130,7 +169,33 @@ "ref_id": "main-elasticsearch" } ], - "enterprise_search": [], + "enterprise_search": [ + { + "elasticsearch_cluster_ref_id": "main-elasticsearch", + "region": "gcp-us-west2", + "plan": { + "cluster_topology": [ + { + "node_type": { + "connector": true, + "appserver": true, + "worker": true + }, + "instance_configuration_id": "gcp.enterprisesearch.1", + "zone_count": 1, + "size": { + "resource": "memory", + "value": 2048 + } + } + ], + "enterprise_search": { + "version": null + } + }, + "ref_id": "main-enterprise_search" + } + ], "kibana": [ { "elasticsearch_cluster_ref_id": "main-elasticsearch", @@ -153,8 +218,7 @@ }, "ref_id": "main-kibana" } - ], - "apm": [] + ] }, "name": null, "metadata": { From db116e0568745235f901ad1a5784af8e369893e2 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Wed, 23 Feb 2022 14:46:39 -0600 Subject: [PATCH 025/137] [cloud deploy] Enable stack monitoring (#126278) --- .../scripts/steps/cloud/build_and_deploy.sh | 7 +++++++ .../scripts/steps/cloud/stack_monitoring.json | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 .buildkite/scripts/steps/cloud/stack_monitoring.json diff --git a/.buildkite/scripts/steps/cloud/build_and_deploy.sh b/.buildkite/scripts/steps/cloud/build_and_deploy.sh index 0d18ca7207b1a5..8b202bea9fc84c 100755 --- a/.buildkite/scripts/steps/cloud/build_and_deploy.sh +++ b/.buildkite/scripts/steps/cloud/build_and_deploy.sh @@ -53,6 +53,13 @@ if [ -z "${CLOUD_DEPLOYMENT_ID}" ]; then CLOUD_DEPLOYMENT_ID=$(jq -r --slurp '.[0].id' "$JSON_FILE") CLOUD_DEPLOYMENT_STATUS_MESSAGES=$(jq --slurp '[.[]|select(.resources == null)]' "$JSON_FILE") + # Enable stack monitoring + jq ' + .settings.observability.metrics.destination.deployment_id = "'$CLOUD_DEPLOYMENT_ID'" | + .settings.observability.logging.destination.deployment_id = "'$CLOUD_DEPLOYMENT_ID'" + ' .buildkite/scripts/steps/cloud/stack_monitoring.json > /tmp/stack_monitoring.json + ecctl deployment update "$CLOUD_DEPLOYMENT_ID" --track --output json --file /tmp/stack_monitoring.json &> "$JSON_FILE" + # Refresh vault token VAULT_ROLE_ID="$(retry 5 15 gcloud secrets versions access latest --secret=kibana-buildkite-vault-role-id)" VAULT_SECRET_ID="$(retry 5 15 gcloud secrets versions access latest --secret=kibana-buildkite-vault-secret-id)" diff --git a/.buildkite/scripts/steps/cloud/stack_monitoring.json b/.buildkite/scripts/steps/cloud/stack_monitoring.json new file mode 100644 index 00000000000000..a7d49cf58ad8b2 --- /dev/null +++ b/.buildkite/scripts/steps/cloud/stack_monitoring.json @@ -0,0 +1,19 @@ +{ + "prune_orphans": false, + "settings": { + "observability": { + "metrics": { + "destination": { + "deployment_id": null, + "ref_id": "main-elasticsearch" + } + }, + "logging": { + "destination": { + "deployment_id": null, + "ref_id": "main-elasticsearch" + } + } + } + } +} From bc356100692ad764313a2e984113c00644b83808 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Wed, 23 Feb 2022 13:59:29 -0700 Subject: [PATCH 026/137] [Maps] remove 'Top term' metric from ESGeoGridSource editor (#125803) * [Maps] remove 'Top term' metric from ESGeoGridSource editor * simplify comment * update snapshot Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../update_source_editor.test.tsx.snap | 1 + .../update_source_editor.tsx | 24 +++++++++---------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/update_source_editor.test.tsx.snap b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/update_source_editor.test.tsx.snap index 7043613b8e20ae..c3c029ffd52905 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/update_source_editor.test.tsx.snap +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/update_source_editor.test.tsx.snap @@ -80,6 +80,7 @@ exports[`source editor geo_grid_source should render editor 1`] = ` fields={Array []} key="12345" metrics={Array []} + metricsFilter={[Function]} onChange={[Function]} /> diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.tsx b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.tsx index fb748cdc63aff2..4754d26702c9ce 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.tsx @@ -106,18 +106,18 @@ export class UpdateSourceEditor extends Component { }; _getMetricsFilter() { - if (this.props.currentLayerType === LAYER_TYPE.HEATMAP) { - return (metric: EuiComboBoxOptionOption) => { - // these are countable metrics, where blending heatmap color blobs make sense - return metric.value ? isMetricCountable(metric.value) : false; - }; - } - - if (this.props.resolution === GRID_RESOLUTION.SUPER_FINE) { - return (metric: EuiComboBoxOptionOption) => { - return metric.value !== AGG_TYPE.TERMS; - }; - } + return this.props.currentLayerType === LAYER_TYPE.HEATMAP + ? (metric: EuiComboBoxOptionOption) => { + // these are countable metrics, where blending heatmap color blobs make sense + return metric.value ? isMetricCountable(metric.value) : false; + } + : (metric: EuiComboBoxOptionOption) => { + // terms aggregation is not supported with Elasticsearch _mvt endpoint + // The goal is to remove GeoJSON ESGeoGridSource implemenation and only have MVT ESGeoGridSource implemenation + // First step is to deprecate terms aggregation for ESGeoGridSource + // and prevent new uses of terms aggregation for ESGeoGridSource + return metric.value !== AGG_TYPE.TERMS; + }; } _renderMetricsPanel() { From 548edcaf06ee49823db8336123846c2fe6cae975 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Wed, 23 Feb 2022 14:00:59 -0700 Subject: [PATCH 027/137] [Maps] fix vector tile URL not properly encoded (#126208) * [Maps] fix vector tile URL not properly encoded * clean up variable name * tslint Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../maps/common/mvt_request_body.test.ts | 40 +++++++++++++++++++ .../plugins/maps/common/mvt_request_body.ts | 17 ++++++++ .../es_geo_grid_source.test.ts | 2 +- .../es_geo_grid_source/es_geo_grid_source.tsx | 7 +--- .../es_search_source/es_search_source.test.ts | 2 +- .../es_search_source/es_search_source.tsx | 7 +--- x-pack/plugins/maps/server/mvt/mvt_routes.ts | 10 ++--- 7 files changed, 66 insertions(+), 19 deletions(-) create mode 100644 x-pack/plugins/maps/common/mvt_request_body.test.ts create mode 100644 x-pack/plugins/maps/common/mvt_request_body.ts diff --git a/x-pack/plugins/maps/common/mvt_request_body.test.ts b/x-pack/plugins/maps/common/mvt_request_body.test.ts new file mode 100644 index 00000000000000..d3efce7f997c9a --- /dev/null +++ b/x-pack/plugins/maps/common/mvt_request_body.test.ts @@ -0,0 +1,40 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { decodeMvtResponseBody, encodeMvtResponseBody } from './mvt_request_body'; + +test('Should encode shape into URI safe string and decode back to original shape', () => { + const searchRequest = { + docvalue_fields: [], + size: 10000, + _source: false, + script_fields: {}, + stored_fields: ['geopoint'], + runtime_mappings: { + 'day of week': { + type: 'keyword', + script: { + source: + "ZonedDateTime input = doc['ISSUE_DATE'].value;\nString output = input.format(DateTimeFormatter.ofPattern('e')) + ' ' + input.format(DateTimeFormatter.ofPattern('E'));\nemit(output);", + }, + }, + }, + query: { + bool: { + must: [], + filter: [], + should: [], + must_not: [], + }, + }, + }; + const encodedSearchRequest = encodeMvtResponseBody(searchRequest); + expect(encodedSearchRequest).toBe( + `(_source%3A!f%2Cdocvalue_fields%3A!()%2Cquery%3A(bool%3A(filter%3A!()%2Cmust%3A!()%2Cmust_not%3A!()%2Cshould%3A!()))%2Cruntime_mappings%3A('day%20of%20week'%3A(script%3A(source%3A'ZonedDateTime%20input%20%3D%20doc%5B!'ISSUE_DATE!'%5D.value%3B%0AString%20output%20%3D%20input.format(DateTimeFormatter.ofPattern(!'e!'))%20%2B%20!'%20!'%20%2B%20input.format(DateTimeFormatter.ofPattern(!'E!'))%3B%0Aemit(output)%3B')%2Ctype%3Akeyword))%2Cscript_fields%3A()%2Csize%3A10000%2Cstored_fields%3A!(geopoint))` + ); + expect(decodeMvtResponseBody(encodedSearchRequest)).toEqual(searchRequest); +}); diff --git a/x-pack/plugins/maps/common/mvt_request_body.ts b/x-pack/plugins/maps/common/mvt_request_body.ts new file mode 100644 index 00000000000000..16f9d2cce63817 --- /dev/null +++ b/x-pack/plugins/maps/common/mvt_request_body.ts @@ -0,0 +1,17 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { RisonValue } from 'rison-node'; +import rison from 'rison-node'; + +export function decodeMvtResponseBody(encodedRequestBody: string): object { + return rison.decode(decodeURIComponent(encodedRequestBody)) as object; +} + +export function encodeMvtResponseBody(unencodedRequestBody: object): string { + return encodeURIComponent(rison.encode(unencodedRequestBody as RisonValue)); +} diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts index eb8db6c786e292..e2f9959b25d31d 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts @@ -303,7 +303,7 @@ describe('ESGeoGridSource', () => { const tileUrl = await mvtGeogridSource.getTileUrl(vectorSourceRequestMeta, '1234'); expect(tileUrl).toEqual( - "rootdir/api/maps/mvt/getGridTile/{z}/{x}/{y}.pbf?geometryFieldName=bar&index=undefined&gridPrecision=8&requestBody=(foobar:ES_DSL_PLACEHOLDER,params:('0':('0':index,'1':(fields:())),'1':('0':size,'1':0),'2':('0':filter,'1':!()),'3':('0':query),'4':('0':index,'1':(fields:())),'5':('0':query,'1':(language:KQL,query:'')),'6':('0':aggs,'1':())))&requestType=point&token=1234" + "rootdir/api/maps/mvt/getGridTile/{z}/{x}/{y}.pbf?geometryFieldName=bar&index=undefined&gridPrecision=8&requestBody=(foobar%3AES_DSL_PLACEHOLDER%2Cparams%3A('0'%3A('0'%3Aindex%2C'1'%3A(fields%3A()))%2C'1'%3A('0'%3Asize%2C'1'%3A0)%2C'2'%3A('0'%3Afilter%2C'1'%3A!())%2C'3'%3A('0'%3Aquery)%2C'4'%3A('0'%3Aindex%2C'1'%3A(fields%3A()))%2C'5'%3A('0'%3Aquery%2C'1'%3A(language%3AKQL%2Cquery%3A''))%2C'6'%3A('0'%3Aaggs%2C'1'%3A())))&requestType=point&token=1234" ); }); }); diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.tsx b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.tsx index 2b9ab6df2d1d04..2eff5ce712ad53 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.tsx @@ -8,7 +8,6 @@ import React, { ReactElement } from 'react'; import { i18n } from '@kbn/i18n'; -import rison from 'rison-node'; import { Feature } from 'geojson'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { makeESBbox } from '../../../../common/elasticsearch_util'; @@ -26,6 +25,7 @@ import { SOURCE_TYPES, VECTOR_SHAPE_TYPE, } from '../../../../common/constants'; +import { encodeMvtResponseBody } from '../../../../common/mvt_request_body'; import { getDataSourceLabel, getDataViewLabel } from '../../../../common/i18n_getters'; import { AbstractESAggSource } from '../es_agg_source'; import { DataRequestAbortError } from '../../util/data_request'; @@ -447,9 +447,6 @@ export class ESGeoGridSource extends AbstractESAggSource implements IMvtVectorSo const indexPattern = await this.getIndexPattern(); const searchSource = await this.makeSearchSource(searchFilters, 0); searchSource.setField('aggs', this.getValueAggsDsl(indexPattern)); - const dsl = searchSource.getSearchRequestBody(); - - const risonDsl = rison.encode(dsl); const mvtUrlServicePath = getHttp().basePath.prepend( `/${GIS_API_PATH}/${MVT_GETGRIDTILE_API_PATH}/{z}/{x}/{y}.pbf` @@ -462,7 +459,7 @@ export class ESGeoGridSource extends AbstractESAggSource implements IMvtVectorSo ?geometryFieldName=${this._descriptor.geoField}\ &index=${indexPattern.title}\ &gridPrecision=${this._getGeoGridPrecisionResolutionDelta()}\ -&requestBody=${risonDsl}\ +&requestBody=${encodeMvtResponseBody(searchSource.getSearchRequestBody())}\ &requestType=${requestType}\ &token=${refreshToken}`; } diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.test.ts b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.test.ts index 45428a2a28b736..cbf79b7a8b3671 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.test.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.test.ts @@ -115,7 +115,7 @@ describe('ESSearchSource', () => { }); const tileUrl = await esSearchSource.getTileUrl(searchFilters, '1234'); expect(tileUrl).toBe( - `rootdir/api/maps/mvt/getTile/{z}/{x}/{y}.pbf?geometryFieldName=bar&index=foobar-title-*&requestBody=(foobar:ES_DSL_PLACEHOLDER,params:('0':('0':index,'1':(fields:(),title:'foobar-title-*')),'1':('0':size,'1':1000),'2':('0':filter,'1':!()),'3':('0':query),'4':('0':index,'1':(fields:(),title:'foobar-title-*')),'5':('0':query,'1':(language:KQL,query:'tooltipField: foobar')),'6':('0':fieldsFromSource,'1':!(tooltipField,styleField)),'7':('0':source,'1':!(tooltipField,styleField))))&token=1234` + `rootdir/api/maps/mvt/getTile/{z}/{x}/{y}.pbf?geometryFieldName=bar&index=foobar-title-*&requestBody=(foobar%3AES_DSL_PLACEHOLDER%2Cparams%3A('0'%3A('0'%3Aindex%2C'1'%3A(fields%3A()%2Ctitle%3A'foobar-title-*'))%2C'1'%3A('0'%3Asize%2C'1'%3A1000)%2C'2'%3A('0'%3Afilter%2C'1'%3A!())%2C'3'%3A('0'%3Aquery)%2C'4'%3A('0'%3Aindex%2C'1'%3A(fields%3A()%2Ctitle%3A'foobar-title-*'))%2C'5'%3A('0'%3Aquery%2C'1'%3A(language%3AKQL%2Cquery%3A'tooltipField%3A%20foobar'))%2C'6'%3A('0'%3AfieldsFromSource%2C'1'%3A!(tooltipField%2CstyleField))%2C'7'%3A('0'%3Asource%2C'1'%3A!(tooltipField%2CstyleField))))&token=1234` ); }); }); diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx index 5c44c5612bf537..e703561357a070 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx @@ -7,7 +7,6 @@ import _ from 'lodash'; import React, { ReactElement } from 'react'; -import rison from 'rison-node'; import { i18n } from '@kbn/i18n'; import { GeoJsonProperties, Geometry, Position } from 'geojson'; import { type Filter, buildPhraseFilter } from '@kbn/es-query'; @@ -27,6 +26,7 @@ import { PreIndexedShape, TotalHits, } from '../../../../common/elasticsearch_util'; +import { encodeMvtResponseBody } from '../../../../common/mvt_request_body'; // @ts-expect-error import { UpdateSourceEditor } from './update_source_editor'; import { @@ -836,9 +836,6 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource searchSource.setField('sort', this._buildEsSort()); } - const dsl = searchSource.getSearchRequestBody(); - const risonDsl = rison.encode(dsl); - const mvtUrlServicePath = getHttp().basePath.prepend( `/${GIS_API_PATH}/${MVT_GETTILE_API_PATH}/{z}/{x}/{y}.pbf` ); @@ -846,7 +843,7 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource return `${mvtUrlServicePath}\ ?geometryFieldName=${this._descriptor.geoField}\ &index=${indexPattern.title}\ -&requestBody=${risonDsl}\ +&requestBody=${encodeMvtResponseBody(searchSource.getSearchRequestBody())}\ &token=${refreshToken}`; } diff --git a/x-pack/plugins/maps/server/mvt/mvt_routes.ts b/x-pack/plugins/maps/server/mvt/mvt_routes.ts index ad6b3f8eb1235e..5fdaea9ab66dfc 100644 --- a/x-pack/plugins/maps/server/mvt/mvt_routes.ts +++ b/x-pack/plugins/maps/server/mvt/mvt_routes.ts @@ -5,7 +5,6 @@ * 2.0. */ -import rison from 'rison-node'; import { Stream } from 'stream'; import { schema } from '@kbn/config-schema'; import { CoreStart, KibanaRequest, KibanaResponseFactory, Logger } from 'src/core/server'; @@ -17,6 +16,7 @@ import { MVT_GETGRIDTILE_API_PATH, RENDER_AS, } from '../../common/constants'; +import { decodeMvtResponseBody } from '../../common/mvt_request_body'; import { getEsTile } from './get_tile'; import { getEsGridTile } from './get_grid_tile'; @@ -57,8 +57,6 @@ export function initMVTRoutes({ const abortController = makeAbortController(request); - const requestBodyDSL = rison.decode(query.requestBody as string); - const gzippedTile = await getEsTile({ url: `${API_ROOT_PATH}/${MVT_GETTILE_API_PATH}/{z}/{x}/{y}.pbf`, core, @@ -69,7 +67,7 @@ export function initMVTRoutes({ y: parseInt((params as any).y, 10) as number, z: parseInt((params as any).z, 10) as number, index: query.index as string, - requestBody: requestBodyDSL as any, + requestBody: decodeMvtResponseBody(query.requestBody as string) as any, abortController, }); @@ -105,8 +103,6 @@ export function initMVTRoutes({ const abortController = makeAbortController(request); - const requestBodyDSL = rison.decode(query.requestBody as string); - const gzipTileStream = await getEsGridTile({ url: `${API_ROOT_PATH}/${MVT_GETGRIDTILE_API_PATH}/{z}/{x}/{y}.pbf`, core, @@ -117,7 +113,7 @@ export function initMVTRoutes({ y: parseInt((params as any).y, 10) as number, z: parseInt((params as any).z, 10) as number, index: query.index as string, - requestBody: requestBodyDSL as any, + requestBody: decodeMvtResponseBody(query.requestBody as string) as any, requestType: query.requestType as RENDER_AS.POINT | RENDER_AS.GRID, gridPrecision: parseInt(query.gridPrecision, 10), abortController, From 6bf8a9c709ed1b18d39087b96dfe253aa5675964 Mon Sep 17 00:00:00 2001 From: Bree Hall <40739624+breehall@users.noreply.github.com> Date: Wed, 23 Feb 2022 17:01:40 -0500 Subject: [PATCH 028/137] Upgrade EUI to v48.1.1 (#125023) * Upgraded the version of EUI to 47.0.0 and react-beautiful-dnd (an EUI dependancy) to 13.1.0 * Update i18n mappings with required changes * Fix all data grid types/tests missing the new `colIndex` prop passed by renderCellValue and cellActions * Fix cellActions closePopover type to indicate conditionality (only passed when popover is open) * Fix more datagrid colIndex errors - pass more missing `colIndex`s - pass RowAction colIndex, because it's inheriting types from EUI - omit colIndex from the leading controls column renderer, because it doesn't need them * Improve StatefulCell typing - pass colIndex (which fixes EUI type match issue) - DRY out ariaColIndex logic - rename ariaRowindex passed to StatefulCell to rowIndex * Updated i18n_eui_mapping tests to add euiSelectable.searchResults to the tokensToSkip array * Fix failing ML datagrid FTR test - `visibleRowIndex` is not a prop passed back by `popoverContents` (see `EuiDataGridCellValueElementProps`), but `rowIndex` is * Revert attempted ML type change - in actuality this is an EUI typing issue, children is a ReactElement, not a ReactNode. However we'll shortly be deprecationg popoverContents, so this isn't worth fixing right now * Update test files to include up to date snapshots of code samples * Updated a test snapshots to match the latest version of code samples * Upgraded the version of EUI from 47.0.0 to 48.0.0 in package.json and license_checker config files * Update the required i18n translation mapping file with additions and changes from EUI version 48.0.0 * Updated three security screen accessibility tests to check for the aria-checked attribute instead of the aria-selected attribute as part of an accessibility update to aria made in EUI PR 5581 * Updated two unit cases to that are responsible for checking strict equality of strings. These unit tests were for the EuiSelectable and EuiFilterGroup components. Both of these components contain and utilize EuiScreenReaderOnly which provides text that is used for screen readers, but can still be viewed and queried in the DOM. These tests have been updated with the EuiScreenReaderOnly text in mind. * Code clean up and added a missing internationalization token * Ran yarn kbn bootstrap to update the yarn lock file * Fix failing ML FTR test - EuiSelectable now relies on aria-checked to indicate selected state, per W3 spec * Fix failing functional tests that click the datagrid cell expand button .euiDataGridRowCell__expandButtonIcon was deprecated in favor of a shared .euiDataGridRowCell__actionButtonIcon class, but the expand action is always the last one * Upgrade to 48.1.1 * Switch to data-test-subj for datagrid cell expansion selectors * Switch to new `data-test-selected` attribute over `aria-checked` * Update snapshots/Jest tests to account for EuiSelectable use in EuiFilter Co-authored-by: Constance Chen Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- package.json | 4 +- .../__snapshots__/i18n_service.test.tsx.snap | 17 +- src/core/public/i18n/i18n_eui_mapping.test.ts | 2 +- src/core/public/i18n/i18n_eui_mapping.tsx | 60 +++- src/dev/license_checker/config.ts | 2 +- .../__snapshots__/data_view.test.tsx.snap | 8 +- .../url/__snapshots__/url.test.tsx.snap | 5 + .../discover_grid_cell_actions.test.tsx | 6 +- .../discover_grid_document_selection.test.tsx | 4 + .../discover_grid_expand_button.test.tsx | 3 + .../get_render_cell_value.test.tsx | 16 ++ .../__snapshots__/cron_editor.test.tsx.snap | 30 ++ .../public/components/table_vis_columns.tsx | 4 +- .../apps/dashboard/dashboard_filter_bar.ts | 4 +- .../apps/discover/_data_grid_doc_table.ts | 8 +- .../extended_template.stories.storyshot | 2 + .../custom_element_modal.stories.storyshot | 12 + .../workpad_table.stories.storyshot | 2 +- .../saved_elements_modal.stories.storyshot | 1 + .../__snapshots__/edit_var.stories.storyshot | 12 + .../extended_template.stories.storyshot | 21 ++ .../extended_template.stories.storyshot | 4 + .../__snapshots__/settings.test.tsx.snap | 1 + .../autoplay_settings.stories.storyshot | 3 + .../components/cell_value.test.tsx | 3 + .../components/columns.tsx | 4 +- .../components/data_grid/data_grid.tsx | 4 +- .../render_cell_value.test.tsx | 1 + .../cypress/screens/alerts_details.ts | 2 +- .../render_cell_value.test.tsx | 1 + .../observablity_alerts/render_cell_value.tsx | 2 + .../render_cell_value.test.tsx | 1 + .../render_cell_value.tsx | 2 + .../render_cell_value.test.tsx | 1 + .../render_cell_value.tsx | 2 + .../effected_policy_select.test.tsx | 5 +- .../view/components/form/index.test.tsx | 5 +- .../view/components/form.test.tsx | 6 +- .../create_trusted_app_form.test.tsx | 2 +- .../__snapshots__/index.test.tsx.snap | 8 +- .../body/data_driven_columns/index.tsx | 8 +- .../stateful_cell.test.tsx | 21 +- .../data_driven_columns/stateful_cell.tsx | 9 +- .../default_cell_renderer.test.tsx | 7 + .../t_grid/body/data_driven_columns/index.tsx | 8 +- .../stateful_cell.test.tsx | 21 +- .../data_driven_columns/stateful_cell.tsx | 9 +- .../public/components/t_grid/body/index.tsx | 4 + .../t_grid/event_rendered_view/index.tsx | 2 +- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - .../es_deprecations/deprecations_list.test.ts | 6 +- .../kibana_deprecations.helpers.ts | 7 +- .../filter_group/filter_group.test.tsx | 4 +- .../services/ml/stack_management_jobs.ts | 2 +- yarn.lock | 262 +++++++++++------- 56 files changed, 469 insertions(+), 183 deletions(-) diff --git a/package.json b/package.json index c9d8a75da2cc04..72309e07bde772 100644 --- a/package.json +++ b/package.json @@ -109,7 +109,7 @@ "@elastic/datemath": "link:bazel-bin/packages/elastic-datemath", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@8.1.0-canary.2", "@elastic/ems-client": "8.0.0", - "@elastic/eui": "46.1.0", + "@elastic/eui": "48.1.1", "@elastic/filesaver": "1.1.2", "@elastic/node-crypto": "1.2.1", "@elastic/numeral": "^2.5.1", @@ -336,7 +336,7 @@ "re2": "^1.16.0", "react": "^16.12.0", "react-ace": "^7.0.5", - "react-beautiful-dnd": "^13.0.0", + "react-beautiful-dnd": "^13.1.0", "react-color": "^2.13.8", "react-dom": "^16.12.0", "react-dropzone": "^4.2.9", diff --git a/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap b/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap index d4a3bd3a7ea742..df924685f09015 100644 --- a/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap +++ b/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap @@ -83,7 +83,7 @@ exports[`#start() returns \`Context\` component 1`] = ` "euiDataGrid.ariaLabelledBy": [Function], "euiDataGrid.screenReaderNotice": "Cell contains interactive content.", "euiDataGridCell.position": [Function], - "euiDataGridCellButtons.expandButtonTitle": "Click or hit enter to interact with cell content", + "euiDataGridCellActions.expandButtonTitle": "Click or hit enter to interact with cell content", "euiDataGridHeaderCell.headerActions": "Header actions", "euiDataGridPagination.detailedPaginationLabel": [Function], "euiDataGridPagination.paginationLabel": "Pagination for preceding grid", @@ -206,10 +206,15 @@ exports[`#start() returns \`Context\` component 1`] = ` "euiSelectable.noAvailableOptions": "No options available", "euiSelectable.noMatchingOptions": [Function], "euiSelectable.placeholderName": "Filter options", + "euiSelectable.screenReaderInstructions": "Use up and down arrows to move focus over options. Enter to select. Escape to collapse options.", + "euiSelectable.searchResults": [Function], + "euiSelectableListItem.checkedOption": "Checked option.", + "euiSelectableListItem.checkedOptionInstructions": "To uncheck this option, press enter.", "euiSelectableListItem.excludedOption": "Excluded option.", - "euiSelectableListItem.excludedOptionInstructions": "To deselect this option, press enter.", - "euiSelectableListItem.includedOption": "Included option.", + "euiSelectableListItem.excludedOptionInstructions": "To uncheck this option, press enter.", + "euiSelectableListItem.includedOption": "Selected option.", "euiSelectableListItem.includedOptionInstructions": "To exclude this option, press enter.", + "euiSelectableListItem.unckeckedOptionInstructions": "To select this option, press enter.", "euiSelectableTemplateSitewide.loadingResults": "Loading results", "euiSelectableTemplateSitewide.noResults": "No results available", "euiSelectableTemplateSitewide.onFocusBadgeGoTo": "Go to", @@ -231,6 +236,12 @@ exports[`#start() returns \`Context\` component 1`] = ` "euiStepStrings.simpleWarning": [Function], "euiStepStrings.step": [Function], "euiStepStrings.warning": [Function], + "euiSuggest.stateLoading": "State: loading.", + "euiSuggest.stateSaved": "State: saved.", + "euiSuggest.stateSavedTooltip": "Saved.", + "euiSuggest.stateUnchanged": "State: unchanged.", + "euiSuggest.stateUnsaved": "State: unsaved.", + "euiSuggest.stateUnsavedTooltip": "Changes have not been saved.", "euiSuperSelect.screenReaderAnnouncement": "You are in a form selector and must select a single option. Use the up and down keys to navigate or escape to close.", "euiSuperSelectControl.selectAnOption": [Function], "euiSuperUpdateButton.cannotUpdateTooltip": "Cannot update", diff --git a/src/core/public/i18n/i18n_eui_mapping.test.ts b/src/core/public/i18n/i18n_eui_mapping.test.ts index d8d48a8e5f1d5a..2b29fa4d0d66c9 100644 --- a/src/core/public/i18n/i18n_eui_mapping.test.ts +++ b/src/core/public/i18n/i18n_eui_mapping.test.ts @@ -76,7 +76,7 @@ describe('@elastic/eui i18n tokens', () => { test('defaultMessage is in sync with defString', () => { // Certain complex tokens (e.g. ones that have a function as a defaultMessage) // need custom i18n handling, and can't be checked for basic defString equality - const tokensToSkip = ['euiColumnSorting.buttonActive']; + const tokensToSkip = ['euiColumnSorting.buttonActive', 'euiSelectable.searchResults']; if (tokensToSkip.includes(token)) return; // Clean up typical errors from the `@elastic/eui` extraction token tool diff --git a/src/core/public/i18n/i18n_eui_mapping.tsx b/src/core/public/i18n/i18n_eui_mapping.tsx index 4603a2074c4d9f..135a515e1e22db 100644 --- a/src/core/public/i18n/i18n_eui_mapping.tsx +++ b/src/core/public/i18n/i18n_eui_mapping.tsx @@ -397,8 +397,8 @@ export const getEuiContextMapping = (): EuiTokensObject => { defaultMessage: 'Row: {row}; Column: {col}', values: { row, col }, }), - 'euiDataGridCellButtons.expandButtonTitle': i18n.translate( - 'core.euiDataGridCellButtons.expandButtonTitle', + 'euiDataGridCellActions.expandButtonTitle': i18n.translate( + 'core.euiDataGridCellActions.expandButtonTitle', { defaultMessage: 'Click or hit enter to interact with cell content', } @@ -954,13 +954,37 @@ export const getEuiContextMapping = (): EuiTokensObject => { values={{ searchValue }} /> ), + 'euiSelectable.screenReaderInstructions': i18n.translate( + 'core.euiSelectable.screenReaderInstructions', + { + defaultMessage: + 'Use up and down arrows to move focus over options. Enter to select. Escape to collapse options.', + } + ), + 'euiSelectable.searchResults': ({ resultsLength }: EuiValues) => + i18n.translate('core.euiSelectable.searchResults', { + defaultMessage: '{resultsLength, plural, one {# result} other {# results}}', + values: { resultsLength }, + }), 'euiSelectable.placeholderName': i18n.translate('core.euiSelectable.placeholderName', { defaultMessage: 'Filter options', }), + 'euiSelectableListItem.checkedOption': i18n.translate( + 'core.euiSelectableListItem.checkedOption', + { + defaultMessage: 'Checked option.', + } + ), + 'euiSelectableListItem.checkedOptionInstructions': i18n.translate( + 'core.euiSelectableListItem.checkedOptionInstructions', + { + defaultMessage: 'To uncheck this option, press enter.', + } + ), 'euiSelectableListItem.includedOption': i18n.translate( 'core.euiSelectableListItem.includedOption', { - defaultMessage: 'Included option.', + defaultMessage: 'Selected option.', } ), 'euiSelectableListItem.includedOptionInstructions': i18n.translate( @@ -978,7 +1002,13 @@ export const getEuiContextMapping = (): EuiTokensObject => { 'euiSelectableListItem.excludedOptionInstructions': i18n.translate( 'core.euiSelectableListItem.excludedOptionInstructions', { - defaultMessage: 'To deselect this option, press enter.', + defaultMessage: 'To uncheck this option, press enter.', + } + ), + 'euiSelectableListItem.unckeckedOptionInstructions': i18n.translate( + 'core.euiSelectableListItem.unckeckedOptionInstructions', + { + defaultMessage: 'To select this option, press enter.', } ), 'euiSelectableTemplateSitewide.loadingResults': i18n.translate( @@ -1088,6 +1118,28 @@ export const getEuiContextMapping = (): EuiTokensObject => { defaultMessage: 'Step {number} is loading', values: { number }, }), + 'euiSuggest.stateSavedTooltip': i18n.translate('core.euiSuggest.stateSavedTooltip', { + defaultMessage: 'Saved.', + }), + + 'euiSuggest.stateUnsavedTooltip': i18n.translate('core.euiSuggest.stateUnsavedTooltip', { + defaultMessage: 'Changes have not been saved.', + }), + + 'euiSuggest.stateLoading': i18n.translate('core.euiSuggest.stateLoading', { + defaultMessage: 'State: loading.', + }), + + 'euiSuggest.stateSaved': i18n.translate('core.euiSuggest.stateSaved', { + defaultMessage: 'State: saved.', + }), + + 'euiSuggest.stateUnsaved': i18n.translate('core.euiSuggest.stateUnsaved', { + defaultMessage: 'State: unsaved.', + }), + 'euiSuggest.stateUnchanged': i18n.translate('core.euiSuggest.stateUnchanged', { + defaultMessage: 'State: unchanged.', + }), 'euiSuperSelect.screenReaderAnnouncement': i18n.translate( 'core.euiSuperSelect.screenReaderAnnouncement', { diff --git a/src/dev/license_checker/config.ts b/src/dev/license_checker/config.ts index 6e05cf6d3f6b03..38cc61387e92c4 100644 --- a/src/dev/license_checker/config.ts +++ b/src/dev/license_checker/config.ts @@ -77,6 +77,6 @@ export const LICENSE_OVERRIDES = { 'jsts@1.6.2': ['Eclipse Distribution License - v 1.0'], // cf. https://github.com/bjornharrtell/jsts '@mapbox/jsonlint-lines-primitives@2.0.2': ['MIT'], // license in readme https://github.com/tmcw/jsonlint '@elastic/ems-client@8.0.0': ['Elastic License 2.0'], - '@elastic/eui@46.1.0': ['SSPL-1.0 OR Elastic License 2.0'], + '@elastic/eui@48.1.1': ['SSPL-1.0 OR Elastic License 2.0'], 'language-subtag-registry@0.3.21': ['CC-BY-4.0'], // retired ODC‑By license https://github.com/mattcg/language-subtag-registry }; diff --git a/src/plugins/data/public/utils/table_inspector_view/components/__snapshots__/data_view.test.tsx.snap b/src/plugins/data/public/utils/table_inspector_view/components/__snapshots__/data_view.test.tsx.snap index 4db3f780fdebe5..dcaba4c3f8e199 100644 --- a/src/plugins/data/public/utils/table_inspector_view/components/__snapshots__/data_view.test.tsx.snap +++ b/src/plugins/data/public/utils/table_inspector_view/components/__snapshots__/data_view.test.tsx.snap @@ -1292,11 +1292,13 @@ exports[`Inspector Data View component should render single table without select >
Type @@ -75,6 +76,7 @@ exports[`UrlFormatEditor should render normally 1`] = `