diff --git a/x-pack/plugins/apm/public/components/app/ServiceDetails/AlertIntegrations/AlertingFlyout/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceDetails/AlertIntegrations/AlertingFlyout/index.tsx index a1ccb04e3c42a..ad3f1696ad5e3 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceDetails/AlertIntegrations/AlertingFlyout/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceDetails/AlertIntegrations/AlertingFlyout/index.tsx @@ -17,14 +17,15 @@ interface Props { export function AlertingFlyout(props: Props) { const { addFlyoutVisible, setAddFlyoutVisibility, alertType } = props; - - return alertType ? ( - - ) : null; + return ( + alertType && ( + + ) + ); } diff --git a/x-pack/plugins/apm/public/components/shared/EnvironmentFilter/index.tsx b/x-pack/plugins/apm/public/components/shared/EnvironmentFilter/index.tsx index d17b3b7689b19..5d03dbbfdf4ff 100644 --- a/x-pack/plugins/apm/public/components/shared/EnvironmentFilter/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/EnvironmentFilter/index.tsx @@ -7,7 +7,6 @@ import { EuiSelect } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; -import { useFetcher } from '../../../hooks/useFetcher'; import { useLocation } from '../../../hooks/useLocation'; import { useUrlParams } from '../../../hooks/useUrlParams'; import { history } from '../../../utils/history'; @@ -16,6 +15,7 @@ import { ENVIRONMENT_ALL, ENVIRONMENT_NOT_DEFINED } from '../../../../common/environment_filter_values'; +import { useEnvironments, ALL_OPTION } from '../../../hooks/useEnvironments'; function updateEnvironmentUrl( location: ReturnType, @@ -32,13 +32,6 @@ function updateEnvironmentUrl( }); } -const ALL_OPTION = { - value: ENVIRONMENT_ALL, - text: i18n.translate('xpack.apm.filter.environment.allLabel', { - defaultMessage: 'All' - }) -}; - const NOT_DEFINED_OPTION = { value: ENVIRONMENT_NOT_DEFINED, text: i18n.translate('xpack.apm.filter.environment.notDefinedLabel', { @@ -74,27 +67,15 @@ function getOptions(environments: string[]) { export const EnvironmentFilter: React.FC = () => { const location = useLocation(); - const { urlParams, uiFilters } = useUrlParams(); - const { start, end, serviceName } = urlParams; + const { uiFilters, urlParams } = useUrlParams(); const { environment } = uiFilters; - const { data: environments = [], status = 'loading' } = useFetcher( - callApmApi => { - if (start && end) { - return callApmApi({ - pathname: '/api/apm/ui_filters/environments', - params: { - query: { - start, - end, - serviceName - } - } - }); - } - }, - [start, end, serviceName] - ); + const { serviceName, start, end } = urlParams; + const { environments, status = 'loading' } = useEnvironments({ + serviceName, + start, + end + }); return ( + + setAlertParams( + 'environment', + e.target.value as ErrorRateAlertTriggerParams['environment'] + ) + } + compressed + /> + , + + setAlertParams('environment', e.target.value as Params['environment']) + } + compressed + /> + , env !== ENVIRONMENT_NOT_DEFINED) + .map(environment => ({ + value: environment, + text: environment + })); + + return [ALL_OPTION, ...environmentOptions]; +} + +export function useEnvironments({ + serviceName, + start, + end +}: { + serviceName?: string; + start?: string; + end?: string; +}) { + const { data: environments = [], status = 'loading' } = useFetcher(() => { + if (start && end) { + return callApmApi({ + pathname: '/api/apm/ui_filters/environments', + params: { + query: { + start, + end, + serviceName + } + } + }); + } + }, [start, end, serviceName]); + + const environmentOptions = useMemo( + () => getEnvironmentOptions(environments), + [environments] + ); + + return { environments, status, environmentOptions }; +} diff --git a/x-pack/plugins/apm/server/lib/alerts/register_error_rate_alert_type.ts b/x-pack/plugins/apm/server/lib/alerts/register_error_rate_alert_type.ts index d6242507e4011..690bfd793b2e2 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_error_rate_alert_type.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_error_rate_alert_type.ts @@ -8,6 +8,7 @@ import { schema, TypeOf } from '@kbn/config-schema'; import { Observable } from 'rxjs'; import { take } from 'rxjs/operators'; import { i18n } from '@kbn/i18n'; +import { ENVIRONMENT_ALL } from '../../../common/environment_filter_values'; import { AlertType, ALERT_TYPES_CONFIG } from '../../../common/alert_types'; import { ESSearchResponse, @@ -15,7 +16,8 @@ import { } from '../../../typings/elasticsearch'; import { PROCESSOR_EVENT, - SERVICE_NAME + SERVICE_NAME, + SERVICE_ENVIRONMENT } from '../../../common/elasticsearch_fieldnames'; import { AlertingPlugin } from '../../../../alerting/server'; import { getApmIndices } from '../settings/apm_indices/get_apm_indices'; @@ -30,7 +32,8 @@ const paramsSchema = schema.object({ serviceName: schema.string(), windowSize: schema.number(), windowUnit: schema.string(), - threshold: schema.number() + threshold: schema.number(), + environment: schema.string() }); const alertTypeConfig = ALERT_TYPES_CONFIG[AlertType.ErrorRate]; @@ -70,6 +73,11 @@ export function registerErrorRateAlertType({ savedObjectsClient: services.savedObjectsClient }); + const environmentTerm = + alertParams.environment === ENVIRONMENT_ALL + ? [] + : [{ term: { [SERVICE_ENVIRONMENT]: alertParams.environment } }]; + const searchParams = { index: indices['apm_oss.errorIndices'], size: 0, @@ -93,7 +101,8 @@ export function registerErrorRateAlertType({ term: { [SERVICE_NAME]: alertParams.serviceName } - } + }, + ...environmentTerm ] } }, diff --git a/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts b/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts index 2799da16cf6a9..c63d1bb3e1d69 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_alert_type.ts @@ -8,13 +8,15 @@ import { schema, TypeOf } from '@kbn/config-schema'; import { Observable } from 'rxjs'; import { take } from 'rxjs/operators'; import { i18n } from '@kbn/i18n'; +import { ENVIRONMENT_ALL } from '../../../common/environment_filter_values'; import { AlertType, ALERT_TYPES_CONFIG } from '../../../common/alert_types'; import { ESSearchResponse } from '../../../typings/elasticsearch'; import { PROCESSOR_EVENT, SERVICE_NAME, TRANSACTION_TYPE, - TRANSACTION_DURATION + TRANSACTION_DURATION, + SERVICE_ENVIRONMENT } from '../../../common/elasticsearch_fieldnames'; import { AlertingPlugin } from '../../../../alerting/server'; import { getApmIndices } from '../settings/apm_indices/get_apm_indices'; @@ -35,7 +37,8 @@ const paramsSchema = schema.object({ schema.literal('avg'), schema.literal('95th'), schema.literal('99th') - ]) + ]), + environment: schema.string() }); const alertTypeConfig = ALERT_TYPES_CONFIG[AlertType.TransactionDuration]; @@ -84,6 +87,11 @@ export function registerTransactionDurationAlertType({ savedObjectsClient: services.savedObjectsClient }); + const environmentTerm = + alertParams.environment === ENVIRONMENT_ALL + ? [] + : [{ term: { [SERVICE_ENVIRONMENT]: alertParams.environment } }]; + const searchParams = { index: indices['apm_oss.transactionIndices'], size: 0, @@ -112,7 +120,8 @@ export function registerTransactionDurationAlertType({ term: { [TRANSACTION_TYPE]: alertParams.transactionType } - } + }, + ...environmentTerm ] } }, diff --git a/x-pack/plugins/endpoint/common/generate_data.ts b/x-pack/plugins/endpoint/common/generate_data.ts index 1d5ea46dcc08b..52dac03754fd5 100644 --- a/x-pack/plugins/endpoint/common/generate_data.ts +++ b/x-pack/plugins/endpoint/common/generate_data.ts @@ -504,10 +504,15 @@ export class EndpointDocGenerator { * Generates an Ingest `datasource` that includes the Endpoint Policy data */ public generatePolicyDatasource(): PolicyData { + const created = new Date(Date.now() - 8.64e7).toISOString(); // 24h ago return { id: this.seededUUIDv4(), name: 'Endpoint Policy', description: 'Policy to protect the worlds data', + created_at: created, + created_by: 'elastic', + updated_at: new Date().toISOString(), + updated_by: 'elastic', config_id: this.seededUUIDv4(), enabled: true, output_id: '', diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_details/index.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_details/index.test.ts index a24687ebbcbbc..dd4e63c95ee8f 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_details/index.test.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_details/index.test.ts @@ -28,6 +28,10 @@ describe('policy details: ', () => { id: '', name: '', description: '', + created_at: '', + created_by: '', + updated_at: '', + updated_by: '', config_id: '', enabled: true, output_id: '', diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_details/middleware.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_details/middleware.ts index 7f17f5381fbda..d82273bfdb221 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_details/middleware.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_details/middleware.ts @@ -5,13 +5,18 @@ */ import { ImmutableMiddlewareFactory, PolicyDetailsState, UpdatePolicyResponse } from '../../types'; -import { policyIdFromParams, isOnPolicyDetailsPage, policyDetails } from './selectors'; +import { + policyIdFromParams, + isOnPolicyDetailsPage, + policyDetails, + policyDetailsForUpdate, +} from './selectors'; import { sendGetDatasource, sendGetFleetAgentStatusForConfig, sendPutDatasource, } from '../policy_list/services/ingest'; -import { PolicyData } from '../../../../../common/types'; +import { NewPolicyData, PolicyData } from '../../../../../common/types'; import { factory as policyConfigFactory } from '../../../../../common/models/policy_config'; export const policyDetailsMiddlewareFactory: ImmutableMiddlewareFactory = coreStart => { @@ -71,7 +76,8 @@ export const policyDetailsMiddlewareFactory: ImmutableMiddlewareFactory) => state.policyItem; +/** + * Return only the policy structure accepted for update/create + */ +export const policyDetailsForUpdate: ( + state: Immutable +) => Immutable | undefined = createSelector(policyDetails, policy => { + if (policy) { + const { id, revision, created_by, created_at, updated_by, updated_at, ...newPolicy } = policy; + return newPolicy; + } +}); + /** Returns a boolean of whether the user is on the policy details page or not */ export const isOnPolicyDetailsPage = (state: Immutable) => { if (state.location) { diff --git a/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.test.ts b/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.test.ts index bff799798ff6e..d319ba2beddf9 100644 --- a/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.test.ts +++ b/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.test.ts @@ -11,6 +11,10 @@ describe('Ingest Manager - storedDatasourceToAgentDatasource', () => { id: 'some-uuid', name: 'mock-datasource', description: '', + created_at: '', + created_by: '', + updated_at: '', + updated_by: '', config_id: '', enabled: true, output_id: '', diff --git a/x-pack/plugins/ingest_manager/common/types/models/datasource.ts b/x-pack/plugins/ingest_manager/common/types/models/datasource.ts index ca61a93d9be93..42ce69a5707cb 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/datasource.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/datasource.ts @@ -58,4 +58,8 @@ export interface Datasource extends Omit { id: string; inputs: DatasourceInput[]; revision: number; + updated_at: string; + updated_by: string; + created_at: string; + created_by: string; } diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/edit_datasource_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/edit_datasource_page/index.tsx index 5f3d59ad60f1f..619f836cfc1cc 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/edit_datasource_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/edit_datasource_page/index.tsx @@ -78,7 +78,16 @@ export const EditDatasourcePage: React.FunctionComponent = () => { setAgentConfig(agentConfigData.item); } if (datasourceData?.item) { - const { id, revision, inputs, ...restOfDatasource } = datasourceData.item; + const { + id, + revision, + inputs, + created_by, + created_at, + updated_by, + updated_at, + ...restOfDatasource + } = datasourceData.item; // Remove `agent_stream` from all stream info, we assign this after saving const newDatasource = { ...restOfDatasource, diff --git a/x-pack/plugins/ingest_manager/server/routes/agent_config/handlers.ts b/x-pack/plugins/ingest_manager/server/routes/agent_config/handlers.ts index 023d465c9cda9..f74f898b2baf9 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent_config/handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent_config/handlers.ts @@ -100,14 +100,14 @@ export const createAgentConfigHandler: RequestHandler< TypeOf > = async (context, request, response) => { const soClient = context.core.savedObjects.client; - const user = await appContextService.getSecurity()?.authc.getCurrentUser(request); + const user = (await appContextService.getSecurity()?.authc.getCurrentUser(request)) || undefined; const withSysMonitoring = request.query.sys_monitoring ?? false; try { // eslint-disable-next-line prefer-const let [agentConfig, newSysDatasource] = await Promise.all( [ agentConfigService.create(soClient, request.body, { - user: user || undefined, + user, }), // If needed, retrieve System package information and build a new Datasource for the system package // NOTE: we ignore failures in attempting to create datasource, since config might have been created @@ -123,7 +123,7 @@ export const createAgentConfigHandler: RequestHandler< // Create the system monitoring datasource and add it to config. if (withSysMonitoring && newSysDatasource !== undefined && agentConfig !== undefined) { newSysDatasource.config_id = agentConfig.id; - const sysDatasource = await datasourceService.create(soClient, newSysDatasource); + const sysDatasource = await datasourceService.create(soClient, newSysDatasource, { user }); if (sysDatasource) { agentConfig = await agentConfigService.assignDatasources(soClient, agentConfig.id, [ diff --git a/x-pack/plugins/ingest_manager/server/saved_objects.ts b/x-pack/plugins/ingest_manager/server/saved_objects/index.ts similarity index 95% rename from x-pack/plugins/ingest_manager/server/saved_objects.ts rename to x-pack/plugins/ingest_manager/server/saved_objects/index.ts index 89d8b9e173ffe..d3835c5980f39 100644 --- a/x-pack/plugins/ingest_manager/server/saved_objects.ts +++ b/x-pack/plugins/ingest_manager/server/saved_objects/index.ts @@ -5,7 +5,7 @@ */ import { SavedObjectsServiceSetup, SavedObjectsType } from 'kibana/server'; -import { EncryptedSavedObjectsPluginSetup } from '../../encrypted_saved_objects/server'; +import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server'; import { OUTPUT_SAVED_OBJECT_TYPE, AGENT_CONFIG_SAVED_OBJECT_TYPE, @@ -16,7 +16,8 @@ import { AGENT_ACTION_SAVED_OBJECT_TYPE, ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, GLOBAL_SETTINGS_SAVED_OBJET_TYPE, -} from './constants'; +} from '../constants'; +import { migrateDatasourcesToV790 } from './migrations/datasources_v790'; /* * Saved object types and mappings @@ -218,8 +219,15 @@ const savedObjectTypes: { [key: string]: SavedObjectsType } = { }, }, revision: { type: 'integer' }, + updated_at: { type: 'date' }, + updated_by: { type: 'keyword' }, + created_at: { type: 'date' }, + created_by: { type: 'keyword' }, }, }, + migrations: { + '7.9.0': migrateDatasourcesToV790, + }, }, [PACKAGES_SAVED_OBJECT_TYPE]: { name: PACKAGES_SAVED_OBJECT_TYPE, diff --git a/x-pack/plugins/ingest_manager/server/saved_objects/migrations/datasources_v790.ts b/x-pack/plugins/ingest_manager/server/saved_objects/migrations/datasources_v790.ts new file mode 100644 index 0000000000000..1f16af6f83065 --- /dev/null +++ b/x-pack/plugins/ingest_manager/server/saved_objects/migrations/datasources_v790.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectMigrationFn } from 'kibana/server'; +import { cloneDeep } from 'lodash'; +import { Datasource } from '../../types/models'; + +type Pre790Datasource = Exclude< + Datasource, + 'created_at' | 'created_by' | 'updated_at' | 'updated_by' +>; + +export const migrateDatasourcesToV790: SavedObjectMigrationFn< + Pre790Datasource, + Datasource +> = doc => { + const updatedDatasource = cloneDeep(doc); + const defDate = new Date().toISOString(); + + updatedDatasource.attributes.created_by = 'system'; + updatedDatasource.attributes.created_at = updatedDatasource?.updated_at ?? defDate; + updatedDatasource.attributes.updated_by = 'system'; + updatedDatasource.attributes.updated_at = updatedDatasource?.updated_at ?? defDate; + + return updatedDatasource; +}; diff --git a/x-pack/plugins/ingest_manager/server/services/app_context.ts b/x-pack/plugins/ingest_manager/server/services/app_context.ts index e5bc082f6f0fc..91b09d651bf5c 100644 --- a/x-pack/plugins/ingest_manager/server/services/app_context.ts +++ b/x-pack/plugins/ingest_manager/server/services/app_context.ts @@ -21,7 +21,7 @@ class AppContextService { private isProductionMode: boolean = false; private kibanaVersion: string | undefined; private cloud?: CloudSetup; - private logger?: Logger; + private logger: Logger | undefined; private httpSetup?: HttpServiceSetup; public async start(appContext: IngestManagerAppContext) { @@ -53,7 +53,7 @@ class AppContextService { public getSecurity() { if (!this.security) { - throw new Error('Secury service not set.'); + throw new Error('Security service not set.'); } return this.security; } @@ -63,6 +63,9 @@ class AppContextService { } public getLogger() { + if (!this.logger) { + throw new Error('Logger not set.'); + } return this.logger; } diff --git a/x-pack/plugins/ingest_manager/server/services/datasource.ts b/x-pack/plugins/ingest_manager/server/services/datasource.ts index 0497bc5a2b541..8f9b4825ab5d7 100644 --- a/x-pack/plugins/ingest_manager/server/services/datasource.ts +++ b/x-pack/plugins/ingest_manager/server/services/datasource.ts @@ -31,11 +31,16 @@ class DatasourceService { datasource: NewDatasource, options?: { id?: string; user?: AuthenticatedUser } ): Promise { + const isoDate = new Date().toISOString(); const newSo = await soClient.create>( SAVED_OBJECT_TYPE, { ...datasource, revision: 1, + created_at: isoDate, + created_by: options?.user?.username ?? 'system', + updated_at: isoDate, + updated_by: options?.user?.username ?? 'system', }, options ); @@ -134,6 +139,8 @@ class DatasourceService { await soClient.update(SAVED_OBJECT_TYPE, id, { ...datasource, revision: oldDatasource.revision + 1, + updated_at: new Date().toISOString(), + updated_by: options?.user?.username ?? 'system', }); // Bump revision of associated agent config diff --git a/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.ts b/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.ts index 6cdcb8782f38e..d220ecfc62bb8 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.ts @@ -13,6 +13,7 @@ import * as Registry from '../../registry'; import { loadFieldsFromYaml, Fields, Field } from '../../fields/field'; import { getPackageKeysByStatus } from '../../packages/get'; import { InstallationStatus, RegistryPackage, CallESAsCurrentUser } from '../../../../types'; +import { appContextService } from '../../../../services'; interface FieldFormatMap { [key: string]: FieldFormatMapItem; @@ -366,10 +367,11 @@ const getFieldFormatParams = (field: Field): FieldFormatParams => { return params; }; -export const ensureDefaultIndices = async (callCluster: CallESAsCurrentUser) => +export const ensureDefaultIndices = async (callCluster: CallESAsCurrentUser) => { // create placeholder indices to supress errors in the kibana Dashboards app // that no matching indices exist https://github.com/elastic/kibana/issues/62343 - Promise.all( + const logger = appContextService.getLogger(); + return Promise.all( Object.keys(IndexPatternType).map(async indexPattern => { const defaultIndexPatternName = indexPattern + INDEX_PATTERN_PLACEHOLDER_SUFFIX; const indexExists = await callCluster('indices.exists', { index: defaultIndexPatternName }); @@ -386,9 +388,9 @@ export const ensureDefaultIndices = async (callCluster: CallESAsCurrentUser) => }, }); } catch (putErr) { - // throw new Error(`${defaultIndexPatternName} could not be created`); - throw new Error(putErr); + logger.error(`${defaultIndexPatternName} could not be created`); } } }) ); +}; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/_index.scss b/x-pack/plugins/ml/public/application/data_frame_analytics/_index.scss index 83314a74331fd..89a0018f90401 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/_index.scss +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/_index.scss @@ -1,5 +1,4 @@ @import 'pages/analytics_exploration/components/regression_exploration/index'; -@import 'pages/analytics_exploration/components/classification_exploration/index'; @import 'pages/analytics_management/components/analytics_list/index'; @import 'pages/analytics_management/components/create_analytics_form/index'; @import 'pages/analytics_management/components/create_analytics_flyout/index'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/_classification_exploration.scss b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/_classification_exploration.scss index 1141dddf398b0..102f6630f2ee2 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/_classification_exploration.scss +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/_classification_exploration.scss @@ -1,4 +1,35 @@ -.euiFormRow.mlDataFrameAnalyticsClassification__actualLabel { - padding-top: $euiSize * 4; +/* + Workaround for EuiDataGrid within a Flex Layout, + this tricks browsers treating the width as a px value instead of % +*/ +.mlDataFrameAnalyticsClassification { + width: 100%; } +.mlDataFrameAnalyticsClassification__confusionMatrix { + padding: 0 5%; +} + +/* + The following two classes are a workaround to avoid having EuiDataGrid in a flex layout + and just uses a legacy approach for a two column layout so we don't break IE11. +*/ +.mlDataFrameAnalyticsClassification__confusionMatrix:after { + content: ""; + display: table; + clear: both; +} + +.mlDataFrameAnalyticsClassification__actualLabel { + float: left; + width: 80px; + padding-top: $euiSize * 4 + $euiSizeXS; +} + +/* + Gives EuiDataGrid a min-width of 480px, otherwise the columns options will disappear if you hide all columns. +*/ +.mlDataFrameAnalyticsClassification__dataGridMinWidth { + min-width: 480px; + width: 100%; +} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/_index.scss b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/_index.scss deleted file mode 100644 index 88edd92951d41..0000000000000 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'classification_exploration'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx index af90547606f82..45f883c4ccd94 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import './_classification_exploration.scss'; + import React, { FC, useState, useEffect, Fragment } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -12,7 +14,6 @@ import { EuiDataGrid, EuiFlexGroup, EuiFlexItem, - EuiFormRow, EuiIconTip, EuiPanel, EuiSpacer, @@ -46,8 +47,6 @@ import { getTrailingControlColumns, } from './column_data'; -const defaultPanelWidth = 500; - interface Props { jobConfig: DataFrameAnalyticsConfig; jobStatus?: DATA_FRAME_TASK_STATE; @@ -104,7 +103,6 @@ export const EvaluatePanel: FC = ({ jobConfig, jobStatus, searchQuery }) const [docsCount, setDocsCount] = useState(null); const [error, setError] = useState(null); const [dataSubsetTitle, setDataSubsetTitle] = useState(SUBSET_TITLE.ENTIRE); - const [panelWidth, setPanelWidth] = useState(defaultPanelWidth); // Column visibility const [visibleColumns, setVisibleColumns] = useState(() => columns.map(({ id }: { id: string }) => id) @@ -168,24 +166,6 @@ export const EvaluatePanel: FC = ({ jobConfig, jobStatus, searchQuery }) } }; - const resizeHandler = () => { - const tablePanelWidth: number = - document.getElementById('mlDataFrameAnalyticsTableResultsPanel')?.clientWidth || - defaultPanelWidth; - // Keep the evaluate panel width slightly smaller than the results table - // to ensure results table can resize correctly. Temporary workaround DataGrid issue with flex - const newWidth = tablePanelWidth - 8; - setPanelWidth(newWidth); - }; - - useEffect(() => { - window.addEventListener('resize', resizeHandler); - resizeHandler(); - return () => { - window.removeEventListener('resize', resizeHandler); - }; - }, []); - useEffect(() => { if (confusionMatrixData.length > 0) { const { columns: derivedColumns, columnData } = getColumnData(confusionMatrixData); @@ -310,158 +290,135 @@ export const EvaluatePanel: FC = ({ jobConfig, jobStatus, searchQuery }) return ( - - - - - - - {i18n.translate( - 'xpack.ml.dataframe.analytics.classificationExploration.evaluateJobIdTitle', - { - defaultMessage: 'Evaluation of classification job ID {jobId}', - values: { jobId: jobConfig.id }, - } - )} - - - - {jobStatus !== undefined && ( - - {getTaskStateBadge(jobStatus)} - - )} - - - - - +
+ + + + {i18n.translate( - 'xpack.ml.dataframe.analytics.classificationExploration.classificationDocsLink', + 'xpack.ml.dataframe.analytics.classificationExploration.evaluateJobIdTitle', { - defaultMessage: 'Classification evaluation docs ', + defaultMessage: 'Evaluation of classification job ID {jobId}', + values: { jobId: jobConfig.id }, } )} - - - - - {error !== null && ( - - + + - )} - {error === null && ( - + {jobStatus !== undefined && ( - - - {getHelpText(dataSubsetTitle)} - - - - - + {getTaskStateBadge(jobStatus)} - {docsCount !== null && ( + )} + + + + {i18n.translate( + 'xpack.ml.dataframe.analytics.classificationExploration.classificationDocsLink', + { + defaultMessage: 'Classification evaluation docs ', + } + )} + + + +
+ {error !== null && } + {error === null && ( + +
+ + + {getHelpText(dataSubsetTitle)} + - - - + - )} - {/* BEGIN TABLE ELEMENTS */} - - - - +
+ {docsCount !== null && ( + + + + )} + {/* BEGIN TABLE ELEMENTS */} + +
+
+ + + +
+
+ {columns.length > 0 && columnsData.length > 0 && ( + <> +
+ + + +
+ + - - - - - {columns.length > 0 && columnsData.length > 0 && ( - - - - - - - - - - - - - )} - - - - - )} - {/* END TABLE ELEMENTS */} - + columns={shownColumns} + columnVisibility={{ visibleColumns, setVisibleColumns }} + rowCount={rowCount} + renderCellValue={renderCellValue} + inMemory={{ level: 'sorting' }} + toolbarVisibility={{ + showColumnSelector: true, + showStyleSelector: false, + showFullScreenSelector: false, + showSortSelector: false, + }} + popoverContents={popoverContents} + gridStyle={{ rowHover: 'none' }} + trailingControlColumns={ + showTrailingColumns === true && showFullColumns === false + ? getTrailingControlColumns(extraColumns, setShowFullColumns) + : undefined + } + /> + + )} +
+
+
+ )} + {/* END TABLE ELEMENTS */}
); }; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 5cfe4f28d2339..7e1e8dea755df 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -4217,7 +4217,6 @@ "xpack.apm.fetcher.error.status": "エラー", "xpack.apm.fetcher.error.title": "リソースの取得中にエラーが発生しました", "xpack.apm.fetcher.error.url": "URL", - "xpack.apm.filter.environment.allLabel": "すべて", "xpack.apm.filter.environment.label": "環境", "xpack.apm.filter.environment.notDefinedLabel": "未定義", "xpack.apm.filter.environment.selectEnvironmentLabel": "環境を選択", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index fc2f2748c57f1..327bda072c1ab 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -4218,7 +4218,6 @@ "xpack.apm.fetcher.error.status": "错误", "xpack.apm.fetcher.error.title": "提取资源时出错", "xpack.apm.fetcher.error.url": "URL", - "xpack.apm.filter.environment.allLabel": "全部", "xpack.apm.filter.environment.label": "环境", "xpack.apm.filter.environment.notDefinedLabel": "未定义", "xpack.apm.filter.environment.selectEnvironmentLabel": "选择环境",