diff --git a/src/plugins/visualizations/public/embeddable/state.ts b/src/plugins/visualizations/public/embeddable/state.ts index ca0721a3a8395..52641703e04b6 100644 --- a/src/plugins/visualizations/public/embeddable/state.ts +++ b/src/plugins/visualizations/public/embeddable/state.ts @@ -112,6 +112,9 @@ export const deserializeSavedObjectState = async ({ enhancements, uiState, timeRange, + title: embeddableTitle, + description: embeddableDescription, + hidePanelTitles, }: VisualizeSavedObjectInputState) => { // Load a saved visualization from the library const { @@ -137,6 +140,8 @@ export const deserializeSavedObjectState = async ({ }, savedObjectId ); + const panelTitle = embeddableTitle ?? title; + const panelDescription = embeddableDescription ?? description; return { savedVis: { title, @@ -149,8 +154,9 @@ export const deserializeSavedObjectState = async ({ savedSearchId, }, }, - title, - description, + title: panelTitle, + description: panelDescription, + hidePanelTitles, savedObjectId, savedObjectProperties, linkedToLibrary: true, @@ -188,6 +194,7 @@ export const serializeState: (props: { if (linkedToLibrary) { return { rawState: { + ...titlesWithDefaults, savedObjectId: id, ...(enhancements ? { enhancements } : {}), ...(!isEmpty(serializedVis.uiState) ? { uiState: serializedVis.uiState } : {}), diff --git a/test/functional/apps/visualize/group3/_add_to_dashboard.ts b/test/functional/apps/visualize/group3/_add_to_dashboard.ts index 77125bc372934..d0921f5ab1d7d 100644 --- a/test/functional/apps/visualize/group3/_add_to_dashboard.ts +++ b/test/functional/apps/visualize/group3/_add_to_dashboard.ts @@ -13,6 +13,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const dashboardExpect = getService('dashboardExpect'); const dashboardPanelActions = getService('dashboardPanelActions'); + const dashboardCustomizePanel = getService('dashboardCustomizePanel'); const testSubjects = getService('testSubjects'); const listingTable = getService('listingTable'); @@ -287,5 +288,24 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await dashboardPanelActions.expectLinkedToLibrary('Neat Saved Vis 2 Copy'); }); + + it('should persist correctly panel title on a by reference visualization', async () => { + await dashboard.navigateToApp(); + + await dashboard.clickNewDashboard(); + await dashboard.addVisualizations(['Visualization AreaChart']); + + await dashboardPanelActions.customizePanel(); + await dashboardCustomizePanel.setCustomPanelTitle('My New panel title'); + await dashboardCustomizePanel.clickSaveButton(); + + await dashboard.saveDashboard('My Very Entitled Dashboard'); + + await dashboard.gotoDashboardLandingPage(); + await listingTable.clickItemLink('dashboard', 'My Very Entitled Dashboard'); + + const [newPanelTitle] = await dashboard.getPanelTitles(); + expect(newPanelTitle).to.equal('My New panel title'); + }); }); } diff --git a/x-pack/plugins/alerting/README.md b/x-pack/plugins/alerting/README.md index b14f2e30202b7..5139b05d4f4fa 100644 --- a/x-pack/plugins/alerting/README.md +++ b/x-pack/plugins/alerting/README.md @@ -92,7 +92,7 @@ The following table describes the properties of the `options` object. |executor|This is where the code for the rule type lives. This is a function to be called when executing a rule on an interval basis. For full details, see the executor section below.|Function| |producer|The id of the application producing this rule type.|string| |minimumLicenseRequired|The value of a minimum license. Most of the rules are licensed as "basic".|string| -|ruleTaskTimeout|(Optional) The length of time a rule can run before being cancelled due to timeout. If not specified, the default value of "5m" is used.|string| +|ruleTaskTimeout|(Optional) The length of time a rule can run before being cancelled due to timeout. If not specified, the default value of "5m" is used. Requests made to Elasticsearch will also receive the same timeout configuration, up to 5m.|string| |cancelAlertsOnRuleTimeout|(Optional) Whether to skip writing alerts and scheduling actions if a rule execution is cancelled due to timeout. If not specified, the default value of "true" is used.|boolean| |useSavedObjectReferences.extractReferences|(Optional) When developing a rule type, you can choose to implement hooks for extracting saved object references from rule parameters. This hook will be invoked when a rule is created or updated. Implementing this hook is optional, but if an extract hook is implemented, an inject hook must also be implemented.|Function |useSavedObjectReferences.injectReferences|(Optional) When developing a rule type, you can choose to implement hooks for injecting saved object references into rule parameters. This hook will be invoked when a rule is retrieved (get or find). Implementing this hook is optional, but if an inject hook is implemented, an extract hook must also be implemented.|Function diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts index 2ae05e4c86227..981e89c8d8ec5 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts @@ -120,30 +120,10 @@ export class RiskScoreDataClient { const oldComponentTemplateExists = await esClient.cluster.existsComponentTemplate({ name: mappingComponentName, }); - // If present then copy the contents to a new component template with the namespace in the name if (oldComponentTemplateExists) { - const oldComponentTemplateResponse = await esClient.cluster.getComponentTemplate( - { - name: mappingComponentName, - }, - { ignore: [404] } - ); - const oldComponentTemplate = oldComponentTemplateResponse?.component_templates[0]; - const newComponentTemplateName = nameSpaceAwareMappingsComponentName(namespace); - await esClient.cluster.putComponentTemplate({ - name: newComponentTemplateName, - body: oldComponentTemplate.component_template, - }); + await this.updateComponentTemplateNamewithNamespace(namespace); } - // Delete the component template without the namespace in the name - await esClient.cluster.deleteComponentTemplate( - { - name: mappingComponentName, - }, - { ignore: [404] } - ); - // Update the new component template with the required data await Promise.all([ createOrUpdateComponentTemplate({ @@ -188,6 +168,14 @@ export class RiskScoreDataClient { }, }); + // Delete the component template without the namespace in the name + await esClient.cluster.deleteComponentTemplate( + { + name: mappingComponentName, + }, + { ignore: [404] } + ); + await createDataStream({ logger: this.options.logger, esClient, @@ -327,4 +315,20 @@ export class RiskScoreDataClient { { logger: this.options.logger } ); } + + private async updateComponentTemplateNamewithNamespace(namespace: string): Promise { + const esClient = this.options.esClient; + const oldComponentTemplateResponse = await esClient.cluster.getComponentTemplate( + { + name: mappingComponentName, + }, + { ignore: [404] } + ); + const oldComponentTemplate = oldComponentTemplateResponse?.component_templates[0]; + const newComponentTemplateName = nameSpaceAwareMappingsComponentName(namespace); + await esClient.cluster.putComponentTemplate({ + name: newComponentTemplateName, + body: oldComponentTemplate.component_template, + }); + } } diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/init_and_status_apis.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/init_and_status_apis.ts index 9483343436018..bbcfd976abeb7 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/init_and_status_apis.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/init_and_status_apis.ts @@ -539,6 +539,86 @@ export default ({ getService }: FtrProviderContext) => { firstResponse?.saved_objects?.[0]?.id ); }); + + it('should update the existing component template and index template without any errors', async () => { + const componentTemplateName = '.risk-score-mappings'; + const indexTemplateName = '.risk-score.risk-score-default-index-template'; + const newComponentTemplateName = '.risk-score-mappings-default'; + + // Call API to put the component template and index template + + await es.cluster.putComponentTemplate({ + name: componentTemplateName, + body: { + template: { + settings: { + number_of_shards: 1, + }, + mappings: { + properties: { + timestamp: { + type: 'date', + }, + user: { + properties: { + id: { + type: 'keyword', + }, + name: { + type: 'text', + }, + }, + }, + }, + }, + }, + version: 1, + }, + }); + + // Call an API to put the index template + + await es.indices.putIndexTemplate({ + name: indexTemplateName, + body: { + index_patterns: [indexTemplateName], + composed_of: [componentTemplateName], + template: { + settings: { + number_of_shards: 1, + }, + mappings: { + properties: { + timestamp: { + type: 'date', + }, + user: { + properties: { + id: { + type: 'keyword', + }, + name: { + type: 'text', + }, + }, + }, + }, + }, + }, + }, + }); + + const response = await riskEngineRoutes.init(); + expect(response.status).to.eql(200); + expect(response.body.result.errors).to.eql([]); + + const response2 = await es.cluster.getComponentTemplate({ + name: newComponentTemplateName, + }); + expect(response2.component_templates.length).to.eql(1); + expect(response2.component_templates[0].name).to.eql(newComponentTemplateName); + }); + // Failing: See https://github.com/elastic/kibana/issues/191637 describe.skip('remove legacy risk score transform', function () { this.tags('skipFIPS');