diff --git a/x-pack/plugins/infra/common/constants.ts b/x-pack/plugins/infra/common/constants.ts index 4c70e34c9899..9fcb488e02fd 100644 --- a/x-pack/plugins/infra/common/constants.ts +++ b/x-pack/plugins/infra/common/constants.ts @@ -5,7 +5,6 @@ * 2.0. */ -export const DEFAULT_SOURCE_ID = 'default'; export const METRICS_INDEX_PATTERN = 'metrics-*,metricbeat-*'; export const LOGS_INDEX_PATTERN = 'logs-*,filebeat-*,kibana_sample_data_logs*'; export const METRICS_APP = 'metrics'; diff --git a/x-pack/plugins/infra/common/log_analysis/job_parameters.ts b/x-pack/plugins/infra/common/log_analysis/job_parameters.ts index b8fa429195ff..1a695af80a4d 100644 --- a/x-pack/plugins/infra/common/log_analysis/job_parameters.ts +++ b/x-pack/plugins/infra/common/log_analysis/job_parameters.ts @@ -16,11 +16,11 @@ export const partitionField = 'event.dataset'; export const getJobIdPrefix = (spaceId: string, sourceId: string) => `kibana-logs-ui-${spaceId}-${sourceId}-`; -export const getJobId = (spaceId: string, sourceId: string, jobType: string) => - `${getJobIdPrefix(spaceId, sourceId)}${jobType}`; +export const getJobId = (spaceId: string, logViewId: string, jobType: string) => + `${getJobIdPrefix(spaceId, logViewId)}${jobType}`; -export const getDatafeedId = (spaceId: string, sourceId: string, jobType: string) => - `datafeed-${getJobId(spaceId, sourceId, jobType)}`; +export const getDatafeedId = (spaceId: string, logViewId: string, jobType: string) => + `datafeed-${getJobId(spaceId, logViewId, jobType)}`; export const datasetFilterRT = rt.union([ rt.strict({ diff --git a/x-pack/plugins/infra/common/log_views/types.ts b/x-pack/plugins/infra/common/log_views/types.ts index 5bf9585bb1c2..ca6dd95330f8 100644 --- a/x-pack/plugins/infra/common/log_views/types.ts +++ b/x-pack/plugins/infra/common/log_views/types.ts @@ -14,6 +14,7 @@ export interface LogViewsStaticConfig { export const logViewOriginRT = rt.keyof({ stored: null, internal: null, + inline: null, 'infra-source-stored': null, 'infra-source-internal': null, 'infra-source-fallback': null, diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/index.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/index.tsx index ad5ef8a99f23..bedd7a127394 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/index.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/index.tsx @@ -42,7 +42,10 @@ const AlertDetailsAppSection = ({ rule, alert }: AlertDetailsAppSectionProps) => { const { services: { @@ -33,7 +48,9 @@ export const AlertDropdown = () => { observability, }, } = useKibanaContextForPlugin(); - const canCreateAlerts = capabilities?.logs?.save ?? false; + const { isPersistedLogView } = useLogViewContext(); + const readOnly = !capabilities?.logs?.save; + const canCreateAlerts = (!readOnly && isPersistedLogView) ?? false; const [popoverOpen, setPopoverOpen] = useState(false); const [flyoutVisible, setFlyoutVisible] = useState(false); @@ -61,8 +78,20 @@ export const AlertDropdown = () => { icon="bell" key="createLink" onClick={openFlyout} - toolTipContent={!canCreateAlerts ? readOnlyUserTooltipContent : undefined} - toolTipTitle={!canCreateAlerts ? readOnlyUserTooltipTitle : undefined} + toolTipContent={ + !canCreateAlerts + ? readOnly + ? readOnlyUserTooltipContent + : inlineLogViewTooltipContent + : undefined + } + toolTipTitle={ + !canCreateAlerts + ? readOnly + ? readOnlyUserTooltipTitle + : inlineLogViewTooltipTitle + : undefined + } > { /> , ]; - }, [manageRulesLinkProps, canCreateAlerts, openFlyout]); + }, [canCreateAlerts, openFlyout, readOnly, manageRulesLinkProps]); return ( <> @@ -104,3 +133,7 @@ export const AlertDropdown = () => { ); }; + +// Allow for lazy loading +// eslint-disable-next-line import/no-default-export +export default AlertDropdown; diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criteria.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criteria.tsx index f26bab6b65f5..c5af2938f545 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criteria.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criteria.tsx @@ -9,7 +9,10 @@ import React, { useCallback } from 'react'; import { EuiFlexItem, EuiFlexGroup, EuiButtonEmpty, EuiAccordion, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; -import type { ResolvedLogViewField } from '../../../../../common/log_views'; +import type { + PersistedLogViewReference, + ResolvedLogViewField, +} from '../../../../../common/log_views'; import { Criterion } from './criterion'; import { PartialRuleParams, @@ -39,7 +42,7 @@ interface SharedProps { defaultCriterion: PartialCriterionType; errors: Errors['criteria']; ruleParams: PartialRuleParams; - sourceId: string; + logViewReference: PersistedLogViewReference; updateCriteria: (criteria: PartialCriteriaType) => void; } @@ -64,7 +67,7 @@ interface CriteriaWrapperProps { addCriterion: () => void; criteria: PartialCountCriteriaType; errors: CriterionErrors; - sourceId: SharedProps['sourceId']; + logViewReference: SharedProps['logViewReference']; isRatio?: boolean; } @@ -77,7 +80,7 @@ const CriteriaWrapper: React.FC = (props) => { fields, errors, ruleParams, - sourceId, + logViewReference, isRatio = false, } = props; @@ -105,7 +108,7 @@ const CriteriaWrapper: React.FC = (props) => { diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx index e5ea692ce779..01d96cebdad9 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx @@ -21,6 +21,7 @@ import { import { EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { PersistedLogViewReference } from '../../../../../common/log_views'; import { ExecutionTimeRange } from '../../../../types'; import { ChartContainer, @@ -55,7 +56,7 @@ const GROUP_LIMIT = 5; interface Props { ruleParams: PartialRuleParams; chartCriterion: Partial; - sourceId: string; + logViewReference: PersistedLogViewReference; showThreshold: boolean; executionTimeRange?: ExecutionTimeRange; } @@ -63,7 +64,7 @@ interface Props { export const CriterionPreview: React.FC = ({ ruleParams, chartCriterion, - sourceId, + logViewReference, showThreshold, executionTimeRange, }) => { @@ -105,7 +106,7 @@ export const CriterionPreview: React.FC = ({ ? NUM_BUCKETS : NUM_BUCKETS / 4 } // Display less data for groups due to space limitations - sourceId={sourceId} + logViewReference={logViewReference} threshold={ruleParams.count} chartAlertParams={chartAlertParams} showThreshold={showThreshold} @@ -116,7 +117,7 @@ export const CriterionPreview: React.FC = ({ interface ChartProps { buckets: number; - sourceId: string; + logViewReference: PersistedLogViewReference; threshold?: Threshold; chartAlertParams: GetLogAlertsChartPreviewDataAlertParamsSubset; showThreshold: boolean; @@ -125,7 +126,7 @@ interface ChartProps { const CriterionPreviewChart: React.FC = ({ buckets, - sourceId, + logViewReference, threshold, chartAlertParams, showThreshold, @@ -141,7 +142,7 @@ const CriterionPreviewChart: React.FC = ({ hasError, chartPreviewData: series, } = useChartPreviewData({ - sourceId, + logViewReference, ruleParams: chartAlertParams, buckets, executionTimeRange, diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx index 2f6f7a8583ae..d9275a1ff017 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx @@ -27,7 +27,6 @@ import { } from '../../../../../common/alerting/logs/log_threshold/types'; import { decodeOrThrow } from '../../../../../common/runtime_types'; import { ObjectEntries } from '../../../../../common/utility_types'; -import { useSourceId } from '../../../../containers/source_id'; import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; import { LogViewProvider, useLogViewContext } from '../../../../hooks/use_log_view'; import { GroupByExpression } from '../../../common/group_by_expression/group_by_expression'; @@ -54,11 +53,6 @@ const DEFAULT_BASE_EXPRESSION = { const DEFAULT_FIELD = 'log.level'; -const createLogViewReference = (logViewId: string): PersistedLogViewReference => ({ - logViewId, - type: 'log-view-reference', -}); - const createDefaultCriterion = ( availableFields: ResolvedLogViewField[], value: ExpressionCriteria['value'] @@ -100,7 +94,6 @@ export const ExpressionEditor: React.FC< RuleTypeParamsExpressionProps > = (props) => { const isInternal = props.metadata?.isInternal ?? false; - const [logViewId] = useSourceId(); const { services: { logViews }, } = useKibanaContextForPlugin(); // injected during alert registration @@ -112,7 +105,7 @@ export const ExpressionEditor: React.FC< ) : ( - + @@ -163,7 +156,11 @@ export const Editor: React.FC { const { setRuleParams, ruleParams, errors } = props; const [hasSetDefaults, setHasSetDefaults] = useState(false); - const { logViewId, resolvedLogView } = useLogViewContext(); + const { logViewReference, resolvedLogView } = useLogViewContext(); + + if (logViewReference.type !== 'log-view-reference') { + throw new Error('The Log Threshold rule type only supports persisted Log Views'); + } const { criteria: criteriaErrors, @@ -230,8 +227,6 @@ export const Editor: React.FC createLogViewReference(logViewId), [logViewId]); - const defaultCountAlertParams = useMemo( () => createDefaultCountRuleParams(supportedFields, logViewReference), [supportedFields, logViewReference] @@ -279,7 +274,7 @@ export const Editor: React.FC ) : null; diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/hooks/use_chart_preview_data.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/hooks/use_chart_preview_data.tsx index 913962f8703d..82a191673419 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/hooks/use_chart_preview_data.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/hooks/use_chart_preview_data.tsx @@ -8,6 +8,7 @@ import { useState, useMemo } from 'react'; import { HttpHandler } from '@kbn/core/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { PersistedLogViewReference } from '../../../../../../common/log_views'; import { ExecutionTimeRange } from '../../../../../types'; import { useTrackedPromise } from '../../../../../utils/use_tracked_promise'; import { @@ -20,14 +21,14 @@ import { decodeOrThrow } from '../../../../../../common/runtime_types'; import { GetLogAlertsChartPreviewDataAlertParamsSubset } from '../../../../../../common/http_api/log_alerts'; interface Options { - sourceId: string; + logViewReference: PersistedLogViewReference; ruleParams: GetLogAlertsChartPreviewDataAlertParamsSubset; buckets: number; executionTimeRange?: ExecutionTimeRange; } export const useChartPreviewData = ({ - sourceId, + logViewReference, ruleParams, buckets, executionTimeRange, @@ -43,7 +44,7 @@ export const useChartPreviewData = ({ createPromise: async () => { setHasError(false); return await callGetChartPreviewDataAPI( - sourceId, + logViewReference, http!.fetch, ruleParams, buckets, @@ -58,7 +59,7 @@ export const useChartPreviewData = ({ setHasError(true); }, }, - [sourceId, http, ruleParams, buckets] + [logViewReference, http, ruleParams, buckets] ); const isLoading = useMemo( @@ -75,7 +76,7 @@ export const useChartPreviewData = ({ }; export const callGetChartPreviewDataAPI = async ( - sourceId: string, + logViewReference: PersistedLogViewReference, fetch: HttpHandler, alertParams: GetLogAlertsChartPreviewDataAlertParamsSubset, buckets: number, @@ -86,7 +87,7 @@ export const callGetChartPreviewDataAPI = async ( body: JSON.stringify( getLogAlertsChartPreviewDataRequestPayloadRT.encode({ data: { - logView: { type: 'log-view-reference', logViewId: sourceId }, + logView: logViewReference, alertParams, buckets, executionTimeRange, diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/lazy_alert_dropdown.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/lazy_alert_dropdown.tsx new file mode 100644 index 000000000000..902a3af8839e --- /dev/null +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/lazy_alert_dropdown.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +const LazyAlertDropdown = React.lazy(() => import('./alert_dropdown')); + +export const LazyAlertDropdownWrapper = () => ( + }> + + +); diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/index.ts b/x-pack/plugins/infra/public/alerting/log_threshold/index.ts index b6eff8ef3826..588d6008ab63 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/index.ts +++ b/x-pack/plugins/infra/public/alerting/log_threshold/index.ts @@ -6,4 +6,4 @@ */ export * from './log_threshold_rule_type'; -export { AlertDropdown } from './components/alert_dropdown'; +export { LazyAlertDropdownWrapper } from './components/lazy_alert_dropdown'; diff --git a/x-pack/plugins/infra/public/components/log_stream/log_stream.tsx b/x-pack/plugins/infra/public/components/log_stream/log_stream.tsx index 6a3e8bfb6352..5ae7daf0731b 100644 --- a/x-pack/plugins/infra/public/components/log_stream/log_stream.tsx +++ b/x-pack/plugins/infra/public/components/log_stream/log_stream.tsx @@ -13,7 +13,7 @@ import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { noop } from 'lodash'; import { LogEntryCursor } from '../../../common/log_entry'; -import { defaultLogViewsStaticConfig } from '../../../common/log_views'; +import { defaultLogViewsStaticConfig, LogViewReference } from '../../../common/log_views'; import { BuiltEsQuery, useLogStream } from '../../containers/logs/log_stream'; import { useLogView } from '../../hooks/use_log_view'; import { LogViewsClient } from '../../services/log_views'; @@ -63,14 +63,8 @@ type LogColumnDefinition = export interface LogStreamProps extends LogStreamContentProps { height?: string | number; } - -interface LogView { - type: 'log-view-reference'; - logViewId: string; -} - interface LogStreamContentProps { - logView: LogView; + logView: LogViewReference; startTimestamp: number; endTimestamp: number; query?: string | Query | BuiltEsQuery; @@ -120,7 +114,7 @@ Read more at https://github.com/elastic/kibana/blob/main/src/plugins/kibana_reac ); } - const { openLogEntryFlyout } = useLogEntryFlyout(logView.logViewId); + const { openLogEntryFlyout } = useLogEntryFlyout(logView); const kibanaQuerySettings = useKibanaQuerySettings(); @@ -135,7 +129,7 @@ Read more at https://github.com/elastic/kibana/blob/main/src/plugins/kibana_reac load: loadLogView, resolvedLogView, } = useLogView({ - logViewId: logView.logViewId, + initialLogViewReference: logView, logViews, }); @@ -166,7 +160,7 @@ Read more at https://github.com/elastic/kibana/blob/main/src/plugins/kibana_reac isLoadingMore, isReloading: isLoadingEntries, } = useLogStream({ - sourceId: logView.logViewId, + logViewReference: logView, startTimestamp, endTimestamp, query: parsedQuery, diff --git a/x-pack/plugins/infra/public/components/logging/inline_log_view_splash_page.tsx b/x-pack/plugins/infra/public/components/logging/inline_log_view_splash_page.tsx new file mode 100644 index 000000000000..cebe7ea21d9c --- /dev/null +++ b/x-pack/plugins/infra/public/components/logging/inline_log_view_splash_page.tsx @@ -0,0 +1,72 @@ +/* + * 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 { EuiButton } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { LazyObservabilityPageTemplateProps } from '@kbn/observability-plugin/public'; +import { EuiEmptyPrompt } from '@elastic/eui'; +import { EuiText } from '@elastic/eui'; +import { PageTemplate } from '../page_template'; + +type InlineLogViewSplashPageProps = { + revertToDefaultLogView: () => void; +} & LazyObservabilityPageTemplateProps; + +export const InlineLogViewSplashPage: React.FC = (props) => { + const { revertToDefaultLogView, ...templateProps } = props; + return ( + + + + ); +}; + +export const InlineLogViewSplashPrompt: React.FC<{ + revertToDefaultLogView: InlineLogViewSplashPageProps['revertToDefaultLogView']; +}> = ({ revertToDefaultLogView }) => { + const title = ( + + ); + + const ctaButton = ( + + + + ); + + const description = ( + + ); + + return ( + {title}} + body={ + +

{description}

+
+ } + actions={ctaButton} + /> + ); +}; diff --git a/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx b/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx index d7b1b45e0242..619d83cf270c 100644 --- a/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx @@ -23,6 +23,7 @@ import { createKibanaReactContext } from '@kbn/kibana-react-plugin/public'; import { OverlayRef } from '@kbn/core/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import { LogViewReference } from '../../../../common/log_views'; import { TimeKey } from '../../../../common/time'; import { useLogEntry } from '../../../containers/logs/log_entry'; import { CenteredEuiFlyoutBody } from '../../centered_flyout_body'; @@ -35,10 +36,10 @@ export interface LogEntryFlyoutProps { logEntryId: string | null | undefined; onCloseFlyout: () => void; onSetFieldFilter?: (filter: Query, logEntryId: string, timeKey?: TimeKey) => void; - sourceId: string | null | undefined; + logViewReference: LogViewReference | null | undefined; } -export const useLogEntryFlyout = (sourceId: string) => { +export const useLogEntryFlyout = (logViewReference: LogViewReference) => { const flyoutRef = useRef(); const { services: { http, data, uiSettings, application }, @@ -63,12 +64,12 @@ export const useLogEntryFlyout = (sourceId: string) => { ); }, - [http, data, uiSettings, application, openFlyout, sourceId, closeLogEntryFlyout] + [http, data, uiSettings, application, openFlyout, logViewReference, closeLogEntryFlyout] ); useEffect(() => { @@ -87,7 +88,7 @@ export const LogEntryFlyout = ({ logEntryId, onCloseFlyout, onSetFieldFilter, - sourceId, + logViewReference, }: LogEntryFlyoutProps) => { const { cancelRequest: cancelLogEntryRequest, @@ -98,15 +99,15 @@ export const LogEntryFlyout = ({ logEntry, total: logEntryRequestTotal, } = useLogEntry({ - sourceId, + logViewReference, logEntryId, }); useEffect(() => { - if (sourceId && logEntryId) { + if (logViewReference && logEntryId) { fetchLogEntry(); } - }, [fetchLogEntry, sourceId, logEntryId]); + }, [fetchLogEntry, logViewReference, logEntryId]); return ( diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_cleanup.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_cleanup.ts index 17377e1484e2..2912cdf74a36 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_cleanup.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_cleanup.ts @@ -13,7 +13,7 @@ import { decodeOrThrow } from '../../../../../common/runtime_types'; interface DeleteJobsRequestArgs { spaceId: string; - sourceId: string; + logViewId: string; jobTypes: JobType[]; } @@ -21,14 +21,14 @@ export const callDeleteJobs = async ( requestArgs: DeleteJobsRequestArgs, fetch: HttpHandler ) => { - const { spaceId, sourceId, jobTypes } = requestArgs; + const { spaceId, logViewId, jobTypes } = requestArgs; // NOTE: Deleting the jobs via this API will delete the datafeeds at the same time const deleteJobsResponse = await fetch('/api/ml/jobs/delete_jobs', { method: 'POST', body: JSON.stringify( deleteJobsRequestPayloadRT.encode({ - jobIds: jobTypes.map((jobType) => getJobId(spaceId, sourceId, jobType)), + jobIds: jobTypes.map((jobType) => getJobId(spaceId, logViewId, jobType)), }) ), }); @@ -44,7 +44,7 @@ export const callGetJobDeletionTasks = async (fetch: HttpHandler) => { interface StopDatafeedsRequestArgs { spaceId: string; - sourceId: string; + logViewId: string; jobTypes: JobType[]; } @@ -52,14 +52,14 @@ export const callStopDatafeeds = async ( requestArgs: StopDatafeedsRequestArgs, fetch: HttpHandler ) => { - const { spaceId, sourceId, jobTypes } = requestArgs; + const { spaceId, logViewId, jobTypes } = requestArgs; // Stop datafeed due to https://github.com/elastic/kibana/issues/44652 const stopDatafeedResponse = await fetch('/api/ml/jobs/stop_datafeeds', { method: 'POST', body: JSON.stringify( stopDatafeedsRequestPayloadRT.encode({ - datafeedIds: jobTypes.map((jobType) => getDatafeedId(spaceId, sourceId, jobType)), + datafeedIds: jobTypes.map((jobType) => getDatafeedId(spaceId, logViewId, jobType)), }) ), }); diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts index 142304269687..b737427b2391 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts @@ -13,7 +13,7 @@ import { decodeOrThrow } from '../../../../../common/runtime_types'; interface RequestArgs { spaceId: string; - sourceId: string; + logViewId: string; jobTypes: JobType[]; } @@ -21,12 +21,12 @@ export const callJobsSummaryAPI = async ( requestArgs: RequestArgs, fetch: HttpHandler ) => { - const { spaceId, sourceId, jobTypes } = requestArgs; + const { spaceId, logViewId, jobTypes } = requestArgs; const response = await fetch('/api/ml/jobs/jobs_summary', { method: 'POST', body: JSON.stringify( fetchJobStatusRequestPayloadRT.encode({ - jobIds: jobTypes.map((jobType) => getJobId(spaceId, sourceId, jobType)), + jobIds: jobTypes.map((jobType) => getJobId(spaceId, logViewId, jobType)), }) ), }); diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_cleanup.tsx b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_cleanup.tsx index 7269a530409f..2a5493f153ff 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_cleanup.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_cleanup.tsx @@ -11,12 +11,12 @@ import { callDeleteJobs, callGetJobDeletionTasks, callStopDatafeeds } from './ap export const cleanUpJobsAndDatafeeds = async ( spaceId: string, - sourceId: string, + logViewId: string, jobTypes: JobType[], fetch: HttpHandler ) => { try { - await callStopDatafeeds({ spaceId, sourceId, jobTypes }, fetch); + await callStopDatafeeds({ spaceId, logViewId, jobTypes }, fetch); } catch (err) { // Proceed only if datafeed has been deleted or didn't exist in the first place if (err?.response?.status !== 404) { @@ -24,27 +24,27 @@ export const cleanUpJobsAndDatafeeds = async ( } } - return await deleteJobs(spaceId, sourceId, jobTypes, fetch); + return await deleteJobs(spaceId, logViewId, jobTypes, fetch); }; const deleteJobs = async ( spaceId: string, - sourceId: string, + logViewId: string, jobTypes: JobType[], fetch: HttpHandler ) => { - const deleteJobsResponse = await callDeleteJobs({ spaceId, sourceId, jobTypes }, fetch); - await waitUntilJobsAreDeleted(spaceId, sourceId, jobTypes, fetch); + const deleteJobsResponse = await callDeleteJobs({ spaceId, logViewId, jobTypes }, fetch); + await waitUntilJobsAreDeleted(spaceId, logViewId, jobTypes, fetch); return deleteJobsResponse; }; const waitUntilJobsAreDeleted = async ( spaceId: string, - sourceId: string, + logViewId: string, jobTypes: JobType[], fetch: HttpHandler ) => { - const moduleJobIds = jobTypes.map((jobType) => getJobId(spaceId, sourceId, jobType)); + const moduleJobIds = jobTypes.map((jobType) => getJobId(spaceId, logViewId, jobType)); while (true) { const { jobIds: jobIdsBeingDeleted } = await callGetJobDeletionTasks(fetch); const needToWait = jobIdsBeingDeleted.some((jobId) => moduleJobIds.includes(jobId)); diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module.tsx b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module.tsx index 29e5508b24d3..3695c6952cd1 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module.tsx @@ -21,7 +21,7 @@ export const useLogAnalysisModule = ({ moduleDescriptor: ModuleDescriptor; }) => { const { services } = useKibanaContextForPlugin(); - const { spaceId, sourceId, timestampField, runtimeMappings } = sourceConfiguration; + const { spaceId, sourceId: logViewId, timestampField, runtimeMappings } = sourceConfiguration; const [moduleStatus, dispatchModuleStatus] = useModuleStatus(moduleDescriptor.jobTypes); const trackMetric = useUiTracker({ app: 'infra_logs' }); @@ -31,21 +31,21 @@ export const useLogAnalysisModule = ({ cancelPreviousOn: 'resolution', createPromise: async () => { dispatchModuleStatus({ type: 'fetchingJobStatuses' }); - return await moduleDescriptor.getJobSummary(spaceId, sourceId, services.http.fetch); + return await moduleDescriptor.getJobSummary(spaceId, logViewId, services.http.fetch); }, onResolve: (jobResponse) => { dispatchModuleStatus({ type: 'fetchedJobStatuses', payload: jobResponse, spaceId, - sourceId, + logViewId, }); }, onReject: () => { dispatchModuleStatus({ type: 'failedFetchingJobStatuses' }); }, }, - [spaceId, sourceId] + [spaceId, logViewId] ); const [, setUpModule] = useTrackedPromise( @@ -64,7 +64,7 @@ export const useLogAnalysisModule = ({ datasetFilter, { indices: selectedIndices, - sourceId, + sourceId: logViewId, spaceId, timestampField, runtimeMappings, @@ -73,7 +73,7 @@ export const useLogAnalysisModule = ({ ); const jobSummaries = await moduleDescriptor.getJobSummary( spaceId, - sourceId, + logViewId, services.http.fetch ); return { setupResult, jobSummaries }; @@ -104,7 +104,7 @@ export const useLogAnalysisModule = ({ jobSetupResults: jobs, jobSummaries, spaceId, - sourceId, + logViewId, }); }, onReject: (e: any) => { @@ -114,17 +114,17 @@ export const useLogAnalysisModule = ({ } }, }, - [moduleDescriptor.setUpModule, spaceId, sourceId, timestampField] + [moduleDescriptor.setUpModule, spaceId, logViewId, timestampField] ); const [cleanUpModuleRequest, cleanUpModule] = useTrackedPromise( { cancelPreviousOn: 'resolution', createPromise: async () => { - return await moduleDescriptor.cleanUpModule(spaceId, sourceId, services.http.fetch); + return await moduleDescriptor.cleanUpModule(spaceId, logViewId, services.http.fetch); }, }, - [spaceId, sourceId] + [spaceId, logViewId] ); const isCleaningUp = useMemo( @@ -156,8 +156,8 @@ export const useLogAnalysisModule = ({ }, [dispatchModuleStatus]); const jobIds = useMemo( - () => moduleDescriptor.getJobIds(spaceId, sourceId), - [moduleDescriptor, spaceId, sourceId] + () => moduleDescriptor.getJobIds(spaceId, logViewId), + [moduleDescriptor, spaceId, logViewId] ); return { diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_status.tsx b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_status.tsx index 5c4623075dec..90f36b40d14d 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_status.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_status.tsx @@ -29,7 +29,7 @@ type StatusReducerAction = | { type: 'startedSetup' } | { type: 'finishedSetup'; - sourceId: string; + logViewId: string; spaceId: string; jobSetupResults: SetupMlModuleResponsePayload['jobs']; jobSummaries: FetchJobStatusResponsePayload; @@ -40,7 +40,7 @@ type StatusReducerAction = | { type: 'fetchedJobStatuses'; spaceId: string; - sourceId: string; + logViewId: string; payload: FetchJobStatusResponsePayload; } | { type: 'failedFetchingJobStatuses' } @@ -84,13 +84,13 @@ const createStatusReducer = }; } case 'finishedSetup': { - const { datafeedSetupResults, jobSetupResults, jobSummaries, spaceId, sourceId } = action; + const { datafeedSetupResults, jobSetupResults, jobSummaries, spaceId, logViewId } = action; const nextJobStatus = jobTypes.reduce( (accumulatedJobStatus, jobType) => ({ ...accumulatedJobStatus, [jobType]: - hasSuccessfullyCreatedJob(getJobId(spaceId, sourceId, jobType))(jobSetupResults) && - hasSuccessfullyStartedDatafeed(getDatafeedId(spaceId, sourceId, jobType))( + hasSuccessfullyCreatedJob(getJobId(spaceId, logViewId, jobType))(jobSetupResults) && + hasSuccessfullyStartedDatafeed(getDatafeedId(spaceId, logViewId, jobType))( datafeedSetupResults ) ? 'started' @@ -142,13 +142,13 @@ const createStatusReducer = }; } case 'fetchedJobStatuses': { - const { payload: jobSummaries, spaceId, sourceId } = action; + const { payload: jobSummaries, spaceId, logViewId } = action; const { setupStatus } = state; const nextJobStatus = jobTypes.reduce( (accumulatedJobStatus, jobType) => ({ ...accumulatedJobStatus, - [jobType]: getJobStatus(getJobId(spaceId, sourceId, jobType))(jobSummaries), + [jobType]: getJobStatus(getJobId(spaceId, logViewId, jobType))(jobSummaries), }), {} as Record ); diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_types.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_types.ts index b26916a22f7b..d82b8f66c8b7 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_types.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_types.ts @@ -25,10 +25,10 @@ export interface ModuleDescriptor { moduleDescription: string; jobTypes: JobType[]; bucketSpan: number; - getJobIds: (spaceId: string, sourceId: string) => Record; + getJobIds: (spaceId: string, logViewId: string) => Record; getJobSummary: ( spaceId: string, - sourceId: string, + logViewId: string, fetch: HttpHandler ) => Promise; getModuleDefinition: (fetch: HttpHandler) => Promise; @@ -41,7 +41,7 @@ export interface ModuleDescriptor { ) => Promise; cleanUpModule: ( spaceId: string, - sourceId: string, + logViewId: string, fetch: HttpHandler ) => Promise; validateSetupIndices: ( diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/module_descriptor.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/module_descriptor.ts index 02e977dfadd3..38a1c5c90752 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/module_descriptor.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/module_descriptor.ts @@ -36,21 +36,21 @@ const moduleDescription = i18n.translate( } ); -const getJobIds = (spaceId: string, sourceId: string) => +const getJobIds = (spaceId: string, logViewId: string) => logEntryCategoriesJobTypes.reduce( (accumulatedJobIds, jobType) => ({ ...accumulatedJobIds, - [jobType]: getJobId(spaceId, sourceId, jobType), + [jobType]: getJobId(spaceId, logViewId, jobType), }), {} as Record ); -const getJobSummary = async (spaceId: string, sourceId: string, fetch: HttpHandler) => { +const getJobSummary = async (spaceId: string, logViewId: string, fetch: HttpHandler) => { const response = await callJobsSummaryAPI( - { spaceId, sourceId, jobTypes: logEntryCategoriesJobTypes }, + { spaceId, logViewId, jobTypes: logEntryCategoriesJobTypes }, fetch ); - const jobIds = Object.values(getJobIds(spaceId, sourceId)); + const jobIds = Object.values(getJobIds(spaceId, logViewId)); return response.filter((jobSummary) => jobIds.includes(jobSummary.id)); }; @@ -130,8 +130,8 @@ const setUpModule = async ( ); }; -const cleanUpModule = async (spaceId: string, sourceId: string, fetch: HttpHandler) => { - return await cleanUpJobsAndDatafeeds(spaceId, sourceId, logEntryCategoriesJobTypes, fetch); +const cleanUpModule = async (spaceId: string, logViewId: string, fetch: HttpHandler) => { + return await cleanUpJobsAndDatafeeds(spaceId, logViewId, logEntryCategoriesJobTypes, fetch); }; const validateSetupIndices = async ( diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/use_log_entry_categories_module.tsx b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/use_log_entry_categories_module.tsx index 433a24cffd93..3f4d80294097 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/use_log_entry_categories_module.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/use_log_entry_categories_module.tsx @@ -17,13 +17,13 @@ import { useLogEntryCategoriesQuality } from './use_log_entry_categories_quality export const useLogEntryCategoriesModule = ({ indexPattern, - sourceId, + logViewId, spaceId, timestampField, runtimeMappings, }: { indexPattern: string; - sourceId: string; + logViewId: string; spaceId: string; timestampField: string; runtimeMappings: estypes.MappingRuntimeFields; @@ -31,12 +31,12 @@ export const useLogEntryCategoriesModule = ({ const sourceConfiguration: ModuleSourceConfiguration = useMemo( () => ({ indices: indexPattern.split(','), - sourceId, + sourceId: logViewId, spaceId, timestampField, runtimeMappings, }), - [indexPattern, sourceId, spaceId, timestampField, runtimeMappings] + [indexPattern, logViewId, spaceId, timestampField, runtimeMappings] ); const logAnalysisModule = useLogAnalysisModule({ diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_rate/module_descriptor.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_rate/module_descriptor.ts index 8acf7ce9d9a5..8cb0bb8a2179 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_rate/module_descriptor.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_rate/module_descriptor.ts @@ -35,21 +35,21 @@ const moduleDescription = i18n.translate( } ); -const getJobIds = (spaceId: string, sourceId: string) => +const getJobIds = (spaceId: string, logViewId: string) => logEntryRateJobTypes.reduce( (accumulatedJobIds, jobType) => ({ ...accumulatedJobIds, - [jobType]: getJobId(spaceId, sourceId, jobType), + [jobType]: getJobId(spaceId, logViewId, jobType), }), {} as Record ); -const getJobSummary = async (spaceId: string, sourceId: string, fetch: HttpHandler) => { +const getJobSummary = async (spaceId: string, logViewId: string, fetch: HttpHandler) => { const response = await callJobsSummaryAPI( - { spaceId, sourceId, jobTypes: logEntryRateJobTypes }, + { spaceId, logViewId, jobTypes: logEntryRateJobTypes }, fetch ); - const jobIds = Object.values(getJobIds(spaceId, sourceId)); + const jobIds = Object.values(getJobIds(spaceId, logViewId)); return response.filter((jobSummary) => jobIds.includes(jobSummary.id)); }; @@ -122,8 +122,8 @@ const setUpModule = async ( ); }; -const cleanUpModule = async (spaceId: string, sourceId: string, fetch: HttpHandler) => { - return await cleanUpJobsAndDatafeeds(spaceId, sourceId, logEntryRateJobTypes, fetch); +const cleanUpModule = async (spaceId: string, logViewId: string, fetch: HttpHandler) => { + return await cleanUpJobsAndDatafeeds(spaceId, logViewId, logEntryRateJobTypes, fetch); }; const validateSetupIndices = async ( diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_rate/use_log_entry_rate_module.tsx b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_rate/use_log_entry_rate_module.tsx index 46587969ca2a..65bddee00ce3 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_rate/use_log_entry_rate_module.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_rate/use_log_entry_rate_module.tsx @@ -16,13 +16,13 @@ import { logEntryRateModule } from './module_descriptor'; export const useLogEntryRateModule = ({ indexPattern, - sourceId, + logViewId, spaceId, timestampField, runtimeMappings, }: { indexPattern: string; - sourceId: string; + logViewId: string; spaceId: string; timestampField: string; runtimeMappings: estypes.MappingRuntimeFields; @@ -30,12 +30,12 @@ export const useLogEntryRateModule = ({ const sourceConfiguration: ModuleSourceConfiguration = useMemo( () => ({ indices: indexPattern.split(','), - sourceId, + sourceId: logViewId, spaceId, timestampField, runtimeMappings, }), - [indexPattern, sourceId, spaceId, timestampField, runtimeMappings] + [indexPattern, logViewId, spaceId, timestampField, runtimeMappings] ); const logAnalysisModule = useLogAnalysisModule({ diff --git a/x-pack/plugins/infra/public/containers/logs/log_entry.ts b/x-pack/plugins/infra/public/containers/logs/log_entry.ts index 958097fc3baa..51b98329ddd5 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_entry.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_entry.ts @@ -6,6 +6,7 @@ */ import { useCallback } from 'react'; +import { LogViewReference } from '../../../common/log_views'; import { decodeOrThrow } from '../../../common/runtime_types'; import { logEntrySearchRequestParamsRT, @@ -19,26 +20,26 @@ import { } from '../../utils/data_search'; export const useLogEntry = ({ - sourceId, + logViewReference, logEntryId, }: { - sourceId: string | null | undefined; + logViewReference: LogViewReference | null | undefined; logEntryId: string | null | undefined; }) => { const { search: fetchLogEntry, requests$: logEntrySearchRequests$ } = useDataSearch({ getRequest: useCallback(() => { - return !!logEntryId && !!sourceId + return !!logEntryId && !!logViewReference ? { request: { params: logEntrySearchRequestParamsRT.encode({ - logView: { type: 'log-view-reference', logViewId: sourceId }, + logView: logViewReference, logEntryId, }), }, options: { strategy: LOG_ENTRY_SEARCH_STRATEGY }, } : null; - }, [sourceId, logEntryId]), + }, [logViewReference, logEntryId]), parseResponses: parseLogEntrySearchResponses, }); diff --git a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx index d9f7ab078ae0..ea0e1fa326c7 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx @@ -6,6 +6,7 @@ */ import { useEffect, useMemo, useState } from 'react'; +import { LogViewReference } from '../../../../common/log_views'; import { LogEntriesHighlightsResponse } from '../../../../common/http_api'; import { LogEntry } from '../../../../common/log_entry'; import { TimeKey } from '../../../../common/time'; @@ -14,7 +15,7 @@ import { useTrackedPromise } from '../../../utils/use_tracked_promise'; import { fetchLogEntriesHighlights } from './api/fetch_log_entries_highlights'; export const useLogEntryHighlights = ( - sourceId: string, + logViewReference: LogViewReference, sourceVersion: string | undefined, startTimestamp: number | null, endTimestamp: number | null, @@ -37,7 +38,7 @@ export const useLogEntryHighlights = ( return await fetchLogEntriesHighlights( { - logView: { type: 'log-view-reference', logViewId: sourceId }, + logView: logViewReference, startTimestamp, endTimestamp, center: centerPoint, @@ -52,7 +53,7 @@ export const useLogEntryHighlights = ( setLogEntryHighlights(response.data); }, }, - [sourceId, startTimestamp, endTimestamp, centerPoint, size, filterQuery, highlightTerms] + [logViewReference, startTimestamp, endTimestamp, centerPoint, size, filterQuery, highlightTerms] ); useEffect(() => { diff --git a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx index 9ca6858c3450..0a6710731bcb 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx @@ -8,6 +8,7 @@ import createContainer from 'constate'; import { useState } from 'react'; import useThrottle from 'react-use/lib/useThrottle'; +import { LogViewReference } from '../../../../common/log_views'; import { useLogEntryHighlights } from './log_entry_highlights'; import { useLogSummaryHighlights } from './log_summary_highlights'; import { useNextAndPrevious } from './next_and_previous'; @@ -17,7 +18,7 @@ import { TimeKey } from '../../../../common/time'; const FETCH_THROTTLE_INTERVAL = 3000; interface UseLogHighlightsStateProps { - sourceId: string; + logViewReference: LogViewReference; sourceVersion: string | undefined; centerCursor: TimeKey | null; size: number; @@ -25,7 +26,7 @@ interface UseLogHighlightsStateProps { } export const useLogHighlightsState = ({ - sourceId, + logViewReference, sourceVersion, centerCursor, size, @@ -40,7 +41,7 @@ export const useLogHighlightsState = ({ const { logEntryHighlights, logEntryHighlightsById, loadLogEntryHighlightsRequest } = useLogEntryHighlights( - sourceId, + logViewReference, sourceVersion, throttledStartTimestamp, throttledEndTimestamp, @@ -51,7 +52,7 @@ export const useLogHighlightsState = ({ ); const { logSummaryHighlights, loadLogSummaryHighlightsRequest } = useLogSummaryHighlights( - sourceId, + logViewReference, sourceVersion, throttledStartTimestamp, throttledEndTimestamp, diff --git a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts index e994e2a013bb..8c5f7fb7ae77 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts @@ -8,6 +8,7 @@ import { useEffect, useMemo, useState } from 'react'; import { debounce } from 'lodash'; +import { LogViewReference } from '../../../../common/log_views'; import { useTrackedPromise } from '../../../utils/use_tracked_promise'; import { fetchLogSummaryHighlights } from './api/fetch_log_summary_highlights'; import { LogEntriesSummaryHighlightsResponse } from '../../../../common/http_api'; @@ -15,7 +16,7 @@ import { useBucketSize } from '../log_summary/bucket_size'; import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; export const useLogSummaryHighlights = ( - sourceId: string, + logViewReference: LogViewReference, sourceVersion: string | undefined, startTimestamp: number | null, endTimestamp: number | null, @@ -39,7 +40,7 @@ export const useLogSummaryHighlights = ( return await fetchLogSummaryHighlights( { - logView: { type: 'log-view-reference', logViewId: sourceId }, + logView: logViewReference, startTimestamp, endTimestamp, bucketSize, @@ -53,7 +54,7 @@ export const useLogSummaryHighlights = ( setLogSummaryHighlights(response.data); }, }, - [sourceId, startTimestamp, endTimestamp, bucketSize, filterQuery, highlightTerms] + [logViewReference, startTimestamp, endTimestamp, bucketSize, filterQuery, highlightTerms] ); const debouncedLoadSummaryHighlights = useMemo( diff --git a/x-pack/plugins/infra/public/containers/logs/log_stream/index.ts b/x-pack/plugins/infra/public/containers/logs/log_stream/index.ts index 2a81afa23472..e1b425b112f8 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_stream/index.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_stream/index.ts @@ -12,7 +12,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import usePrevious from 'react-use/lib/usePrevious'; import useSetState from 'react-use/lib/useSetState'; import { LogEntry, LogEntryCursor } from '../../../../common/log_entry'; -import { LogViewColumnConfiguration } from '../../../../common/log_views'; +import { LogViewColumnConfiguration, LogViewReference } from '../../../../common/log_views'; import { useSubscription } from '../../../utils/use_observable'; import { useFetchLogEntriesAfter } from './use_fetch_log_entries_after'; import { useFetchLogEntriesAround } from './use_fetch_log_entries_around'; @@ -21,7 +21,7 @@ import { useFetchLogEntriesBefore } from './use_fetch_log_entries_before'; export type BuiltEsQuery = ReturnType; interface LogStreamProps { - sourceId: string; + logViewReference: LogViewReference; startTimestamp: number; endTimestamp: number; query?: BuiltEsQuery; @@ -52,7 +52,7 @@ const INITIAL_STATE: LogStreamState = { const LOG_ENTRIES_CHUNK_SIZE = 200; export function useLogStream({ - sourceId, + logViewReference, startTimestamp, endTimestamp, query, @@ -85,13 +85,13 @@ export function useLogStream({ const commonFetchArguments = useMemo( () => ({ - sourceId, + logViewReference, startTimestamp, endTimestamp, query: cachedQuery, columnOverrides: columns, }), - [columns, endTimestamp, cachedQuery, sourceId, startTimestamp] + [columns, endTimestamp, cachedQuery, logViewReference, startTimestamp] ); const { diff --git a/x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_after.ts b/x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_after.ts index 3ee39fbda3d4..168d2e8577b8 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_after.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_after.ts @@ -11,7 +11,7 @@ import { Observable } from 'rxjs'; import { exhaustMap } from 'rxjs/operators'; import { IKibanaSearchRequest } from '@kbn/data-plugin/public'; import { LogEntryAfterCursor } from '../../../../common/log_entry'; -import { LogViewColumnConfiguration } from '../../../../common/log_views'; +import { LogViewColumnConfiguration, LogViewReference } from '../../../../common/log_views'; import { decodeOrThrow } from '../../../../common/runtime_types'; import { logEntriesSearchRequestParamsRT, @@ -34,21 +34,21 @@ export const useLogEntriesAfterRequest = ({ endTimestamp, highlightPhrase, query, - sourceId, + logViewReference, startTimestamp, }: { columnOverrides?: LogViewColumnConfiguration[]; endTimestamp: number; highlightPhrase?: string; query?: LogEntriesSearchRequestQuery; - sourceId: string; + logViewReference: LogViewReference; startTimestamp: number; }) => { const { search: fetchLogEntriesAfter, requests$: logEntriesAfterSearchRequests$ } = useDataSearch( { getRequest: useCallback( (cursor: LogEntryAfterCursor['after'], params: { size: number; extendTo?: number }) => { - return !!sourceId + return !!logViewReference ? { request: { params: logEntriesSearchRequestParamsRT.encode({ @@ -58,7 +58,7 @@ export const useLogEntriesAfterRequest = ({ highlightPhrase, query: query as JsonObject, size: params.size, - logView: { type: 'log-view-reference', logViewId: sourceId }, + logView: logViewReference, startTimestamp, }), }, @@ -66,7 +66,7 @@ export const useLogEntriesAfterRequest = ({ } : null; }, - [columnOverrides, endTimestamp, highlightPhrase, query, sourceId, startTimestamp] + [columnOverrides, endTimestamp, highlightPhrase, query, logViewReference, startTimestamp] ), parseResponses: parseLogEntriesAfterSearchResponses, } @@ -107,14 +107,14 @@ export const useFetchLogEntriesAfter = ({ endTimestamp, highlightPhrase, query, - sourceId, + logViewReference, startTimestamp, }: { columnOverrides?: LogViewColumnConfiguration[]; endTimestamp: number; highlightPhrase?: string; query?: LogEntriesSearchRequestQuery; - sourceId: string; + logViewReference: LogViewReference; startTimestamp: number; }) => { const { fetchLogEntriesAfter, logEntriesAfterSearchRequests$ } = useLogEntriesAfterRequest({ @@ -122,7 +122,7 @@ export const useFetchLogEntriesAfter = ({ endTimestamp, highlightPhrase, query, - sourceId, + logViewReference, startTimestamp, }); diff --git a/x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_around.ts b/x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_around.ts index 748281d5517a..a6bd8ba79428 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_around.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_around.ts @@ -9,7 +9,7 @@ import { useCallback } from 'react'; import { combineLatest, Observable, ReplaySubject } from 'rxjs'; import { last, map, startWith, switchMap } from 'rxjs/operators'; import { LogEntryCursor } from '../../../../common/log_entry'; -import { LogViewColumnConfiguration } from '../../../../common/log_views'; +import { LogViewColumnConfiguration, LogViewReference } from '../../../../common/log_views'; import { LogEntriesSearchRequestQuery } from '../../../../common/search_strategies/log_entries/log_entries'; import { flattenDataSearchResponseDescriptor } from '../../../utils/data_search'; import { useObservable, useObservableState } from '../../../utils/use_observable'; @@ -21,14 +21,14 @@ export const useFetchLogEntriesAround = ({ endTimestamp, highlightPhrase, query, - sourceId, + logViewReference, startTimestamp, }: { columnOverrides?: LogViewColumnConfiguration[]; endTimestamp: number; highlightPhrase?: string; query?: LogEntriesSearchRequestQuery; - sourceId: string; + logViewReference: LogViewReference; startTimestamp: number; }) => { const { fetchLogEntriesBefore } = useLogEntriesBeforeRequest({ @@ -36,7 +36,7 @@ export const useFetchLogEntriesAround = ({ endTimestamp, highlightPhrase, query, - sourceId, + logViewReference, startTimestamp, }); @@ -45,7 +45,7 @@ export const useFetchLogEntriesAround = ({ endTimestamp, highlightPhrase, query, - sourceId, + logViewReference, startTimestamp, }); diff --git a/x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_before.ts b/x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_before.ts index 581d31a28a8c..a745b5f13a19 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_before.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_before.ts @@ -11,7 +11,7 @@ import { Observable } from 'rxjs'; import { exhaustMap } from 'rxjs/operators'; import { IKibanaSearchRequest } from '@kbn/data-plugin/public'; import { LogEntryBeforeCursor } from '../../../../common/log_entry'; -import { LogViewColumnConfiguration } from '../../../../common/log_views'; +import { LogViewColumnConfiguration, LogViewReference } from '../../../../common/log_views'; import { decodeOrThrow } from '../../../../common/runtime_types'; import { logEntriesSearchRequestParamsRT, @@ -34,21 +34,21 @@ export const useLogEntriesBeforeRequest = ({ endTimestamp, highlightPhrase, query, - sourceId, + logViewReference, startTimestamp, }: { columnOverrides?: LogViewColumnConfiguration[]; endTimestamp: number; highlightPhrase?: string; query?: LogEntriesSearchRequestQuery; - sourceId: string; + logViewReference: LogViewReference; startTimestamp: number; }) => { const { search: fetchLogEntriesBefore, requests$: logEntriesBeforeSearchRequests$ } = useDataSearch({ getRequest: useCallback( (cursor: LogEntryBeforeCursor['before'], params: { size: number; extendTo?: number }) => { - return !!sourceId + return !!logViewReference ? { request: { params: logEntriesSearchRequestParamsRT.encode({ @@ -58,7 +58,7 @@ export const useLogEntriesBeforeRequest = ({ highlightPhrase, query: query as JsonObject, size: params.size, - logView: { type: 'log-view-reference', logViewId: sourceId }, + logView: logViewReference, startTimestamp: params.extendTo ?? startTimestamp, }), }, @@ -66,7 +66,7 @@ export const useLogEntriesBeforeRequest = ({ } : null; }, - [columnOverrides, endTimestamp, highlightPhrase, query, sourceId, startTimestamp] + [columnOverrides, endTimestamp, highlightPhrase, query, logViewReference, startTimestamp] ), parseResponses: parseLogEntriesBeforeSearchResponses, }); @@ -106,14 +106,14 @@ export const useFetchLogEntriesBefore = ({ endTimestamp, highlightPhrase, query, - sourceId, + logViewReference, startTimestamp, }: { columnOverrides?: LogViewColumnConfiguration[]; endTimestamp: number; highlightPhrase?: string; query?: LogEntriesSearchRequestQuery; - sourceId: string; + logViewReference: LogViewReference; startTimestamp: number; }) => { const { fetchLogEntriesBefore, logEntriesBeforeSearchRequests$ } = useLogEntriesBeforeRequest({ @@ -121,7 +121,7 @@ export const useFetchLogEntriesBefore = ({ endTimestamp, highlightPhrase, query, - sourceId, + logViewReference, startTimestamp, }); diff --git a/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx b/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx index fe02367e4b1f..183950edf977 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx @@ -14,6 +14,12 @@ import { useLogSummary } from './log_summary'; import { fetchLogSummary } from './api/fetch_log_summary'; import { datemathToEpochMillis } from '../../../utils/datemath'; +const LOG_VIEW_REFERENCE = { type: 'log-view-reference' as const, logViewId: 'LOG_VIEW_ID' }; +const CHANGED_LOG_VIEW_REFERENCE = { + type: 'log-view-reference' as const, + logViewId: 'CHANGED_LOG_VIEW_ID', +}; + // Typescript doesn't know that `fetchLogSummary` is a jest mock. // We use a second variable with a type cast to help the compiler further down the line. jest.mock('./api/fetch_log_summary', () => ({ fetchLogSummary: jest.fn() })); @@ -32,7 +38,7 @@ describe('useLogSummary hook', () => { }); it('provides an empty list of buckets by default', () => { - const { result } = renderHook(() => useLogSummary('SOURCE_ID', null, null, null)); + const { result } = renderHook(() => useLogSummary(LOG_VIEW_REFERENCE, null, null, null)); expect(result.current.buckets).toEqual([]); }); @@ -51,9 +57,9 @@ describe('useLogSummary hook', () => { .mockResolvedValueOnce(secondMockResponse); const { result, waitForNextUpdate, rerender } = renderHook( - ({ sourceId }) => useLogSummary(sourceId, startTimestamp, endTimestamp, null), + ({ logViewReference }) => useLogSummary(logViewReference, startTimestamp, endTimestamp, null), { - initialProps: { sourceId: 'INITIAL_SOURCE_ID' }, + initialProps: { logViewReference: LOG_VIEW_REFERENCE }, } ); @@ -62,19 +68,19 @@ describe('useLogSummary hook', () => { expect(fetchLogSummaryMock).toHaveBeenCalledTimes(1); expect(fetchLogSummaryMock).toHaveBeenLastCalledWith( expect.objectContaining({ - logView: { logViewId: 'INITIAL_SOURCE_ID', type: 'log-view-reference' }, + logView: LOG_VIEW_REFERENCE, }), expect.anything() ); expect(result.current.buckets).toEqual(firstMockResponse.data.buckets); - rerender({ sourceId: 'CHANGED_SOURCE_ID' }); + rerender({ logViewReference: CHANGED_LOG_VIEW_REFERENCE }); await waitForNextUpdate(); expect(fetchLogSummaryMock).toHaveBeenCalledTimes(2); expect(fetchLogSummaryMock).toHaveBeenLastCalledWith( expect.objectContaining({ - logView: { logViewId: 'CHANGED_SOURCE_ID', type: 'log-view-reference' }, + logView: CHANGED_LOG_VIEW_REFERENCE, }), expect.anything() ); @@ -96,7 +102,8 @@ describe('useLogSummary hook', () => { .mockResolvedValueOnce(secondMockResponse); const { result, waitForNextUpdate, rerender } = renderHook( - ({ filterQuery }) => useLogSummary('SOURCE_ID', startTimestamp, endTimestamp, filterQuery), + ({ filterQuery }) => + useLogSummary(LOG_VIEW_REFERENCE, startTimestamp, endTimestamp, filterQuery), { initialProps: { filterQuery: 'INITIAL_FILTER_QUERY' }, } @@ -134,7 +141,7 @@ describe('useLogSummary hook', () => { const firstRange = createMockDateRange(); const { waitForNextUpdate, rerender } = renderHook( ({ startTimestamp, endTimestamp }) => - useLogSummary('SOURCE_ID', startTimestamp, endTimestamp, null), + useLogSummary(LOG_VIEW_REFERENCE, startTimestamp, endTimestamp, null), { initialProps: firstRange, } @@ -171,7 +178,7 @@ describe('useLogSummary hook', () => { const firstRange = createMockDateRange(); const { waitForNextUpdate, rerender } = renderHook( ({ startTimestamp, endTimestamp }) => - useLogSummary('SOURCE_ID', startTimestamp, endTimestamp, null), + useLogSummary(LOG_VIEW_REFERENCE, startTimestamp, endTimestamp, null), { initialProps: firstRange, } diff --git a/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.tsx b/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.tsx index c4b933ab04cd..c2792300c5f3 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.tsx @@ -8,6 +8,7 @@ import { useEffect } from 'react'; import { exhaustMap, map, Observable } from 'rxjs'; import { HttpHandler } from '@kbn/core-http-browser'; +import { LogViewReference } from '../../../../common/log_views'; import { useObservableState, useReplaySubject } from '../../../utils/use_observable'; import { fetchLogSummary } from './api/fetch_log_summary'; import { LogEntriesSummaryRequest, LogEntriesSummaryResponse } from '../../../../common/http_api'; @@ -17,7 +18,7 @@ import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; export type LogSummaryBuckets = LogEntriesSummaryResponse['data']['buckets']; export const useLogSummary = ( - sourceId: string, + logViewReference: LogViewReference, startTimestamp: number | null, endTimestamp: number | null, filterQuery: string | null @@ -35,7 +36,7 @@ export const useLogSummary = ( pushLogSummaryBucketsArgs([ { - logView: { type: 'log-view-reference', logViewId: sourceId }, + logView: logViewReference, startTimestamp, endTimestamp, bucketSize, @@ -49,7 +50,7 @@ export const useLogSummary = ( filterQuery, pushLogSummaryBucketsArgs, services.http.fetch, - sourceId, + logViewReference, startTimestamp, ]); diff --git a/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts b/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts index 102999a19953..1dc5c7021d25 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts @@ -25,7 +25,7 @@ export const WithSummary = ({ end: number | null; }>; }) => { - const { logViewId } = useLogViewContext(); + const { logViewReference } = useLogViewContext(); const serializedParsedQuery = useSelector(useLogStreamPageStateContext(), (logStreamPageState) => logStreamPageState.matches({ hasLogViewIndices: 'initialized' }) ? stringify(logStreamPageState.context.parsedQuery) @@ -38,7 +38,7 @@ export const WithSummary = ({ const throttledEndTimestamp = useThrottle(endTimestamp, FETCH_THROTTLE_INTERVAL); const { buckets, start, end } = useLogSummary( - logViewId, + logViewReference, throttledStartTimestamp, throttledEndTimestamp, serializedParsedQuery diff --git a/x-pack/plugins/infra/public/containers/logs/view_log_in_context/view_log_in_context.ts b/x-pack/plugins/infra/public/containers/logs/view_log_in_context/view_log_in_context.ts index b22a73d77cc5..1ea8f71da129 100644 --- a/x-pack/plugins/infra/public/containers/logs/view_log_in_context/view_log_in_context.ts +++ b/x-pack/plugins/infra/public/containers/logs/view_log_in_context/view_log_in_context.ts @@ -7,10 +7,11 @@ import { useState } from 'react'; import createContainer from 'constate'; +import { LogViewReference } from '../../../../common/log_views'; import { LogEntry } from '../../../../common/log_entry'; interface ViewLogInContextProps { - sourceId: string; + logViewReference: LogViewReference; startTimestamp: number; endTimestamp: number; } @@ -27,13 +28,13 @@ export const useViewLogInContext = ( props: ViewLogInContextProps ): [ViewLogInContextState, ViewLogInContextCallbacks] => { const [contextEntry, setContextEntry] = useState(); - const { startTimestamp, endTimestamp, sourceId } = props; + const { startTimestamp, endTimestamp, logViewReference } = props; return [ { startTimestamp, endTimestamp, - sourceId, + logViewReference, contextEntry, }, { diff --git a/x-pack/plugins/infra/public/containers/source_id/source_id.ts b/x-pack/plugins/infra/public/containers/source_id/source_id.ts deleted file mode 100644 index f7c7c11b87fe..000000000000 --- a/x-pack/plugins/infra/public/containers/source_id/source_id.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import * as runtimeTypes from 'io-ts'; - -import { pipe } from 'fp-ts/lib/pipeable'; -import { fold } from 'fp-ts/lib/Either'; -import { constant, identity } from 'fp-ts/lib/function'; -import { useUrlState, replaceStateKeyInQueryString } from '../../utils/use_url_state'; - -const SOURCE_ID_URL_STATE_KEY = 'sourceId'; - -export const useSourceId = () => { - return useUrlState({ - defaultState: 'default', - decodeUrlState: decodeSourceIdUrlState, - encodeUrlState: encodeSourceIdUrlState, - urlStateKey: SOURCE_ID_URL_STATE_KEY, - }); -}; - -export const replaceSourceIdInQueryString = (sourceId: string) => - replaceStateKeyInQueryString(SOURCE_ID_URL_STATE_KEY, sourceId); - -const sourceIdRuntimeType = runtimeTypes.union([runtimeTypes.string, runtimeTypes.undefined]); -const encodeSourceIdUrlState = sourceIdRuntimeType.encode; -const decodeSourceIdUrlState = (value: unknown) => - pipe(sourceIdRuntimeType.decode(value), fold(constant(undefined), identity)); diff --git a/x-pack/plugins/infra/public/hooks/use_log_view.mock.ts b/x-pack/plugins/infra/public/hooks/use_log_view.mock.ts index cdf702f741c4..3d95dfb72abb 100644 --- a/x-pack/plugins/infra/public/hooks/use_log_view.mock.ts +++ b/x-pack/plugins/infra/public/hooks/use_log_view.mock.ts @@ -36,12 +36,18 @@ export const createUninitializedUseLogViewMock = load: jest.fn(), retry: jest.fn(), logView: undefined, - logViewId, + logViewReference: { type: 'log-view-reference', logViewId }, logViewStatus: undefined, resolvedLogView: undefined, update: jest.fn(), - logViewStateService: interpret(createPureLogViewStateMachine({ logViewId })), + changeLogViewReference: jest.fn(), + revertToDefaultLogView: jest.fn(), + logViewStateService: interpret( + createPureLogViewStateMachine({ logViewReference: { type: 'log-view-reference', logViewId } }) + ), logViewStateNotifications: createLogViewNotificationChannel(), + isPersistedLogView: false, + isInlineLogView: false, }); export const createLoadingUseLogViewMock = diff --git a/x-pack/plugins/infra/public/hooks/use_log_view.ts b/x-pack/plugins/infra/public/hooks/use_log_view.ts index 8afc517dbfca..258fbd76850f 100644 --- a/x-pack/plugins/infra/public/hooks/use_log_view.ts +++ b/x-pack/plugins/infra/public/hooks/use_log_view.ts @@ -7,62 +7,90 @@ import { useInterpret, useSelector } from '@xstate/react'; import createContainer from 'constate'; -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useState } from 'react'; import { waitFor } from 'xstate/lib/waitFor'; -import { LogViewAttributes } from '../../common/log_views'; +import { LogViewAttributes, LogViewReference } from '../../common/log_views'; import { createLogViewNotificationChannel, createLogViewStateMachine, + DEFAULT_LOG_VIEW, } from '../observability_logs/log_view_state'; import type { ILogViewsClient } from '../services/log_views'; import { isDevMode } from '../utils/dev_mode'; +import { useKbnUrlStateStorageFromRouterContext } from '../utils/kbn_url_state_context'; +import { useKibanaContextForPlugin } from './use_kibana'; export const useLogView = ({ - logViewId, + initialLogViewReference, logViews, useDevTools = isDevMode(), }: { - logViewId: string; + initialLogViewReference?: LogViewReference; logViews: ILogViewsClient; useDevTools?: boolean; }) => { + const { + services: { + notifications: { toasts: toastsService }, + }, + } = useKibanaContextForPlugin(); + + const urlStateStorage = useKbnUrlStateStorageFromRouterContext(); + const [logViewStateNotifications] = useState(() => createLogViewNotificationChannel()); const logViewStateService = useInterpret( () => createLogViewStateMachine({ initialContext: { - logViewId, + logViewReference: initialLogViewReference ?? DEFAULT_LOG_VIEW, }, logViews, notificationChannel: logViewStateNotifications, + toastsService, + urlStateStorage, }), { devTools: useDevTools, } ); - useEffect(() => { - logViewStateService.send({ - type: 'LOG_VIEW_ID_CHANGED', - logViewId, - }); - }, [logViewId, logViewStateService]); + const changeLogViewReference = useCallback( + (logViewReference: LogViewReference) => { + logViewStateService.send({ + type: 'LOG_VIEW_REFERENCE_CHANGED', + logViewReference, + }); + }, + [logViewStateService] + ); + + const logViewReference = useSelector( + logViewStateService, + (state) => state.context.logViewReference + ); const logView = useSelector(logViewStateService, (state) => - state.matches('resolving') || state.matches('checkingStatus') || state.matches('resolved') + state.matches('resolving') || + state.matches('checkingStatus') || + state.matches('resolvedPersistedLogView') || + state.matches('resolvedInlineLogView') ? state.context.logView : undefined ); const resolvedLogView = useSelector(logViewStateService, (state) => - state.matches('checkingStatus') || state.matches('resolved') + state.matches('checkingStatus') || + state.matches('resolvedPersistedLogView') || + state.matches('resolvedInlineLogView') ? state.context.resolvedLogView : undefined ); const logViewStatus = useSelector(logViewStateService, (state) => - state.matches('resolved') ? state.context.status : undefined + state.matches('resolvedPersistedLogView') || state.matches('resolvedInlineLogView') + ? state.context.status + : undefined ); const isLoadingLogView = useSelector(logViewStateService, (state) => state.matches('loading')); @@ -91,6 +119,13 @@ export const useLogView = ({ state.matches('checkingStatusFailed') ); + const isPersistedLogView = useSelector( + logViewStateService, + (state) => state.context.logViewReference.type === 'log-view-reference' + ); + + const isInlineLogView = !isPersistedLogView; + const latestLoadLogViewFailures = useSelector(logViewStateService, (state) => state.matches('loadingFailed') || state.matches('resolutionFailed') || @@ -118,7 +153,10 @@ export const useLogView = ({ const doneState = await waitFor( logViewStateService, - (state) => state.matches('updatingFailed') || state.matches('resolved') + (state) => + state.matches('updatingFailed') || + state.matches('resolvedPersistedLogView') || + state.matches('resolvedInlineLogView') ); if (doneState.matches('updatingFailed')) { @@ -128,8 +166,12 @@ export const useLogView = ({ [logViewStateService] ); + const revertToDefaultLogView = useCallback(() => { + changeLogViewReference(DEFAULT_LOG_VIEW); + }, [changeLogViewReference]); + return { - // underlying state machine + // Underlying state machine logViewStateService, logViewStateNotifications, @@ -147,17 +189,21 @@ export const useLogView = ({ isLoadingLogViewStatus, isResolvingLogView, - // data - logViewId, + // Data + logViewReference, logView, resolvedLogView, logViewStatus, derivedDataView: resolvedLogView?.dataViewReference, + isInlineLogView, + isPersistedLogView, - // actions + // Actions load: retry, retry, update, + changeLogViewReference, + revertToDefaultLogView, }; }; diff --git a/x-pack/plugins/infra/public/containers/source_id/index.ts b/x-pack/plugins/infra/public/observability_logs/log_view_state/src/defaults.ts similarity index 63% rename from x-pack/plugins/infra/public/containers/source_id/index.ts rename to x-pack/plugins/infra/public/observability_logs/log_view_state/src/defaults.ts index 158169c82f89..eb31f91ed12f 100644 --- a/x-pack/plugins/infra/public/containers/source_id/index.ts +++ b/x-pack/plugins/infra/public/observability_logs/log_view_state/src/defaults.ts @@ -5,4 +5,8 @@ * 2.0. */ -export * from './source_id'; +const DEFAULT_LOG_VIEW_ID = 'default'; +export const DEFAULT_LOG_VIEW = { + type: 'log-view-reference' as const, + logViewId: DEFAULT_LOG_VIEW_ID, +}; diff --git a/x-pack/plugins/infra/public/observability_logs/log_view_state/src/index.ts b/x-pack/plugins/infra/public/observability_logs/log_view_state/src/index.ts index 82e7b9fbd279..5dcfedc6faa4 100644 --- a/x-pack/plugins/infra/public/observability_logs/log_view_state/src/index.ts +++ b/x-pack/plugins/infra/public/observability_logs/log_view_state/src/index.ts @@ -8,3 +8,5 @@ export { createLogViewNotificationChannel, type LogViewNotificationEvent } from './notifications'; export * from './state_machine'; export * from './types'; +export * from './defaults'; +export * from './url_state_storage_service'; diff --git a/x-pack/plugins/infra/public/observability_logs/log_view_state/src/notifications.ts b/x-pack/plugins/infra/public/observability_logs/log_view_state/src/notifications.ts index 4b5733452c03..105f911f6c78 100644 --- a/x-pack/plugins/infra/public/observability_logs/log_view_state/src/notifications.ts +++ b/x-pack/plugins/infra/public/observability_logs/log_view_state/src/notifications.ts @@ -5,14 +5,14 @@ * 2.0. */ -import { LogViewStatus, ResolvedLogView } from '../../../../common/log_views'; +import { LogViewReference, LogViewStatus, ResolvedLogView } from '../../../../common/log_views'; import { createNotificationChannel } from '../../xstate_helpers'; import { LogViewContext, LogViewEvent } from './types'; export type LogViewNotificationEvent = | { type: 'LOADING_LOG_VIEW_STARTED'; - logViewId: string; + logViewReference: LogViewReference; } | { type: 'LOADING_LOG_VIEW_SUCCEEDED'; @@ -29,10 +29,10 @@ export const createLogViewNotificationChannel = () => export const logViewNotificationEventSelectors = { loadingLogViewStarted: (context: LogViewContext) => - 'logViewId' in context + 'logViewReference' in context ? ({ type: 'LOADING_LOG_VIEW_STARTED', - logViewId: context.logViewId, + logViewReference: context.logViewReference, } as LogViewNotificationEvent) : undefined, loadingLogViewSucceeded: (context: LogViewContext) => diff --git a/x-pack/plugins/infra/public/observability_logs/log_view_state/src/state_machine.ts b/x-pack/plugins/infra/public/observability_logs/log_view_state/src/state_machine.ts index aa94ebcce6a0..2074119bb329 100644 --- a/x-pack/plugins/infra/public/observability_logs/log_view_state/src/state_machine.ts +++ b/x-pack/plugins/infra/public/observability_logs/log_view_state/src/state_machine.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { IToasts } from '@kbn/core/public'; +import { IKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; import { catchError, from, map, of, throwError } from 'rxjs'; import { createMachine, actions, assign } from 'xstate'; import { ILogViewsClient } from '../../../services/log_views'; @@ -13,15 +15,20 @@ import { LogViewNotificationEvent, logViewNotificationEventSelectors } from './n import { LogViewContext, LogViewContextWithError, - LogViewContextWithId, LogViewContextWithLogView, + LogViewContextWithReference, LogViewContextWithResolvedLogView, LogViewContextWithStatus, LogViewEvent, LogViewTypestate, } from './types'; +import { + initializeFromUrl, + updateContextInUrl, + listenForUrlChanges, +} from './url_state_storage_service'; -export const createPureLogViewStateMachine = (initialContext: LogViewContextWithId) => +export const createPureLogViewStateMachine = (initialContext: LogViewContextWithReference) => /** @xstate-layout N4IgpgJg5mDOIC5QBkD2UBqBLMB3AxMgPIDiA+hgJICiA6mZQCJkDCAEgIIByJ1jA2gAYAuolAAHVLCwAXLKgB2YkAA9EAFkEBmAHQAmAGwB2dQYAcAVkGmAjGYMAaEAE9EAWi3qjO9WcGCLIzMbLWMLAE4AX0inNEwcAgBVAAVGDgAVaiFRJBBJaTlFZTUEG20DfUEDLUtwg3CbdXUtJ1cEDz11HSMLTzMagwtjAOjY9Gw8HQBXBSxZuQBDABssAC9IfGzlfNl5JVySz3CdGwMwrRsbCK0a1sQyiqNDCMEjcK09QT8LUZA4idwOiWqAWEDmUEIRA4jEoPDIAGVEiwWNQ+HwtrkdoV9qASkZPDoLnpwmZniTgmY7ghSTorP5fFo6lorHobL9-gkgSCwQoIcRobDyAAxDiUZDokTbKS7IoHRBWMzdT4vCxDPQ3PRUvTqnThcL+OoWVmdU7qdnjTkAJzgqCWADdwfgAErUeFEZCJdKUIhcMgisUSnISaXY4qIcm0y6XJ5mdR6Iw2IxanV6g2DY3qRPm+KTa2wW0O3nO13uz3e32I5GoxiBqUFPZh0ra7z9S6CBo9G4tFyIGl06z9JlWOzZgE6ADGAAswOOANbg+EyBYyKawfDsagsADSgoR6QyiXhftF4oEksxIYbctKFhCOksxjsF3CQSTPYQ2t0qfb6ZsJqMo6clOM7zryi7Lqu65sJuO5wvC+7pIeCJIiiaJnkGeSXrKuL3K+3RnJ8eqGPGgRUm8PjEvq5jBKE6oATEfwWrmNr2hsLr8swxDkFQdAYsG9bYao9x6BYXSkjcBrqp8RiOO+Hg6NUAzhEM6gkvUNRmgxHKTMCoLgkKCxYEsbHUOkToAJp8ZhAk4kJCCMl0MlPKqoS+O2b5tJ0jynGc+KCHowR1HogHMfmSxTNiBlGSZZmWee-EyrZJT6jYCkfARVxaDJsZaqY3Q+cYWj+YFBghYCwFzguS4rrAUXGRAxaxVZWJXjhpSZt4ng2O84Qie2njdp5eUJmchXFd1BjBVpTGAlM4gQMujopGkXpwv6p7NVhSXCV43SqfU2qqYYWVUm42rHAOcb1L12gsmV0zzYtRbLRku6VqhNboXWiWNi+3j6spfSDB84TqKdZjHAY-kRE05hfMSbLTTms2PXIvJ1SZHFkFxFA0LQm02Y2xiKsyJJNPqNxQ5Scl-hYOgBEVt6fsYqn0QxCioBAcDKNpuDfaG14hIq9T2FcjSWEEgync0XQkv4k1ZQYjQRD8SNjjMcy7MsayQPzrV2XYFQi0rt6+IE9gWFSZR09qZzNN1fRDFEaucrpPJQHrgklF4ej0yJInKRDpIeYg5hKoMZhvGUbxFfRYzIzoeYFuCnvbQgcs+Gb+oib4x1UsE4e9PYZzWLe90VaBUDgTVqeNmLF1GlU9S+FDRpkcLKkhO8Vwkqr8djknrEQLX16spcCl0poRoGE0NztxP1QvmLARPM7-eu9y+mGfVI9tV4RuXOqgSJsSIlUrRJyr8fdT+FUfeMQng8RXsGPDxehPXkvlTVAmQy9Le59JqX2JPiF8gxMyR3LtOSqYFqqrlfrvA2jcfAWH6IYGePtwjnwiCcYqHxbCqk0Jpdekw5oLTRh7d+P1P5nAUp8Dq6hRJeFVKdTovtSR2CaD+TMTQzD3TIU9KACCqECzauLOmYtiZZRqAOVhxJ7ysljCEGSTQ4xs0iEAA */ createMachine( { @@ -33,11 +40,22 @@ export const createPureLogViewStateMachine = (initialContext: LogViewContextWith states: { uninitialized: { always: { - target: 'loading', + target: 'initializingFromUrl', + }, + }, + initializingFromUrl: { + on: { + INITIALIZED_FROM_URL: { + target: 'loading', + actions: ['storeLogViewReference'], + }, + }, + invoke: { + src: 'initializeFromUrl', }, }, loading: { - entry: 'notifyLoadingStarted', + entry: ['notifyLoadingStarted', 'updateContextInUrl'], invoke: { src: 'loadLogView', }, @@ -76,18 +94,69 @@ export const createPureLogViewStateMachine = (initialContext: LogViewContextWith target: 'checkingStatusFailed', actions: 'storeError', }, - CHECKING_STATUS_SUCCEEDED: { - target: 'resolved', - actions: 'storeStatus', - }, + CHECKING_STATUS_SUCCEEDED: [ + { + target: 'resolvedPersistedLogView', + actions: 'storeStatus', + cond: 'isPersistedLogView', + }, + { + target: 'resolvedInlineLogView', + actions: 'storeStatus', + }, + ], }, }, - resolved: { - entry: 'notifyLoadingSucceeded', + resolvedPersistedLogView: { + invoke: { + src: 'listenForUrlChanges', + }, + entry: ['notifyLoadingSucceeded', 'updateContextInUrl'], on: { + PERSIST_INLINE_LOG_VIEW: undefined, RELOAD_LOG_VIEW: { target: 'loading', }, + LOG_VIEW_URL_KEY_REMOVED: { + actions: 'updateContextInUrl', + }, + }, + }, + resolvedInlineLogView: { + invoke: { + src: 'listenForUrlChanges', + }, + entry: ['notifyLoadingSucceeded', 'updateContextInUrl'], + on: { + PERSIST_INLINE_LOG_VIEW: { + target: 'persistingInlineLogView', + }, + LOG_VIEW_URL_KEY_REMOVED: { + actions: 'updateContextInUrl', + }, + }, + }, + persistingInlineLogView: { + invoke: { + src: 'persistInlineLogView', + }, + on: { + PERSISTING_INLINE_LOG_VIEW_FAILED: { + target: 'persistingInlineLogViewFailed', + actions: 'storeError', + }, + PERSISTING_INLINE_LOG_VIEW_SUCCEEDED: { + target: 'resolving', + actions: ['convertInlineLogViewReferenceToPersistedLogViewReference', 'storeLogView'], + }, + }, + }, + persistingInlineLogViewFailed: { + entry: 'notifyPersistingInlineLogViewFailed', + on: { + RETRY_PERSISTING_INLINE_LOG_VIEW: { + target: 'persistingInlineLogView', + }, }, }, loadingFailed: { @@ -126,7 +195,7 @@ export const createPureLogViewStateMachine = (initialContext: LogViewContextWith }, UPDATING_SUCCEEDED: { target: 'resolving', - actions: 'storeLogView', + actions: ['updateLogViewReference', 'storeLogView'], }, }, }, @@ -140,10 +209,9 @@ export const createPureLogViewStateMachine = (initialContext: LogViewContextWith }, }, on: { - LOG_VIEW_ID_CHANGED: { + LOG_VIEW_REFERENCE_CHANGED: { target: '.loading', - actions: 'storeLogViewId', - cond: 'isLogViewIdDifferent', + actions: 'storeLogViewReference', }, UPDATE: { target: '.updating', @@ -155,11 +223,11 @@ export const createPureLogViewStateMachine = (initialContext: LogViewContextWith notifyLoadingStarted: actions.pure(() => undefined), notifyLoadingSucceeded: actions.pure(() => undefined), notifyLoadingFailed: actions.pure(() => undefined), - storeLogViewId: assign((context, event) => - 'logViewId' in event + storeLogViewReference: assign((context, event) => + 'logViewReference' in event && event.logViewReference !== null ? ({ - logViewId: event.logViewId, - } as LogViewContextWithId) + logViewReference: event.logViewReference, + } as LogViewContextWithReference) : {} ), storeLogView: assign((context, event) => @@ -190,28 +258,55 @@ export const createPureLogViewStateMachine = (initialContext: LogViewContextWith } as LogViewContextWithError) : {} ), + convertInlineLogViewReferenceToPersistedLogViewReference: assign((context, event) => + 'logView' in event && context.logViewReference.type === 'log-view-inline' + ? ({ + logViewReference: { + type: 'log-view-reference', + logViewId: context.logViewReference.id, + }, + } as LogViewContextWithReference) + : {} + ), + updateLogViewReference: assign((context, event) => + 'attributes' in event && context.logViewReference.type === 'log-view-inline' + ? ({ + logViewReference: { + ...context.logViewReference, + attributes: { + ...context.logViewReference.attributes, + ...event.attributes, + }, + }, + } as LogViewContextWithReference) + : {} + ), }, guards: { - isLogViewIdDifferent: (context, event) => - 'logViewId' in event ? event.logViewId !== context.logViewId : false, + isPersistedLogView: (context, event) => + context.logViewReference.type === 'log-view-reference', }, } ); export interface LogViewStateMachineDependencies { - initialContext: LogViewContextWithId; + initialContext: LogViewContextWithReference; logViews: ILogViewsClient; notificationChannel?: NotificationChannel; + toastsService: IToasts; + urlStateStorage: IKbnUrlStateStorage; } export const createLogViewStateMachine = ({ initialContext, logViews, notificationChannel, + toastsService, + urlStateStorage, }: LogViewStateMachineDependencies) => createPureLogViewStateMachine(initialContext).withConfig({ - actions: - notificationChannel != null + actions: { + ...(notificationChannel != null ? { notifyLoadingStarted: notificationChannel.notify( logViewNotificationEventSelectors.loadingLogViewStarted @@ -223,13 +318,17 @@ export const createLogViewStateMachine = ({ logViewNotificationEventSelectors.loadingLogViewFailed ), } - : {}, + : {}), + updateContextInUrl: updateContextInUrl({ toastsService, urlStateStorage }), + }, services: { + initializeFromUrl: initializeFromUrl({ toastsService, urlStateStorage }), + listenForUrlChanges: listenForUrlChanges({ urlStateStorage }), loadLogView: (context) => from( - 'logViewId' in context - ? logViews.getLogView(context.logViewId) - : throwError(() => new Error('Failed to load log view: No id found in context.')) + 'logViewReference' in context + ? logViews.getLogView(context.logViewReference) + : throwError(() => new Error('Failed to load log view')) ).pipe( map( (logView): LogViewEvent => ({ @@ -246,8 +345,8 @@ export const createLogViewStateMachine = ({ ), updateLogView: (context, event) => from( - 'logViewId' in context && event.type === 'UPDATE' - ? logViews.putLogView(context.logViewId, event.attributes) + 'logViewReference' in context && event.type === 'UPDATE' + ? logViews.putLogView(context.logViewReference, event.attributes) : throwError( () => new Error( @@ -268,6 +367,30 @@ export const createLogViewStateMachine = ({ }) ) ), + persistInlineLogView: (context, event) => + from( + 'logViewReference' in context && + event.type === 'PERSIST_INLINE_LOG_VIEW' && + context.logViewReference.type === 'log-view-inline' + ? logViews.putLogView( + { type: 'log-view-reference', logViewId: context.logViewReference.id }, + context.logViewReference.attributes + ) + : throwError(() => new Error('Failed to persist inline Log View.')) + ).pipe( + map( + (logView): LogViewEvent => ({ + type: 'PERSISTING_INLINE_LOG_VIEW_SUCCEEDED', + logView, + }) + ), + catchError((error) => + of({ + type: 'PERSISTING_INLINE_LOG_VIEW_FAILED', + error, + }) + ) + ), resolveLogView: (context) => from( 'logView' in context diff --git a/x-pack/plugins/infra/public/observability_logs/log_view_state/src/types.ts b/x-pack/plugins/infra/public/observability_logs/log_view_state/src/types.ts index 3a997748c5c3..3a7914e8ff6d 100644 --- a/x-pack/plugins/infra/public/observability_logs/log_view_state/src/types.ts +++ b/x-pack/plugins/infra/public/observability_logs/log_view_state/src/types.ts @@ -9,14 +9,15 @@ import type { ActorRef } from 'xstate'; import type { LogView, LogViewAttributes, + LogViewReference, LogViewStatus, ResolvedLogView, } from '../../../../common/log_views'; import { type NotificationChannel } from '../../xstate_helpers'; import { type LogViewNotificationEvent } from './notifications'; -export interface LogViewContextWithId { - logViewId: string; +export interface LogViewContextWithReference { + logViewReference: LogViewReference; } export interface LogViewContextWithLogView { @@ -38,46 +39,55 @@ export interface LogViewContextWithError { export type LogViewTypestate = | { value: 'uninitialized'; - context: LogViewContextWithId; + context: LogViewContextWithReference; } | { value: 'loading'; - context: LogViewContextWithId; + context: LogViewContextWithReference; } | { value: 'resolving'; - context: LogViewContextWithId & LogViewContextWithLogView; + context: LogViewContextWithReference & LogViewContextWithLogView; } | { value: 'checkingStatus'; - context: LogViewContextWithId & LogViewContextWithLogView & LogViewContextWithResolvedLogView; + context: LogViewContextWithReference & + LogViewContextWithLogView & + LogViewContextWithResolvedLogView; } | { - value: 'resolved'; - context: LogViewContextWithId & + value: 'resolvedPersistedLogView'; + context: LogViewContextWithReference & + LogViewContextWithLogView & + LogViewContextWithResolvedLogView & + LogViewContextWithStatus; + } + | { + value: 'resolvedInlineLogView'; + context: LogViewContextWithReference & LogViewContextWithLogView & LogViewContextWithResolvedLogView & LogViewContextWithStatus; } | { value: 'updating'; - context: LogViewContextWithId; + context: LogViewContextWithReference; } | { value: 'loadingFailed'; - context: LogViewContextWithId & LogViewContextWithError; + context: LogViewContextWithReference & LogViewContextWithError; } | { value: 'updatingFailed'; - context: LogViewContextWithId & LogViewContextWithError; + context: LogViewContextWithReference & LogViewContextWithError; } | { value: 'resolutionFailed'; - context: LogViewContextWithId & LogViewContextWithLogView & LogViewContextWithError; + context: LogViewContextWithReference & LogViewContextWithLogView & LogViewContextWithError; } | { value: 'checkingStatusFailed'; - context: LogViewContextWithId & LogViewContextWithLogView & LogViewContextWithError; + context: LogViewContextWithReference & LogViewContextWithLogView & LogViewContextWithError; }; export type LogViewContext = LogViewTypestate['context']; @@ -86,8 +96,12 @@ export type LogViewStateValue = LogViewTypestate['value']; export type LogViewEvent = | { - type: 'LOG_VIEW_ID_CHANGED'; - logViewId: string; + type: 'LOG_VIEW_REFERENCE_CHANGED'; + logViewReference: LogViewReference; + } + | { + type: 'INITIALIZED_FROM_URL'; + logViewReference: LogViewReference | null; } | { type: 'LOADING_SUCCEEDED'; @@ -130,7 +144,17 @@ export type LogViewEvent = } | { type: 'RELOAD_LOG_VIEW'; - }; + } + | { + type: 'PERSIST_INLINE_LOG_VIEW'; + } + | { type: 'PERSISTING_INLINE_LOG_VIEW_FAILED'; error: Error } + | { type: 'PERSISTING_INLINE_LOG_VIEW_SUCCEEDED'; logView: LogView } + | { + type: 'RETRY_PERSISTING_INLINE_LOG_VIEW'; + } + | { type: 'LOG_VIEW_URL_KEY_REMOVED' } + | { type: 'LOG_VIEW_URL_KEY_CHANGED' }; export type LogViewActorRef = ActorRef; export type LogViewNotificationChannel = NotificationChannel< diff --git a/x-pack/plugins/infra/public/observability_logs/log_view_state/src/url_state_storage_service.ts b/x-pack/plugins/infra/public/observability_logs/log_view_state/src/url_state_storage_service.ts new file mode 100644 index 000000000000..dd9098b09052 --- /dev/null +++ b/x-pack/plugins/infra/public/observability_logs/log_view_state/src/url_state_storage_service.ts @@ -0,0 +1,135 @@ +/* + * 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 * as rt from 'io-ts'; +import { IToasts } from '@kbn/core/public'; +import { IKbnUrlStateStorage, withNotifyOnErrors } from '@kbn/kibana-utils-plugin/public'; +import { InvokeCreator } from 'xstate'; +import * as Either from 'fp-ts/lib/Either'; +import { identity, pipe } from 'fp-ts/lib/function'; +import { map } from 'rxjs'; +import { createPlainError, formatErrors } from '../../../../common/runtime_types'; +import { + LogViewReference, + logViewReferenceRT, + PersistedLogViewReference, +} from '../../../../common/log_views'; +import { LogViewContext, LogViewEvent } from './types'; +import { replaceStateKeyInQueryString } from '../../../utils/url_state'; + +export const defaultLogViewKey = 'logView'; +const defaultLegacySourceIdKey = 'sourceId'; + +interface LogViewUrlStateDependencies { + logViewKey?: string; + sourceIdKey?: string; + toastsService: IToasts; + urlStateStorage: IKbnUrlStateStorage; +} + +export const updateContextInUrl = + ({ urlStateStorage, logViewKey = defaultLogViewKey }: LogViewUrlStateDependencies) => + (context: LogViewContext, _event: LogViewEvent) => { + if (!('logViewReference' in context)) { + throw new Error('Missing keys from context needed to sync to the URL'); + } + + urlStateStorage.set(logViewKey, logViewStateInUrlRT.encode(context.logViewReference), { + replace: true, + }); + }; + +export const initializeFromUrl = + ({ + logViewKey = defaultLogViewKey, + sourceIdKey = defaultLegacySourceIdKey, + toastsService, + urlStateStorage, + }: LogViewUrlStateDependencies): InvokeCreator => + (_context, _event) => + (send) => { + const logViewQueryValueFromUrl = urlStateStorage.get(logViewKey); + const logViewQueryE = decodeLogViewQueryValueFromUrl(logViewQueryValueFromUrl); + + const legacySourceIdQueryValueFromUrl = urlStateStorage.get(sourceIdKey); + const sourceIdQueryE = decodeSourceIdQueryValueFromUrl(legacySourceIdQueryValueFromUrl); + + if (Either.isLeft(logViewQueryE) || Either.isLeft(sourceIdQueryE)) { + withNotifyOnErrors(toastsService).onGetError( + createPlainError( + formatErrors([ + ...(Either.isLeft(logViewQueryE) ? logViewQueryE.left : []), + ...(Either.isLeft(sourceIdQueryE) ? sourceIdQueryE.left : []), + ]) + ) + ); + + send({ + type: 'INITIALIZED_FROM_URL', + logViewReference: null, + }); + } else { + send({ + type: 'INITIALIZED_FROM_URL', + logViewReference: pipe( + // Via the legacy sourceId key + pipe( + sourceIdQueryE.right, + Either.fromNullable(null), + Either.map(convertSourceIdToReference) + ), + // Via the logView key + Either.alt(() => pipe(logViewQueryE.right, Either.fromNullable(null))), + Either.fold(identity, identity) + ), + }); + } + }; + +// NOTE: Certain navigations within the Logs solution will remove the logView URL key, +// we want to ensure the logView key is present in the URL at all times by monitoring for it's removal. +export const listenForUrlChanges = + ({ + urlStateStorage, + logViewKey = defaultLogViewKey, + }: { + urlStateStorage: LogViewUrlStateDependencies['urlStateStorage']; + logViewKey?: LogViewUrlStateDependencies['logViewKey']; + }): InvokeCreator => + (context, event) => { + return urlStateStorage + .change$(logViewKey) + .pipe( + map((value) => + value === undefined || value === null + ? { type: 'LOG_VIEW_URL_KEY_REMOVED' } + : { type: 'LOG_VIEW_URL_KEY_CHANGED' } + ) + ); + }; + +const logViewStateInUrlRT = rt.union([logViewReferenceRT, rt.null]); +const sourceIdStateInUrl = rt.union([rt.string, rt.null]); + +const decodeLogViewQueryValueFromUrl = (queryValueFromUrl: unknown) => { + return logViewStateInUrlRT.decode(queryValueFromUrl); +}; + +const decodeSourceIdQueryValueFromUrl = (queryValueFromUrl: unknown) => { + return sourceIdStateInUrl.decode(queryValueFromUrl); +}; + +const convertSourceIdToReference = (sourceId: string): PersistedLogViewReference => { + return { + type: 'log-view-reference' as const, + logViewId: sourceId, + }; +}; + +// NOTE: Used by link-to components +export const replaceLogViewInQueryString = (logViewReference: LogViewReference) => + replaceStateKeyInQueryString(defaultLogViewKey, logViewReference); diff --git a/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/index.ts b/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/index.ts index fa0e5da4cb4c..8e6f993a9755 100644 --- a/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/index.ts +++ b/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/index.ts @@ -9,3 +9,4 @@ export * from './invalid_state_callout'; export * from './notification_channel'; export * from './send_actions'; export * from './types'; +export * from './state_machine_playground'; diff --git a/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/state_machine_playground.tsx b/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/state_machine_playground.tsx new file mode 100644 index 000000000000..78f1c5f15dee --- /dev/null +++ b/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/state_machine_playground.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. + */ + +import { EuiButton } from '@elastic/eui'; +import React, { useCallback } from 'react'; +import { useLogViewContext } from '../../../hooks/use_log_view'; + +export const StateMachinePlayground = () => { + const { changeLogViewReference, revertToDefaultLogView, update, isLoading, logViewStateService } = + useLogViewContext(); + + const switchToInlineLogView = useCallback(() => { + changeLogViewReference({ + type: 'log-view-inline', + id: 'playground-log-view', + attributes: { + name: 'playground-log-view-name', + description: 'from the state machine playground', + logIndices: { type: 'index_name', indexName: 'logs-*' }, + logColumns: [ + { + fieldColumn: { + id: 'playground-field-column', + field: 'event.dataset', + }, + }, + ], + }, + }); + }, [changeLogViewReference]); + + const updateLogView = useCallback(() => { + update({ + name: 'Updated playground name', + }); + }, [update]); + + const persistInlineLogView = useCallback(() => { + logViewStateService.send({ + type: 'PERSIST_INLINE_LOG_VIEW', + }); + }, [logViewStateService]); + + return ( + <> + {isLoading && 'Is loading'} + switchToInlineLogView()} + > + {'Switch to inline Log View'} + + persistInlineLogView()} + > + {'Persist inline Log View'} + + revertToDefaultLogView()} + > + {'Revert to default (persisted) Log View'} + + updateLogView()} + > + {'Update log view'} + + + ); +}; diff --git a/x-pack/plugins/infra/public/pages/link_to/link_to_logs.test.tsx b/x-pack/plugins/infra/public/pages/link_to/link_to_logs.test.tsx index c6af835dad80..d3dd705a5981 100644 --- a/x-pack/plugins/infra/public/pages/link_to/link_to_logs.test.tsx +++ b/x-pack/plugins/infra/public/pages/link_to/link_to_logs.test.tsx @@ -22,6 +22,8 @@ import { LinkToLogsPage } from './link_to_logs'; jest.mock('../../hooks/use_log_view'); const useLogViewMock = useLogView as jest.MockedFunction; +const LOG_VIEW_REFERENCE = '(logViewId:default,type:log-view-reference)'; +const OTHER_LOG_VIEW_REFERENCE = '(logViewId:OTHER_SOURCE,type:log-view-reference)'; const renderRoutes = (routes: React.ReactElement) => { const history = createMemoryHistory(); @@ -71,7 +73,7 @@ describe('LinkToLogsPage component', () => { expect(history.location.pathname).toEqual('/stream'); const searchParams = new URLSearchParams(history.location.search); - expect(searchParams.get('sourceId')).toEqual('default'); + expect(searchParams.get('logView')).toEqual(LOG_VIEW_REFERENCE); expect(searchParams.get('logFilter')).toMatchInlineSnapshot( `"(query:(language:kuery,query:'FILTER_FIELD:FILTER_VALUE'),refreshInterval:(pause:!t,value:5000),timeRange:(from:'2019-02-20T12:58:09.404Z',to:'2019-02-20T14:58:09.404Z'))"` ); @@ -92,7 +94,7 @@ describe('LinkToLogsPage component', () => { expect(history.location.pathname).toEqual('/stream'); const searchParams = new URLSearchParams(history.location.search); - expect(searchParams.get('sourceId')).toEqual('OTHER_SOURCE'); + expect(searchParams.get('logView')).toEqual(OTHER_LOG_VIEW_REFERENCE); expect(searchParams.get('logFilter')).toMatchInlineSnapshot( `"(query:(language:kuery,query:''),refreshInterval:(pause:!t,value:5000))"` ); @@ -113,7 +115,7 @@ describe('LinkToLogsPage component', () => { expect(history.location.pathname).toEqual('/stream'); const searchParams = new URLSearchParams(history.location.search); - expect(searchParams.get('sourceId')).toEqual('default'); + expect(searchParams.get('logView')).toEqual(LOG_VIEW_REFERENCE); expect(searchParams.get('logFilter')).toMatchInlineSnapshot( `"(query:(language:kuery,query:'FILTER_FIELD:FILTER_VALUE'),refreshInterval:(pause:!t,value:5000),timeRange:(from:'2019-02-20T12:58:09.404Z',to:'2019-02-20T14:58:09.404Z'))"` ); @@ -134,7 +136,7 @@ describe('LinkToLogsPage component', () => { expect(history.location.pathname).toEqual('/stream'); const searchParams = new URLSearchParams(history.location.search); - expect(searchParams.get('sourceId')).toEqual('OTHER_SOURCE'); + expect(searchParams.get('logView')).toEqual(OTHER_LOG_VIEW_REFERENCE); expect(searchParams.get('logFilter')).toMatchInlineSnapshot( `"(query:(language:kuery,query:''),refreshInterval:(pause:!t,value:5000))"` ); @@ -155,7 +157,7 @@ describe('LinkToLogsPage component', () => { expect(history.location.pathname).toEqual('/stream'); const searchParams = new URLSearchParams(history.location.search); - expect(searchParams.get('sourceId')).toEqual('default'); + expect(searchParams.get('logView')).toEqual(LOG_VIEW_REFERENCE); expect(searchParams.get('logFilter')).toMatchInlineSnapshot( `"(query:(language:kuery,query:'host.name: HOST_NAME'),refreshInterval:(pause:!t,value:5000))"` ); @@ -176,7 +178,7 @@ describe('LinkToLogsPage component', () => { expect(history.location.pathname).toEqual('/stream'); const searchParams = new URLSearchParams(history.location.search); - expect(searchParams.get('sourceId')).toEqual('default'); + expect(searchParams.get('logView')).toEqual(LOG_VIEW_REFERENCE); expect(searchParams.get('logFilter')).toMatchInlineSnapshot( `"(query:(language:kuery,query:'(host.name: HOST_NAME) and (FILTER_FIELD:FILTER_VALUE)'),refreshInterval:(pause:!t,value:5000),timeRange:(from:'2019-02-20T12:58:09.404Z',to:'2019-02-20T14:58:09.404Z'))"` ); @@ -197,7 +199,7 @@ describe('LinkToLogsPage component', () => { expect(history.location.pathname).toEqual('/stream'); const searchParams = new URLSearchParams(history.location.search); - expect(searchParams.get('sourceId')).toEqual('OTHER_SOURCE'); + expect(searchParams.get('logView')).toEqual(OTHER_LOG_VIEW_REFERENCE); expect(searchParams.get('logFilter')).toMatchInlineSnapshot( `"(query:(language:kuery,query:'host.name: HOST_NAME'),refreshInterval:(pause:!t,value:5000))"` ); @@ -233,7 +235,7 @@ describe('LinkToLogsPage component', () => { expect(history.location.pathname).toEqual('/stream'); const searchParams = new URLSearchParams(history.location.search); - expect(searchParams.get('sourceId')).toEqual('default'); + expect(searchParams.get('logView')).toEqual(LOG_VIEW_REFERENCE); expect(searchParams.get('logFilter')).toMatchInlineSnapshot( `"(query:(language:kuery,query:'container.id: CONTAINER_ID'),refreshInterval:(pause:!t,value:5000))"` ); @@ -254,7 +256,7 @@ describe('LinkToLogsPage component', () => { expect(history.location.pathname).toEqual('/stream'); const searchParams = new URLSearchParams(history.location.search); - expect(searchParams.get('sourceId')).toEqual('default'); + expect(searchParams.get('logView')).toEqual(LOG_VIEW_REFERENCE); expect(searchParams.get('logFilter')).toMatchInlineSnapshot( `"(query:(language:kuery,query:'(container.id: CONTAINER_ID) and (FILTER_FIELD:FILTER_VALUE)'),refreshInterval:(pause:!t,value:5000),timeRange:(from:'2019-02-20T12:58:09.404Z',to:'2019-02-20T14:58:09.404Z'))"` ); @@ -291,7 +293,7 @@ describe('LinkToLogsPage component', () => { expect(history.location.pathname).toEqual('/stream'); const searchParams = new URLSearchParams(history.location.search); - expect(searchParams.get('sourceId')).toEqual('default'); + expect(searchParams.get('logView')).toEqual(LOG_VIEW_REFERENCE); expect(searchParams.get('logFilter')).toMatchInlineSnapshot( `"(query:(language:kuery,query:'kubernetes.pod.uid: POD_UID'),refreshInterval:(pause:!t,value:5000))"` ); @@ -310,7 +312,7 @@ describe('LinkToLogsPage component', () => { expect(history.location.pathname).toEqual('/stream'); const searchParams = new URLSearchParams(history.location.search); - expect(searchParams.get('sourceId')).toEqual('default'); + expect(searchParams.get('logView')).toEqual(LOG_VIEW_REFERENCE); expect(searchParams.get('logFilter')).toMatchInlineSnapshot( `"(query:(language:kuery,query:'(kubernetes.pod.uid: POD_UID) and (FILTER_FIELD:FILTER_VALUE)'),refreshInterval:(pause:!t,value:5000),timeRange:(from:'2019-02-20T12:58:09.404Z',to:'2019-02-20T14:58:09.404Z'))"` ); diff --git a/x-pack/plugins/infra/public/pages/link_to/link_to_logs.tsx b/x-pack/plugins/infra/public/pages/link_to/link_to_logs.tsx index 85d38e7f476d..60afe9c98bd5 100644 --- a/x-pack/plugins/infra/public/pages/link_to/link_to_logs.tsx +++ b/x-pack/plugins/infra/public/pages/link_to/link_to_logs.tsx @@ -26,11 +26,11 @@ export const LinkToLogsPage: React.FC = (props) => { return ( - - + + ); diff --git a/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.test.tsx b/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.test.tsx index 58ba0df004f2..c5c3e4d50758 100644 --- a/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.test.tsx +++ b/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.test.tsx @@ -20,7 +20,7 @@ describe('RedirectToLogs component', () => { expect(component).toMatchInlineSnapshot(` `); }); @@ -34,7 +34,7 @@ describe('RedirectToLogs component', () => { expect(component).toMatchInlineSnapshot(` `); }); @@ -46,7 +46,7 @@ describe('RedirectToLogs component', () => { expect(component).toMatchInlineSnapshot(` `); }); diff --git a/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.tsx b/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.tsx index 32bc4c909284..4d4533596fd5 100644 --- a/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.tsx +++ b/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.tsx @@ -8,27 +8,27 @@ import React from 'react'; import { match as RouteMatch, Redirect, RouteComponentProps } from 'react-router-dom'; import { flowRight } from 'lodash'; -import { replaceSourceIdInQueryString } from '../../containers/source_id'; import { replaceLogPositionInQueryString } from '../../observability_logs/log_stream_position_state/src/url_state_storage_service'; import { replaceLogFilterInQueryString } from '../../observability_logs/log_stream_query_state'; import { getFilterFromLocation, getTimeFromLocation } from './query_params'; +import { replaceLogViewInQueryString } from '../../observability_logs/log_view_state'; type RedirectToLogsType = RouteComponentProps<{}>; interface RedirectToLogsProps extends RedirectToLogsType { match: RouteMatch<{ - sourceId?: string; + logViewId?: string; }>; } export const RedirectToLogs = ({ location, match }: RedirectToLogsProps) => { - const sourceId = match.params.sourceId || 'default'; + const logViewId = match.params.logViewId || 'default'; const filter = getFilterFromLocation(location); const time = getTimeFromLocation(location); const searchString = flowRight( replaceLogFilterInQueryString({ language: 'kuery', query: filter }, time), replaceLogPositionInQueryString(time), - replaceSourceIdInQueryString(sourceId) + replaceLogViewInQueryString({ type: 'log-view-reference', logViewId }) )(''); return ; diff --git a/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx b/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx index 4f5fb79acc76..7be728273e5b 100644 --- a/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx +++ b/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx @@ -14,28 +14,28 @@ import { flowRight } from 'lodash'; import { findInventoryFields } from '../../../common/inventory_models'; import { InventoryItemType } from '../../../common/inventory_models/types'; import { LoadingPage } from '../../components/loading_page'; -import { replaceSourceIdInQueryString } from '../../containers/source_id'; import { useKibanaContextForPlugin } from '../../hooks/use_kibana'; import { useLogView } from '../../hooks/use_log_view'; import { replaceLogFilterInQueryString } from '../../observability_logs/log_stream_query_state'; import { getFilterFromLocation, getTimeFromLocation } from './query_params'; import { replaceLogPositionInQueryString } from '../../observability_logs/log_stream_position_state/src/url_state_storage_service'; +import { replaceLogViewInQueryString } from '../../observability_logs/log_view_state'; type RedirectToNodeLogsType = RouteComponentProps<{ nodeId: string; nodeType: InventoryItemType; - sourceId?: string; + logViewId?: string; }>; export const RedirectToNodeLogs = ({ match: { - params: { nodeId, nodeType, sourceId = 'default' }, + params: { nodeId, nodeType, logViewId = 'default' }, }, location, }: RedirectToNodeLogsType) => { const { services } = useKibanaContextForPlugin(); const { isLoading, load } = useLogView({ - logViewId: sourceId, + initialLogViewReference: { type: 'log-view-reference', logViewId }, logViews: services.logViews.client, }); @@ -65,7 +65,7 @@ export const RedirectToNodeLogs = ({ const searchString = flowRight( replaceLogFilterInQueryString({ language: 'kuery', query: filter }, time), replaceLogPositionInQueryString(time), - replaceSourceIdInQueryString(sourceId) + replaceLogViewInQueryString({ type: 'log-view-reference', logViewId }) )(''); return ; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_providers.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_providers.tsx index 35150368b586..6e1d3f4a4f0b 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_providers.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_providers.tsx @@ -6,6 +6,7 @@ */ import React from 'react'; +import { InlineLogViewSplashPage } from '../../../components/logging/inline_log_view_splash_page'; import { LogAnalysisSetupFlyoutStateProvider } from '../../../components/logging/log_analysis_setup/setup_flyout'; import { SourceLoadingPage } from '../../../components/source_loading_page'; import { LogEntryCategoriesModuleProvider } from '../../../containers/logs/log_analysis/modules/log_entry_categories'; @@ -14,8 +15,15 @@ import { useLogViewContext } from '../../../hooks/use_log_view'; import { ConnectedLogViewErrorPage } from '../shared/page_log_view_error'; export const LogEntryCategoriesPageProviders: React.FunctionComponent = ({ children }) => { - const { hasFailedLoading, isLoading, isUninitialized, resolvedLogView, logViewId } = - useLogViewContext(); + const { + hasFailedLoading, + isLoading, + isUninitialized, + resolvedLogView, + logViewReference, + isPersistedLogView, + revertToDefaultLogView, + } = useLogViewContext(); const { space } = useActiveKibanaSpace(); // This is a rather crude way of guarding the dependent providers against @@ -23,15 +31,20 @@ export const LogEntryCategoriesPageProviders: React.FunctionComponent = ({ child // React concurrent mode and Suspense in order to handle that more gracefully. if (space == null) { return null; + } else if (!isPersistedLogView) { + return ; } else if (hasFailedLoading) { return ; } else if (isLoading || isUninitialized) { return ; } else if (resolvedLogView != null) { + if (logViewReference.type === 'log-view-inline') { + throw new Error('Logs ML features only support persisted Log View references'); + } return ( @@ -265,7 +265,7 @@ export const LogEntryCategoriesResultsContent: React.FunctionComponent< = ({ categoryId, timeRange, sourceId }) => { + logViewReference: PersistedLogViewReference; +}> = ({ categoryId, timeRange, logViewReference }) => { const { getLogEntryCategoryExamples, hasFailedLoadingLogEntryCategoryExamples, @@ -27,7 +28,7 @@ export const CategoryDetailsRow: React.FunctionComponent<{ categoryId, endTime: timeRange.endTime, exampleCount, - sourceId, + logViewReference, startTime: timeRange.startTime, }); diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_section.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_section.tsx index 0aabc570a89e..c9f93ee618dd 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_section.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_section.tsx @@ -9,6 +9,7 @@ import { EuiLoadingSpinner } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; +import { PersistedLogViewReference } from '../../../../../../common/log_views'; import { LogEntryCategory } from '../../../../../../common/log_analysis'; import { TimeRange } from '../../../../../../common/time'; import { LoadingOverlayWrapper } from '../../../../../components/loading_overlay_wrapper'; @@ -18,7 +19,7 @@ import { SortOptions, ChangeSortOptions } from '../../use_log_entry_categories_r export const TopCategoriesSection: React.FunctionComponent<{ isLoadingTopCategories?: boolean; jobId: string; - sourceId: string; + logViewReference: PersistedLogViewReference; timeRange: TimeRange; topCategories: LogEntryCategory[]; sortOptions: SortOptions; @@ -26,7 +27,7 @@ export const TopCategoriesSection: React.FunctionComponent<{ }> = ({ isLoadingTopCategories = false, jobId, - sourceId, + logViewReference, timeRange, topCategories, sortOptions, @@ -40,7 +41,7 @@ export const TopCategoriesSection: React.FunctionComponent<{ > ), }), {} ), - [expandedCategories, sourceId, timeRange] + [expandedCategories, logViewReference, timeRange] ); return ( diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_datasets.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_datasets.ts index 34358b983e12..e2f34f644eca 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_datasets.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_datasets.ts @@ -6,6 +6,7 @@ */ import type { HttpHandler } from '@kbn/core/public'; +import { PersistedLogViewReference } from '../../../../../common/log_views'; import { getLogEntryCategoryDatasetsRequestPayloadRT, @@ -15,7 +16,7 @@ import { import { decodeOrThrow } from '../../../../../common/runtime_types'; interface RequestArgs { - sourceId: string; + logViewReference: PersistedLogViewReference; startTime: number; endTime: number; } @@ -24,14 +25,14 @@ export const callGetLogEntryCategoryDatasetsAPI = async ( requestArgs: RequestArgs, fetch: HttpHandler ) => { - const { sourceId, startTime, endTime } = requestArgs; + const { logViewReference, startTime, endTime } = requestArgs; const response = await fetch(LOG_ANALYSIS_GET_LOG_ENTRY_CATEGORY_DATASETS_PATH, { method: 'POST', body: JSON.stringify( getLogEntryCategoryDatasetsRequestPayloadRT.encode({ data: { - logView: { type: 'log-view-reference', logViewId: sourceId }, + logView: logViewReference, timeRange: { startTime, endTime, diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_examples.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_examples.ts index e3b99750af71..52f9f17747b5 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_examples.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_examples.ts @@ -6,6 +6,7 @@ */ import type { HttpHandler } from '@kbn/core/public'; +import { PersistedLogViewReference } from '../../../../../common/log_views'; import { getLogEntryCategoryExamplesRequestPayloadRT, @@ -15,7 +16,7 @@ import { import { decodeOrThrow } from '../../../../../common/runtime_types'; interface RequestArgs { - sourceId: string; + logViewReference: PersistedLogViewReference; startTime: number; endTime: number; categoryId: number; @@ -26,7 +27,7 @@ export const callGetLogEntryCategoryExamplesAPI = async ( requestArgs: RequestArgs, fetch: HttpHandler ) => { - const { sourceId, startTime, endTime, categoryId, exampleCount } = requestArgs; + const { logViewReference, startTime, endTime, categoryId, exampleCount } = requestArgs; const response = await fetch(LOG_ANALYSIS_GET_LOG_ENTRY_CATEGORY_EXAMPLES_PATH, { method: 'POST', @@ -35,7 +36,7 @@ export const callGetLogEntryCategoryExamplesAPI = async ( data: { categoryId, exampleCount, - logView: { type: 'log-view-reference', logViewId: sourceId }, + logView: logViewReference, timeRange: { startTime, endTime, diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_top_log_entry_categories.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_top_log_entry_categories.ts index 93e9daf0b9cb..0cd04d999183 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_top_log_entry_categories.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_top_log_entry_categories.ts @@ -6,6 +6,7 @@ */ import type { HttpHandler } from '@kbn/core/public'; +import { PersistedLogViewReference } from '../../../../../common/log_views'; import { getLogEntryCategoriesRequestPayloadRT, @@ -16,7 +17,7 @@ import { CategoriesSort } from '../../../../../common/log_analysis'; import { decodeOrThrow } from '../../../../../common/runtime_types'; interface RequestArgs { - sourceId: string; + logViewReference: PersistedLogViewReference; startTime: number; endTime: number; categoryCount: number; @@ -28,7 +29,7 @@ export const callGetTopLogEntryCategoriesAPI = async ( requestArgs: RequestArgs, fetch: HttpHandler ) => { - const { sourceId, startTime, endTime, categoryCount, datasets, sort } = requestArgs; + const { logViewReference, startTime, endTime, categoryCount, datasets, sort } = requestArgs; const intervalDuration = endTime - startTime; const response = await fetch(LOG_ANALYSIS_GET_LOG_ENTRY_CATEGORIES_PATH, { @@ -36,7 +37,7 @@ export const callGetTopLogEntryCategoriesAPI = async ( body: JSON.stringify( getLogEntryCategoriesRequestPayloadRT.encode({ data: { - logView: { type: 'log-view-reference', logViewId: sourceId }, + logView: logViewReference, timeRange: { startTime, endTime, diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results.ts index b4cfb301e040..f7e05c885f02 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results.ts @@ -7,6 +7,7 @@ import { useMemo, useState } from 'react'; +import { PersistedLogViewReference } from '../../../../common/log_views'; import { GetLogEntryCategoriesSuccessResponsePayload, GetLogEntryCategoryDatasetsSuccessResponsePayload, @@ -30,7 +31,7 @@ export const useLogEntryCategoriesResults = ({ endTime, onGetLogEntryCategoryDatasetsError, onGetTopLogEntryCategoriesError, - sourceId, + logViewReference, startTime, }: { categoriesCount: number; @@ -38,7 +39,7 @@ export const useLogEntryCategoriesResults = ({ endTime: number; onGetLogEntryCategoryDatasetsError?: (error: Error) => void; onGetTopLogEntryCategoriesError?: (error: Error) => void; - sourceId: string; + logViewReference: PersistedLogViewReference; startTime: number; }) => { const [sortOptions, setSortOptions] = useState({ @@ -56,7 +57,7 @@ export const useLogEntryCategoriesResults = ({ createPromise: async () => { return await callGetTopLogEntryCategoriesAPI( { - sourceId, + logViewReference, startTime, endTime, categoryCount: categoriesCount, @@ -79,7 +80,7 @@ export const useLogEntryCategoriesResults = ({ } }, }, - [categoriesCount, endTime, filteredDatasets, sourceId, startTime, sortOptions] + [categoriesCount, endTime, filteredDatasets, logViewReference, startTime, sortOptions] ); const [getLogEntryCategoryDatasetsRequest, getLogEntryCategoryDatasets] = useTrackedPromise( @@ -87,7 +88,7 @@ export const useLogEntryCategoriesResults = ({ cancelPreviousOn: 'creation', createPromise: async () => { return await callGetLogEntryCategoryDatasetsAPI( - { sourceId, startTime, endTime }, + { logViewReference, startTime, endTime }, services.http.fetch ); }, @@ -104,7 +105,7 @@ export const useLogEntryCategoriesResults = ({ } }, }, - [categoriesCount, endTime, sourceId, startTime] + [categoriesCount, endTime, logViewReference, startTime] ); const isLoadingTopLogEntryCategories = useMemo( diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_category_examples.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_category_examples.tsx index 010bcaa47942..8152cae42644 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_category_examples.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_category_examples.tsx @@ -6,6 +6,7 @@ */ import { useMemo, useState } from 'react'; +import { PersistedLogViewReference } from '../../../../common/log_views'; import { LogEntryCategoryExample } from '../../../../common/http_api'; import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; @@ -16,13 +17,13 @@ export const useLogEntryCategoryExamples = ({ categoryId, endTime, exampleCount, - sourceId, + logViewReference, startTime, }: { categoryId: number; endTime: number; exampleCount: number; - sourceId: string; + logViewReference: PersistedLogViewReference; startTime: number; }) => { const { services } = useKibanaContextForPlugin(); @@ -37,7 +38,7 @@ export const useLogEntryCategoryExamples = ({ createPromise: async () => { return await callGetLogEntryCategoryExamplesAPI( { - sourceId, + logViewReference, startTime, endTime, categoryId, @@ -50,7 +51,7 @@ export const useLogEntryCategoryExamples = ({ setLogEntryCategoryExamples(examples); }, }, - [categoryId, endTime, exampleCount, sourceId, startTime] + [categoryId, endTime, exampleCount, logViewReference, startTime] ); const isLoadingLogEntryCategoryExamples = useMemo( diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_providers.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_providers.tsx index f3ddc6c7cc57..7e1381cbd4f2 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_providers.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_providers.tsx @@ -6,6 +6,7 @@ */ import React from 'react'; +import { InlineLogViewSplashPage } from '../../../components/logging/inline_log_view_splash_page'; import { LogAnalysisSetupFlyoutStateProvider } from '../../../components/logging/log_analysis_setup/setup_flyout'; import { SourceLoadingPage } from '../../../components/source_loading_page'; import { LogEntryCategoriesModuleProvider } from '../../../containers/logs/log_analysis/modules/log_entry_categories'; @@ -16,8 +17,16 @@ import { useLogViewContext } from '../../../hooks/use_log_view'; import { ConnectedLogViewErrorPage } from '../shared/page_log_view_error'; export const LogEntryRatePageProviders: React.FunctionComponent = ({ children }) => { - const { hasFailedLoading, isLoading, isUninitialized, logViewId, resolvedLogView } = - useLogViewContext(); + const { + hasFailedLoading, + isLoading, + isUninitialized, + logViewReference, + resolvedLogView, + isPersistedLogView, + revertToDefaultLogView, + } = useLogViewContext(); + const { space } = useActiveKibanaSpace(); // This is a rather crude way of guarding the dependent providers against @@ -25,23 +34,28 @@ export const LogEntryRatePageProviders: React.FunctionComponent = ({ children }) // React concurrent mode and Suspense in order to handle that more gracefully. if (space == null) { return null; + } else if (!isPersistedLogView) { + return ; } else if (isLoading || isUninitialized) { return ; } else if (hasFailedLoading) { return ; } else if (resolvedLogView != null) { + if (logViewReference.type === 'log-view-inline') { + throw new Error('Logs ML features only support persisted Log Views'); + } return ( ) : null} diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/expanded_row.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/expanded_row.tsx index ef5db53ebdc3..493b5c4077c6 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/expanded_row.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/expanded_row.tsx @@ -28,7 +28,11 @@ export const AnomaliesTableExpandedRow: React.FunctionComponent<{ anomaly: LogEntryAnomaly; timeRange: TimeRange; }> = ({ anomaly, timeRange }) => { - const { logViewId } = useLogViewContext(); + const { logViewReference } = useLogViewContext(); + + if (logViewReference.type === 'log-view-inline') { + throw new Error('Logs ML features only support persisted Log Views'); + } const { getLogEntryExamples, @@ -39,7 +43,7 @@ export const AnomaliesTableExpandedRow: React.FunctionComponent<{ dataset: anomaly.dataset, endTime: anomaly.startTime + anomaly.duration, exampleCount: EXAMPLE_COUNT, - sourceId: logViewId, + logViewReference, startTime: anomaly.startTime, categoryId: isCategoryAnomaly(anomaly) ? anomaly.categoryId : undefined, }); diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies.ts index 7916cad0f1e0..ef39f6ac5f01 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies.ts @@ -6,6 +6,7 @@ */ import type { HttpHandler } from '@kbn/core/public'; +import { PersistedLogViewReference } from '../../../../../common/log_views'; import { getLogEntryAnomaliesRequestPayloadRT, getLogEntryAnomaliesSuccessReponsePayloadRT, @@ -15,7 +16,7 @@ import { decodeOrThrow } from '../../../../../common/runtime_types'; import { AnomaliesSort, Pagination } from '../../../../../common/log_analysis'; interface RequestArgs { - sourceId: string; + logViewReference: PersistedLogViewReference; startTime: number; endTime: number; sort: AnomaliesSort; @@ -24,13 +25,13 @@ interface RequestArgs { } export const callGetLogEntryAnomaliesAPI = async (requestArgs: RequestArgs, fetch: HttpHandler) => { - const { sourceId, startTime, endTime, sort, pagination, datasets } = requestArgs; + const { logViewReference, startTime, endTime, sort, pagination, datasets } = requestArgs; const response = await fetch(LOG_ANALYSIS_GET_LOG_ENTRY_ANOMALIES_PATH, { method: 'POST', body: JSON.stringify( getLogEntryAnomaliesRequestPayloadRT.encode({ data: { - logView: { type: 'log-view-reference', logViewId: sourceId }, + logView: logViewReference, timeRange: { startTime, endTime, diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies_datasets.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies_datasets.ts index 16a8092f290f..8915f514d1bf 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies_datasets.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies_datasets.ts @@ -6,6 +6,7 @@ */ import type { HttpHandler } from '@kbn/core/public'; +import { PersistedLogViewReference } from '../../../../../common/log_views'; import { decodeOrThrow } from '../../../../../common/runtime_types'; import { getLogEntryAnomaliesDatasetsRequestPayloadRT, @@ -14,7 +15,7 @@ import { } from '../../../../../common/http_api/log_analysis'; interface RequestArgs { - sourceId: string; + logViewReference: PersistedLogViewReference; startTime: number; endTime: number; } @@ -23,13 +24,13 @@ export const callGetLogEntryAnomaliesDatasetsAPI = async ( requestArgs: RequestArgs, fetch: HttpHandler ) => { - const { sourceId, startTime, endTime } = requestArgs; + const { logViewReference, startTime, endTime } = requestArgs; const response = await fetch(LOG_ANALYSIS_GET_LOG_ENTRY_ANOMALIES_DATASETS_PATH, { method: 'POST', body: JSON.stringify( getLogEntryAnomaliesDatasetsRequestPayloadRT.encode({ data: { - logView: { type: 'log-view-reference', logViewId: sourceId }, + logView: logViewReference, timeRange: { startTime, endTime, diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_examples.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_examples.ts index 0e44e5b02feb..75580caa6db8 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_examples.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_examples.ts @@ -6,6 +6,7 @@ */ import type { HttpHandler } from '@kbn/core/public'; +import { PersistedLogViewReference } from '../../../../../common/log_views'; import { getLogEntryExamplesRequestPayloadRT, @@ -15,7 +16,7 @@ import { import { decodeOrThrow } from '../../../../../common/runtime_types'; interface RequestArgs { - sourceId: string; + logViewReference: PersistedLogViewReference; startTime: number; endTime: number; dataset: string; @@ -24,7 +25,7 @@ interface RequestArgs { } export const callGetLogEntryExamplesAPI = async (requestArgs: RequestArgs, fetch: HttpHandler) => { - const { sourceId, startTime, endTime, dataset, exampleCount, categoryId } = requestArgs; + const { logViewReference, startTime, endTime, dataset, exampleCount, categoryId } = requestArgs; const response = await fetch(LOG_ANALYSIS_GET_LOG_ENTRY_RATE_EXAMPLES_PATH, { method: 'POST', body: JSON.stringify( @@ -32,7 +33,7 @@ export const callGetLogEntryExamplesAPI = async (requestArgs: RequestArgs, fetch data: { dataset, exampleCount, - logView: { type: 'log-view-reference', logViewId: sourceId }, + logView: logViewReference, timeRange: { startTime, endTime, diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_anomalies_results.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_anomalies_results.ts index 80123bb10e55..f68aa20d41f0 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_anomalies_results.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_anomalies_results.ts @@ -7,6 +7,7 @@ import { useMemo, useState, useCallback, useEffect, useReducer } from 'react'; import useMount from 'react-use/lib/useMount'; +import { PersistedLogViewReference } from '../../../../common/log_views'; import { useTrackedPromise, CanceledPromiseError } from '../../../utils/use_tracked_promise'; import { callGetLogEntryAnomaliesAPI } from './service_calls/get_log_entry_anomalies'; import { callGetLogEntryAnomaliesDatasetsAPI } from './service_calls/get_log_entry_anomalies_datasets'; @@ -137,7 +138,7 @@ const STATE_DEFAULTS: ReducerStateDefaults = { export const useLogEntryAnomaliesResults = ({ endTime, startTime, - sourceId, + logViewReference, defaultSortOptions, defaultPaginationOptions, onGetLogEntryAnomaliesDatasetsError, @@ -145,7 +146,7 @@ export const useLogEntryAnomaliesResults = ({ }: { endTime: number; startTime: number; - sourceId: string; + logViewReference: PersistedLogViewReference; defaultSortOptions: AnomaliesSort; defaultPaginationOptions: Pick; onGetLogEntryAnomaliesDatasetsError?: (error: Error) => void; @@ -183,7 +184,7 @@ export const useLogEntryAnomaliesResults = ({ } = reducerState; return await callGetLogEntryAnomaliesAPI( { - sourceId, + logViewReference, startTime: queryStartTime, endTime: queryEndTime, sort: sortOptions, @@ -216,7 +217,7 @@ export const useLogEntryAnomaliesResults = ({ }, }, [ - sourceId, + logViewReference, dispatch, reducerState.timeRange, reducerState.sortOptions, @@ -294,7 +295,7 @@ export const useLogEntryAnomaliesResults = ({ cancelPreviousOn: 'creation', createPromise: async () => { return await callGetLogEntryAnomaliesDatasetsAPI( - { sourceId, startTime, endTime }, + { logViewReference, startTime, endTime }, services.http.fetch ); }, @@ -311,7 +312,7 @@ export const useLogEntryAnomaliesResults = ({ } }, }, - [endTime, sourceId, startTime] + [endTime, logViewReference, startTime] ); const isLoadingDatasets = useMemo( diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_examples.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_examples.ts index 8f6269c46c3d..a678f5deaf07 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_examples.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_examples.ts @@ -6,6 +6,7 @@ */ import { useMemo, useState } from 'react'; +import { PersistedLogViewReference } from '../../../../common/log_views'; import { LogEntryExample } from '../../../../common/log_analysis'; import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; @@ -16,14 +17,14 @@ export const useLogEntryExamples = ({ dataset, endTime, exampleCount, - sourceId, + logViewReference, startTime, categoryId, }: { dataset: string; endTime: number; exampleCount: number; - sourceId: string; + logViewReference: PersistedLogViewReference; startTime: number; categoryId?: string; }) => { @@ -36,7 +37,7 @@ export const useLogEntryExamples = ({ createPromise: async () => { return await callGetLogEntryExamplesAPI( { - sourceId, + logViewReference, startTime, endTime, dataset, @@ -50,7 +51,7 @@ export const useLogEntryExamples = ({ setLogEntryExamples(examples); }, }, - [dataset, endTime, exampleCount, sourceId, startTime] + [dataset, endTime, exampleCount, logViewReference, startTime] ); const isLoadingLogEntryExamples = useMemo( diff --git a/x-pack/plugins/infra/public/pages/logs/page_content.tsx b/x-pack/plugins/infra/public/pages/logs/page_content.tsx index 0be59c785919..17def99bc161 100644 --- a/x-pack/plugins/infra/public/pages/logs/page_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/page_content.tsx @@ -12,7 +12,7 @@ import { Switch } from 'react-router-dom'; import { Route } from '@kbn/shared-ux-router'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { HeaderMenuPortal, useLinkProps } from '@kbn/observability-plugin/public'; -import { AlertDropdown } from '../../alerting/log_threshold'; +import { LazyAlertDropdownWrapper } from '../../alerting/log_threshold'; import { HelpCenterContent } from '../../components/help_center_content'; import { useReadOnlyBadge } from '../../hooks/use_readonly_badge'; import { HeaderActionMenuContext } from '../../utils/header_action_menu_provider'; @@ -21,9 +21,12 @@ import { LogEntryCategoriesPage } from './log_entry_categories'; import { LogEntryRatePage } from './log_entry_rate'; import { LogsSettingsPage } from './settings'; import { StreamPage } from './stream'; +import { isDevMode } from '../../utils/dev_mode'; +import { StateMachinePlayground } from '../../observability_logs/xstate_helpers'; import { NotFoundPage } from '../404'; export const LogsPageContent: React.FunctionComponent = () => { + const enableDeveloperRoutes = isDevMode(); const uiCapabilities = useKibana().services.application?.capabilities; const { setHeaderActionMenu, theme$ } = useContext(HeaderActionMenuContext); @@ -71,7 +74,7 @@ export const LogsPageContent: React.FunctionComponent = () => { {settingsTabTitle} - + { + {enableDeveloperRoutes && ( + + )} diff --git a/x-pack/plugins/infra/public/pages/logs/page_providers.tsx b/x-pack/plugins/infra/public/pages/logs/page_providers.tsx index 92db5f003932..c6548cfbd40a 100644 --- a/x-pack/plugins/infra/public/pages/logs/page_providers.tsx +++ b/x-pack/plugins/infra/public/pages/logs/page_providers.tsx @@ -7,16 +7,14 @@ import React from 'react'; import { LogAnalysisCapabilitiesProvider } from '../../containers/logs/log_analysis'; -import { useSourceId } from '../../containers/source_id'; import { useKibanaContextForPlugin } from '../../hooks/use_kibana'; import { LogViewProvider } from '../../hooks/use_log_view'; export const LogsPageProviders: React.FunctionComponent = ({ children }) => { - const [sourceId] = useSourceId(); const { services } = useKibanaContextForPlugin(); return ( - + {children} ); diff --git a/x-pack/plugins/infra/public/pages/logs/settings/inline_log_view_callout.tsx b/x-pack/plugins/infra/public/pages/logs/settings/inline_log_view_callout.tsx new file mode 100644 index 000000000000..ed332f97ea47 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/logs/settings/inline_log_view_callout.tsx @@ -0,0 +1,47 @@ +/* + * 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 { EuiButton } from '@elastic/eui'; +import { EuiCallOut } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; + +export const InlineLogViewCallout = ({ + revertToDefaultLogView, +}: { + revertToDefaultLogView: () => void; +}) => { + return ( + + <> +

+ {i18n.translate('xpack.infra.logs.settings.inlineLogViewCalloutDescription', { + defaultMessage: + 'An inline Log View is currently being used, changes will be synchronized to the URL, but they will not be persisted.', + })} +

+ + + + +
+ ); +}; diff --git a/x-pack/plugins/infra/public/pages/logs/settings/source_configuration_settings.tsx b/x-pack/plugins/infra/public/pages/logs/settings/source_configuration_settings.tsx index f826be82ac67..6ee20c00b9e6 100644 --- a/x-pack/plugins/infra/public/pages/logs/settings/source_configuration_settings.tsx +++ b/x-pack/plugins/infra/public/pages/logs/settings/source_configuration_settings.tsx @@ -28,6 +28,7 @@ import { LogColumnsConfigurationPanel } from './log_columns_configuration_panel' import { NameConfigurationPanel } from './name_configuration_panel'; import { LogSourceConfigurationFormErrors } from './source_configuration_form_errors'; import { useLogSourceConfigurationFormState } from './source_configuration_form_state'; +import { InlineLogViewCallout } from './inline_log_view_callout'; export const LogsSettingsPage = () => { const uiCapabilities = useKibana().services.application?.capabilities; @@ -46,8 +47,16 @@ export const LogsSettingsPage = () => { }, ]); - const { logView, hasFailedLoadingLogView, isLoading, isUninitialized, update, resolvedLogView } = - useLogViewContext(); + const { + logView, + hasFailedLoadingLogView, + isLoading, + isUninitialized, + update, + resolvedLogView, + isInlineLogView, + revertToDefaultLogView, + } = useLogViewContext(); const availableFields = useMemo( () => resolvedLogView?.fields.map((field) => field.name) ?? [], @@ -141,6 +150,14 @@ export const LogsSettingsPage = () => { ) : ( <> + {isInlineLogView && ( + + + + + + + )} ) : null} - + { const { startTimestamp, endTimestamp } = useLogPositionStateContext(); - const { logViewId } = useLogViewContext(); + const { logViewReference } = useLogViewContext(); if (!startTimestamp || !endTimestamp) { return null; @@ -35,7 +35,7 @@ const ViewLogInContext: React.FC = ({ children }) => { {children} @@ -45,7 +45,7 @@ const ViewLogInContext: React.FC = ({ children }) => { const LogEntriesStateProvider: React.FC<{ logStreamPageState: InitializedLogStreamPageState; }> = ({ children, logStreamPageState }) => { - const { logViewId } = useLogViewContext(); + const { logViewReference } = useLogViewContext(); const { startTimestamp, endTimestamp, targetPosition } = useLogPositionStateContext(); const { context: { parsedQuery }, @@ -58,7 +58,7 @@ const LogEntriesStateProvider: React.FC<{ return ( = ({ children, logStreamPageState }) => { - const { logViewId, logView } = useLogViewContext(); + const { logViewReference, logView } = useLogViewContext(); const { topCursor, bottomCursor, entries } = useLogStreamContext(); const serializedParsedQuery = useMemo( () => stringify(logStreamPageState.context.parsedQuery), @@ -80,7 +80,7 @@ const LogHighlightsState: React.FC<{ ); const highlightsProps = { - sourceId: logViewId, + logViewReference, sourceVersion: logView?.version, entriesStart: topCursor, entriesEnd: bottomCursor, diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx index 9b4a2ffb4185..15dbbcca7ce9 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx @@ -25,7 +25,7 @@ import { LogStream } from '../../../components/log_stream'; const MODAL_MARGIN = 25; export const PageViewLogInContext: React.FC = () => { - const [{ contextEntry, startTimestamp, endTimestamp, sourceId }, { setContextEntry }] = + const [{ contextEntry, startTimestamp, endTimestamp, logViewReference }, { setContextEntry }] = useViewLogInProviderContext(); const closeModal = useCallback(() => setContextEntry(undefined), [setContextEntry]); const { width: vw, height: vh } = useViewportDimensions(); @@ -56,7 +56,7 @@ export const PageViewLogInContext: React.FC = () => { { + public async getLogView(logViewReference: LogViewReference): Promise { + if (logViewReference.type === 'log-view-inline') { + return { + ...logViewReference, + origin: 'inline', + }; + } + + const { logViewId } = logViewReference; const response = await this.http.get(getLogViewUrl(logViewId)).catch((error) => { throw new FetchLogViewError(`Failed to fetch log view "${logViewId}": ${error}`); }); @@ -51,8 +61,8 @@ export class LogViewsClient implements ILogViewsClient { return data; } - public async getResolvedLogView(logViewId: string): Promise { - const logView = await this.getLogView(logViewId); + public async getResolvedLogView(logViewReference: LogViewReference): Promise { + const logView = await this.getLogView(logViewReference); const resolvedLogView = await this.resolveLogView(logView.id, logView.attributes); return resolvedLogView; } @@ -98,24 +108,44 @@ export class LogViewsClient implements ILogViewsClient { } public async putLogView( - logViewId: string, + logViewReference: LogViewReference, logViewAttributes: Partial ): Promise { - const response = await this.http - .put(getLogViewUrl(logViewId), { - body: JSON.stringify(putLogViewRequestPayloadRT.encode({ attributes: logViewAttributes })), - }) - .catch((error) => { - throw new PutLogViewError(`Failed to write log view "${logViewId}": ${error}`); - }); + if (logViewReference.type === 'log-view-inline') { + const { id } = logViewReference; + const attributes = decodeOrThrow( + rt.partial(logViewAttributesRT.type.props), + (message: string) => + new PutLogViewError(`Failed to decode inline log view "${id}": ${message}"`) + )(logViewAttributes); + return { + id, + attributes: { + ...logViewReference.attributes, + ...attributes, + }, + origin: 'inline', + }; + } else { + const { logViewId } = logViewReference; + const response = await this.http + .put(getLogViewUrl(logViewId), { + body: JSON.stringify( + putLogViewRequestPayloadRT.encode({ attributes: logViewAttributes }) + ), + }) + .catch((error) => { + throw new PutLogViewError(`Failed to write log view "${logViewId}": ${error}`); + }); - const { data } = decodeOrThrow( - getLogViewResponsePayloadRT, - (message: string) => - new PutLogViewError(`Failed to decode written log view "${logViewId}": ${message}"`) - )(response); + const { data } = decodeOrThrow( + getLogViewResponsePayloadRT, + (message: string) => + new PutLogViewError(`Failed to decode written log view "${logViewId}": ${message}"`) + )(response); - return data; + return data; + } } public async resolveLogView( diff --git a/x-pack/plugins/infra/public/services/log_views/types.ts b/x-pack/plugins/infra/public/services/log_views/types.ts index 9054ef79b4a4..6fd7a4d208b3 100644 --- a/x-pack/plugins/infra/public/services/log_views/types.ts +++ b/x-pack/plugins/infra/public/services/log_views/types.ts @@ -11,6 +11,7 @@ import { DataViewsContract } from '@kbn/data-views-plugin/public'; import { LogView, LogViewAttributes, + LogViewReference, LogViewStatus, ResolvedLogView, } from '../../../common/log_views'; @@ -28,9 +29,12 @@ export interface LogViewsServiceStartDeps { } export interface ILogViewsClient { - getLogView(logViewId: string): Promise; + getLogView(logViewReference: LogViewReference): Promise; getResolvedLogViewStatus(resolvedLogView: ResolvedLogView): Promise; - getResolvedLogView(logViewId: string): Promise; - putLogView(logViewId: string, logViewAttributes: Partial): Promise; + getResolvedLogView(logViewReference: LogViewReference): Promise; + putLogView( + logViewReference: LogViewReference, + logViewAttributes: Partial + ): Promise; resolveLogView(logViewId: string, logViewAttributes: LogViewAttributes): Promise; } diff --git a/x-pack/plugins/infra/public/utils/logs_overview_fetchers.ts b/x-pack/plugins/infra/public/utils/logs_overview_fetchers.ts index 825a070ed15d..511ef829553b 100644 --- a/x-pack/plugins/infra/public/utils/logs_overview_fetchers.ts +++ b/x-pack/plugins/infra/public/utils/logs_overview_fetchers.ts @@ -12,8 +12,9 @@ import { FetchDataParams, LogsFetchDataResponse, } from '@kbn/observability-plugin/public'; -import { DEFAULT_SOURCE_ID, TIMESTAMP_FIELD } from '../../common/constants'; +import { TIMESTAMP_FIELD } from '../../common/constants'; import { InfraClientStartDeps, InfraClientStartServicesAccessor } from '../types'; +import { DEFAULT_LOG_VIEW } from '../observability_logs/log_view_state/src/defaults'; interface StatsAggregation { buckets: Array<{ @@ -38,7 +39,7 @@ type StatsAndSeries = Pick; export function getLogsHasDataFetcher(getStartServices: InfraClientStartServicesAccessor) { return async () => { const [, , { logViews }] = await getStartServices(); - const resolvedLogView = await logViews.client.getResolvedLogView(DEFAULT_SOURCE_ID); + const resolvedLogView = await logViews.client.getResolvedLogView(DEFAULT_LOG_VIEW); const logViewStatus = await logViews.client.getResolvedLogViewStatus(resolvedLogView); const hasData = logViewStatus.index === 'available'; @@ -56,7 +57,7 @@ export function getLogsOverviewDataFetcher( ): FetchData { return async (params) => { const [, { data }, { logViews }] = await getStartServices(); - const resolvedLogView = await logViews.client.getResolvedLogView(DEFAULT_SOURCE_ID); + const resolvedLogView = await logViews.client.getResolvedLogView(DEFAULT_LOG_VIEW); const { stats, series } = await fetchLogsOverview( { diff --git a/x-pack/test/functional/apps/infra/link_to.ts b/x-pack/test/functional/apps/infra/link_to.ts index 890f85369877..6598e0e03d6b 100644 --- a/x-pack/test/functional/apps/infra/link_to.ts +++ b/x-pack/test/functional/apps/infra/link_to.ts @@ -10,6 +10,7 @@ import { URL } from 'url'; import { FtrProviderContext } from '../../ftr_provider_context'; const ONE_HOUR = 60 * 60 * 1000; +const LOG_VIEW_REFERENCE = '(logViewId:default,type:log-view-reference)'; export default ({ getPageObjects, getService }: FtrProviderContext) => { const pageObjects = getPageObjects(['common']); @@ -51,7 +52,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { expect(parsedUrl.searchParams.get('logPosition')).to.be( `(position:(tiebreaker:0,time:${timestamp}))` ); - expect(parsedUrl.searchParams.get('sourceId')).to.be('default'); + expect(parsedUrl.searchParams.get('logView')).to.be(LOG_VIEW_REFERENCE); expect(documentTitle).to.contain('Stream - Logs - Observability - Elastic'); }); });