From bf9927cadc0bd3476c0cf52950ae75c4aa71e63e Mon Sep 17 00:00:00 2001 From: Agustina Nahir Ruidiaz <61565784+agusruidiazgd@users.noreply.github.com> Date: Fri, 24 Jan 2025 13:47:41 +0100 Subject: [PATCH] [Security Solution] Connector selector onboarding (#203742) ## Summary Summarize your PR. If it involves visual changes include a screenshot or gif. https://github.com/user-attachments/assets/6d7527d1-dc8d-4f3a-9b03-cfd0022701d2 ### Checklist Check the PR satisfies following conditions. Reviewers should verify this PR satisfies this list as well. - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --------- Co-authored-by: Elastic Machine Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> (cherry picked from commit 86666bf790c87ee8bde353d53dcf5413e80e50e5) # Conflicts: # .github/CODEOWNERS # x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/connector_setup.tsx --- .github/CODEOWNERS | 188 ++++++++++++++++- package.json | 1 + tsconfig.base.json | 2 + .../security/packages/connectors/index.ts | 9 + .../packages/connectors/jest.config.js | 12 ++ .../security/packages/connectors/kibana.jsonc | 9 + .../security/packages/connectors/package.json | 7 + .../src/connector_selector.styles.ts | 41 ++++ .../connectors/src/connector_selector.tsx | 145 +++++++++++++ .../packages/connectors/src/constants.ts | 8 + .../packages/connectors/src/translations.ts | 36 ++++ .../packages/connectors/tsconfig.json | 19 ++ .../components/hooks/use_stored_state.ts | 7 + .../cards/assistant/assistant_card.tsx | 113 +++++++++- .../cards/assistant/translations.ts | 2 +- .../common/connectors/connector_cards.tsx | 150 ++++--------- .../connectors/connector_selector_panel.tsx | 50 +++++ .../connector_selector_with_icon.tsx | 118 +++++++++++ .../common/connectors/connector_setup.tsx | 197 +++++++----------- .../connectors/create_connector_popover.tsx | 59 ------ .../connectors/hooks/use_load_action_types.ts | 6 +- .../cards/common/connectors/translations.ts | 17 +- .../cards/common/connectors/types.ts | 15 ++ .../ai_connector/ai_connector_card.tsx | 15 +- .../plugins/security_solution/tsconfig.json | 1 + yarn.lock | 4 + 26 files changed, 921 insertions(+), 310 deletions(-) create mode 100644 x-pack/solutions/security/packages/connectors/index.ts create mode 100644 x-pack/solutions/security/packages/connectors/jest.config.js create mode 100644 x-pack/solutions/security/packages/connectors/kibana.jsonc create mode 100644 x-pack/solutions/security/packages/connectors/package.json create mode 100644 x-pack/solutions/security/packages/connectors/src/connector_selector.styles.ts create mode 100644 x-pack/solutions/security/packages/connectors/src/connector_selector.tsx create mode 100644 x-pack/solutions/security/packages/connectors/src/constants.ts create mode 100644 x-pack/solutions/security/packages/connectors/src/translations.ts create mode 100644 x-pack/solutions/security/packages/connectors/tsconfig.json create mode 100644 x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/connector_selector_panel.tsx create mode 100644 x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/connector_selector_with_icon.tsx delete mode 100644 x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/create_connector_popover.tsx create mode 100644 x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/types.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 869d012c5f088..bf54df9493233 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1039,12 +1039,188 @@ src/platform/packages/shared/kbn-visualization-ui-components @elastic/kibana-vis src/platform/packages/shared/kbn-visualization-utils @elastic/kibana-visualizations src/platform/plugins/shared/visualizations @elastic/kibana-visualizations x-pack/platform/plugins/private/watcher @elastic/kibana-management -packages/kbn-web-worker-stub @elastic/kibana-operations -packages/kbn-whereis-pkg-cli @elastic/kibana-operations -src/platform/packages/shared/kbn-xstate-utils @elastic/obs-ux-logs-team -packages/kbn-yarn-lock-validator @elastic/kibana-operations -src/platform/packages/shared/kbn-zod @elastic/kibana-core -src/platform/packages/shared/kbn-zod-helpers @elastic/security-detection-rule-management +x-pack/platform/plugins/shared/actions @elastic/response-ops +x-pack/platform/plugins/shared/ai_infra/llm_tasks @elastic/appex-ai-infra +x-pack/platform/plugins/shared/ai_infra/product_doc_base @elastic/appex-ai-infra +x-pack/platform/plugins/shared/aiops @elastic/ml-ui +x-pack/platform/plugins/shared/alerting @elastic/response-ops +x-pack/platform/plugins/shared/cases @elastic/response-ops +x-pack/platform/plugins/shared/cloud @elastic/kibana-core +x-pack/platform/plugins/shared/dashboard_enhanced @elastic/kibana-presentation +x-pack/platform/plugins/shared/data_quality @elastic/obs-ux-logs-team +x-pack/platform/plugins/shared/dataset_quality @elastic/obs-ux-logs-team +x-pack/platform/plugins/shared/embeddable_enhanced @elastic/kibana-presentation +x-pack/platform/plugins/shared/encrypted_saved_objects @elastic/kibana-security +x-pack/platform/plugins/shared/entity_manager @elastic/obs-entities +x-pack/platform/plugins/shared/event_log @elastic/response-ops +x-pack/platform/plugins/shared/features @elastic/kibana-core +x-pack/platform/plugins/shared/fields_metadata @elastic/obs-ux-logs-team +x-pack/platform/plugins/shared/fleet @elastic/fleet +x-pack/platform/plugins/shared/global_search @elastic/appex-sharedux +x-pack/platform/plugins/shared/index_management @elastic/kibana-management +x-pack/platform/plugins/shared/inference @elastic/appex-ai-infra +x-pack/platform/plugins/shared/inference_endpoint @elastic/ml-ui +x-pack/platform/plugins/shared/ingest_pipelines @elastic/kibana-management +x-pack/platform/plugins/shared/integration_assistant @elastic/security-scalability +x-pack/platform/plugins/shared/lens @elastic/kibana-visualizations +x-pack/platform/plugins/shared/license_management @elastic/kibana-management +x-pack/platform/plugins/shared/licensing @elastic/kibana-core +x-pack/platform/plugins/shared/logs_data_access @elastic/obs-ux-logs-team +x-pack/platform/plugins/shared/logs_shared @elastic/obs-ux-logs-team +x-pack/platform/plugins/shared/maps @elastic/kibana-presentation +x-pack/platform/plugins/shared/ml @elastic/ml-ui +x-pack/platform/plugins/shared/notifications @elastic/appex-sharedux +x-pack/platform/plugins/shared/observability_solution/observability_ai_assistant @elastic/obs-ai-assistant +x-pack/platform/plugins/shared/osquery @elastic/security-defend-workflows +x-pack/platform/plugins/shared/rule_registry @elastic/response-ops @elastic/obs-ux-management-team +x-pack/platform/plugins/shared/saved_objects_tagging @elastic/appex-sharedux +x-pack/platform/plugins/shared/screenshotting @elastic/kibana-reporting-services +x-pack/platform/plugins/shared/searchprofiler @elastic/kibana-management +x-pack/platform/plugins/shared/security @elastic/kibana-security +x-pack/platform/plugins/shared/serverless @elastic/appex-sharedux +x-pack/platform/plugins/shared/spaces @elastic/kibana-security +x-pack/platform/plugins/shared/stack_alerts @elastic/response-ops +x-pack/platform/plugins/shared/stack_connectors @elastic/response-ops +x-pack/platform/plugins/shared/task_manager @elastic/response-ops +x-pack/platform/plugins/shared/triggers_actions_ui @elastic/response-ops +x-pack/solutions/observability/packages/alert_details @elastic/obs-ux-management-team +x-pack/solutions/observability/packages/alerting_test_data @elastic/obs-ux-management-team +x-pack/solutions/observability/packages/get_padded_alert_time_range_util @elastic/obs-ux-management-team +x-pack/solutions/observability/packages/kbn-alerts-grouping @elastic/response-ops +x-pack/solutions/observability/packages/kbn-custom-integrations @elastic/obs-ux-logs-team +x-pack/solutions/observability/packages/kbn-investigation-shared @elastic/obs-ux-management-team +x-pack/solutions/observability/packages/kbn-streams-schema @elastic/streams-program-team +x-pack/solutions/observability/packages/observability_ai/observability_ai_common @elastic/obs-ai-assistant +x-pack/solutions/observability/packages/observability_ai/observability_ai_server @elastic/obs-ai-assistant +x-pack/solutions/observability/packages/synthetics_test_data @elastic/obs-ux-management-team +x-pack/solutions/observability/packages/utils_browser @elastic/observability-ui +x-pack/solutions/observability/packages/utils_common @elastic/observability-ui +x-pack/solutions/observability/packages/utils_server @elastic/observability-ui +x-pack/solutions/observability/plugins/apm @elastic/obs-ux-infra_services-team +x-pack/solutions/observability/plugins/apm_data_access @elastic/obs-ux-infra_services-team +x-pack/solutions/observability/plugins/apm/ftr_e2e @elastic/obs-ux-infra_services-team +x-pack/solutions/observability/plugins/entities_data_access @elastic/obs-entities +x-pack/solutions/observability/plugins/entity_manager_app @elastic/obs-entities +x-pack/solutions/observability/plugins/exploratory_view @elastic/obs-ux-management-team +x-pack/solutions/observability/plugins/infra @elastic/obs-ux-logs-team @elastic/obs-ux-infra_services-team +x-pack/solutions/observability/plugins/inventory @elastic/obs-ux-infra_services-team +x-pack/solutions/observability/plugins/inventory/e2e @elastic/obs-ux-infra_services-team +x-pack/solutions/observability/plugins/investigate @elastic/obs-ux-management-team +x-pack/solutions/observability/plugins/investigate_app @elastic/obs-ux-management-team +x-pack/solutions/observability/plugins/logs_explorer @elastic/obs-ux-logs-team +x-pack/solutions/observability/plugins/metrics_data_access @elastic/obs-ux-infra_services-team +x-pack/solutions/observability/plugins/observability @elastic/obs-ux-management-team +x-pack/solutions/observability/plugins/observability_ai_assistant_app @elastic/obs-ai-assistant +x-pack/solutions/observability/plugins/observability_ai_assistant_management @elastic/obs-ai-assistant +x-pack/solutions/observability/plugins/observability_logs_explorer @elastic/obs-ux-logs-team +x-pack/solutions/observability/plugins/observability_onboarding @elastic/obs-ux-logs-team +x-pack/solutions/observability/plugins/observability_shared @elastic/observability-ui +x-pack/solutions/observability/plugins/profiling @elastic/obs-ux-infra_services-team +x-pack/solutions/observability/plugins/profiling_data_access @elastic/obs-ux-infra_services-team +x-pack/solutions/observability/plugins/serverless_observability @elastic/obs-ux-management-team +x-pack/solutions/observability/plugins/slo @elastic/obs-ux-management-team +x-pack/solutions/observability/plugins/streams @elastic/streams-program-team +x-pack/solutions/observability/plugins/streams_app @elastic/streams-program-team +x-pack/solutions/observability/plugins/synthetics @elastic/obs-ux-management-team +x-pack/solutions/observability/plugins/synthetics/e2e @elastic/obs-ux-management-team +x-pack/solutions/observability/plugins/uptime @elastic/obs-ux-management-team +x-pack/solutions/observability/plugins/ux @elastic/obs-ux-management-team +x-pack/solutions/search/packages/kbn-ipynb @elastic/search-kibana +x-pack/solutions/search/packages/kbn-search-api-keys-components @elastic/search-kibana +x-pack/solutions/search/packages/kbn-search-api-keys-server @elastic/search-kibana +x-pack/solutions/search/packages/kbn-search-index-documents @elastic/search-kibana +x-pack/solutions/search/packages/search/shared_ui @elastic/search-kibana +x-pack/solutions/search/plugins/enterprise_search @elastic/search-kibana +x-pack/solutions/search/plugins/search_assistant @elastic/search-kibana +x-pack/solutions/search/plugins/search_connectors @elastic/search-kibana +x-pack/solutions/search/plugins/search_homepage @elastic/search-kibana +x-pack/solutions/search/plugins/search_indices @elastic/search-kibana +x-pack/solutions/search/plugins/search_inference_endpoints @elastic/search-kibana +x-pack/solutions/search/plugins/search_notebooks @elastic/search-kibana +x-pack/solutions/search/plugins/search_playground @elastic/search-kibana +x-pack/solutions/search/plugins/search_solution/search_navigation @elastic/search-kibana +x-pack/solutions/search/plugins/search_synonyms @elastic/search-kibana +x-pack/solutions/search/plugins/serverless_search @elastic/search-kibana +x-pack/solutions/security/packages/connectors @elastic/security-threat-hunting-explore +x-pack/solutions/security/packages/data_table @elastic/security-threat-hunting-investigations +x-pack/solutions/security/packages/data-stream-adapter @elastic/security-threat-hunting +x-pack/solutions/security/packages/distribution_bar @elastic/kibana-cloud-security-posture +x-pack/solutions/security/packages/ecs_data_quality_dashboard @elastic/security-threat-hunting-explore +x-pack/solutions/security/packages/expandable-flyout @elastic/security-threat-hunting-investigations +x-pack/solutions/security/packages/features @elastic/security-threat-hunting-explore +x-pack/solutions/security/packages/index-adapter @elastic/security-threat-hunting +x-pack/solutions/security/packages/kbn-cloud-security-posture/graph @elastic/kibana-cloud-security-posture +x-pack/solutions/security/packages/kbn-cloud-security-posture/public @elastic/kibana-cloud-security-posture +x-pack/solutions/security/packages/kbn-securitysolution-autocomplete @elastic/security-detection-engine +x-pack/solutions/security/packages/kbn-securitysolution-endpoint-exceptions-common @elastic/security-detection-engine +x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components @elastic/security-detection-engine +x-pack/solutions/security/packages/kbn-securitysolution-exceptions-common @elastic/security-detection-engine +x-pack/solutions/security/packages/kbn-securitysolution-hook-utils @elastic/security-detection-engine +x-pack/solutions/security/packages/kbn-securitysolution-io-ts-alerting-types @elastic/security-detection-engine +x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types @elastic/security-detection-engine +x-pack/solutions/security/packages/kbn-securitysolution-list-api @elastic/security-detection-engine +x-pack/solutions/security/packages/kbn-securitysolution-list-constants @elastic/security-detection-engine +x-pack/solutions/security/packages/kbn-securitysolution-list-hooks @elastic/security-detection-engine +x-pack/solutions/security/packages/kbn-securitysolution-list-utils @elastic/security-detection-engine +x-pack/solutions/security/packages/kbn-securitysolution-lists-common @elastic/security-detection-engine +x-pack/solutions/security/packages/kbn-securitysolution-t-grid @elastic/security-detection-engine +x-pack/solutions/security/packages/kbn-securitysolution-utils @elastic/security-detection-engine @elastic/security-detection-rule-management +x-pack/solutions/security/packages/navigation @elastic/security-threat-hunting-explore +x-pack/solutions/security/packages/side_nav @elastic/security-threat-hunting-explore +x-pack/solutions/security/packages/storybook/config @elastic/security-threat-hunting-explore +x-pack/solutions/security/packages/upselling @elastic/security-threat-hunting-explore +x-pack/solutions/security/plugins/cloud_defend @elastic/kibana-cloud-security-posture +x-pack/solutions/security/plugins/cloud_security_posture @elastic/kibana-cloud-security-posture +x-pack/solutions/security/plugins/ecs_data_quality_dashboard @elastic/security-threat-hunting-explore +x-pack/solutions/security/plugins/elastic_assistant @elastic/security-generative-ai +x-pack/solutions/security/plugins/kubernetes_security @elastic/kibana-cloud-security-posture +x-pack/solutions/security/plugins/lists @elastic/security-detection-engine +x-pack/solutions/security/plugins/security_solution @elastic/security-solution +x-pack/solutions/security/plugins/security_solution_ess @elastic/security-solution +x-pack/solutions/security/plugins/security_solution_serverless @elastic/security-solution +x-pack/solutions/security/plugins/session_view @elastic/kibana-cloud-security-posture +x-pack/solutions/security/plugins/threat_intelligence @elastic/security-threat-hunting-investigations +x-pack/solutions/security/plugins/timelines @elastic/security-threat-hunting-investigations +x-pack/test +x-pack/test_serverless +x-pack/test/alerting_api_integration/common/plugins/aad @elastic/response-ops +x-pack/test/alerting_api_integration/common/plugins/actions_simulators @elastic/response-ops +x-pack/test/alerting_api_integration/common/plugins/alerts @elastic/response-ops +x-pack/test/alerting_api_integration/common/plugins/alerts_restricted @elastic/response-ops +x-pack/test/alerting_api_integration/common/plugins/task_manager_fixture @elastic/response-ops +x-pack/test/alerting_api_integration/packages/helpers @elastic/response-ops +x-pack/test/api_integration/apis/entity_manager/fixture_plugin @elastic/obs-entities +x-pack/test/cases_api_integration/common/plugins/cases @elastic/response-ops +x-pack/test/cases_api_integration/common/plugins/observability @elastic/response-ops +x-pack/test/cases_api_integration/common/plugins/security_solution @elastic/response-ops +x-pack/test/cloud_integration/plugins/saml_provider @elastic/kibana-core +x-pack/test/encrypted_saved_objects_api_integration/plugins/api_consumer_plugin @elastic/kibana-security +x-pack/test/functional_cors/plugins/kibana_cors_test @elastic/kibana-security +x-pack/test/functional_embedded/plugins/iframe_embedded @elastic/kibana-core +x-pack/test/functional_execution_context/plugins/alerts @elastic/kibana-core +x-pack/test/functional_with_es_ssl/plugins/alerts @elastic/response-ops +x-pack/test/functional_with_es_ssl/plugins/cases @elastic/response-ops +x-pack/test/licensing_plugin/plugins/test_feature_usage @elastic/kibana-security +x-pack/test/plugin_api_integration/plugins/elasticsearch_client @elastic/kibana-core +x-pack/test/plugin_api_integration/plugins/event_log @elastic/response-ops +x-pack/test/plugin_api_integration/plugins/feature_usage_test @elastic/kibana-security +x-pack/test/plugin_api_integration/plugins/sample_task_plugin @elastic/response-ops +x-pack/test/plugin_api_perf/plugins/task_manager_performance @elastic/response-ops +x-pack/test/plugin_functional/plugins/global_search_test @elastic/kibana-core +x-pack/test/plugin_functional/plugins/resolver_test @elastic/security-solution +x-pack/test/saved_object_api_integration/common/plugins/saved_object_test_plugin @elastic/kibana-security +x-pack/test/security_api_integration/packages/helpers @elastic/kibana-security +x-pack/test/security_api_integration/plugins/audit_log @elastic/kibana-security +x-pack/test/security_api_integration/plugins/features_provider @elastic/kibana-security +x-pack/test/security_api_integration/plugins/oidc_provider @elastic/kibana-security +x-pack/test/security_api_integration/plugins/saml_provider @elastic/kibana-security +x-pack/test/security_api_integration/plugins/user_profiles_consumer @elastic/kibana-security +x-pack/test/security_functional/plugins/test_endpoints @elastic/kibana-security +x-pack/test/spaces_api_integration/common/plugins/spaces_test_plugin @elastic/kibana-security +x-pack/test/task_manager_claimer_update_by_query/plugins/sample_task_plugin_mget @elastic/response-ops +x-pack/test/ui_capabilities/common/plugins/foo_plugin @elastic/kibana-security +x-pack/test/usage_collection/plugins/application_usage_test @elastic/kibana-core +x-pack/test/usage_collection/plugins/stack_management_usage_test @elastic/kibana-management #### ## Everything below this line overrides the default assignments for each package. ## Items lower in the file have higher precedence: diff --git a/package.json b/package.json index a45421b2c9728..add7353337be3 100644 --- a/package.json +++ b/package.json @@ -821,6 +821,7 @@ "@kbn/security-plugin-types-public": "link:x-pack/platform/packages/shared/security/plugin_types_public", "@kbn/security-plugin-types-server": "link:x-pack/platform/packages/shared/security/plugin_types_server", "@kbn/security-role-management-model": "link:x-pack/platform/packages/private/security/role_management_model", + "@kbn/security-solution-connectors": "link:x-pack/solutions/security/packages/connectors", "@kbn/security-solution-distribution-bar": "link:x-pack/solutions/security/packages/distribution_bar", "@kbn/security-solution-ess": "link:x-pack/solutions/security/plugins/security_solution_ess", "@kbn/security-solution-features": "link:x-pack/solutions/security/packages/features", diff --git a/tsconfig.base.json b/tsconfig.base.json index 269507698c51a..2732f1c646850 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1632,6 +1632,8 @@ "@kbn/security-plugin-types-server/*": ["x-pack/platform/packages/shared/security/plugin_types_server/*"], "@kbn/security-role-management-model": ["x-pack/platform/packages/private/security/role_management_model"], "@kbn/security-role-management-model/*": ["x-pack/platform/packages/private/security/role_management_model/*"], + "@kbn/security-solution-connectors": ["x-pack/solutions/security/packages/connectors"], + "@kbn/security-solution-connectors/*": ["x-pack/solutions/security/packages/connectors/*"], "@kbn/security-solution-distribution-bar": ["x-pack/solutions/security/packages/distribution_bar"], "@kbn/security-solution-distribution-bar/*": ["x-pack/solutions/security/packages/distribution_bar/*"], "@kbn/security-solution-ess": ["x-pack/solutions/security/plugins/security_solution_ess"], diff --git a/x-pack/solutions/security/packages/connectors/index.ts b/x-pack/solutions/security/packages/connectors/index.ts new file mode 100644 index 0000000000000..01eae1b554656 --- /dev/null +++ b/x-pack/solutions/security/packages/connectors/index.ts @@ -0,0 +1,9 @@ +/* + * 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. + */ + +export { ConnectorSelector } from './src/connector_selector'; +export type { ConnectorSelectorProps } from './src/connector_selector'; diff --git a/x-pack/solutions/security/packages/connectors/jest.config.js b/x-pack/solutions/security/packages/connectors/jest.config.js new file mode 100644 index 0000000000000..a56ddf1903cc2 --- /dev/null +++ b/x-pack/solutions/security/packages/connectors/jest.config.js @@ -0,0 +1,12 @@ +/* + * 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. + */ + +module.exports = { + preset: '@kbn/test', + roots: ['/x-pack/solutions/security/packages/connectors'], + rootDir: '../../../../..', +}; diff --git a/x-pack/solutions/security/packages/connectors/kibana.jsonc b/x-pack/solutions/security/packages/connectors/kibana.jsonc new file mode 100644 index 0000000000000..b3f8ff1d76925 --- /dev/null +++ b/x-pack/solutions/security/packages/connectors/kibana.jsonc @@ -0,0 +1,9 @@ +{ + "type": "shared-browser", + "id": "@kbn/security-solution-connectors", + "owner": [ + "@elastic/security-threat-hunting-explore" + ], + "group": "security", + "visibility": "private" +} \ No newline at end of file diff --git a/x-pack/solutions/security/packages/connectors/package.json b/x-pack/solutions/security/packages/connectors/package.json new file mode 100644 index 0000000000000..7b906bac6b9a4 --- /dev/null +++ b/x-pack/solutions/security/packages/connectors/package.json @@ -0,0 +1,7 @@ +{ + "name": "@kbn/security-solution-connectors", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0", + "sideEffects": false +} \ No newline at end of file diff --git a/x-pack/solutions/security/packages/connectors/src/connector_selector.styles.ts b/x-pack/solutions/security/packages/connectors/src/connector_selector.styles.ts new file mode 100644 index 0000000000000..b2d90aee0b3a1 --- /dev/null +++ b/x-pack/solutions/security/packages/connectors/src/connector_selector.styles.ts @@ -0,0 +1,41 @@ +/* + * 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 { useEuiTheme } from '@elastic/eui'; +import { css } from '@emotion/react'; + +export const useConnectorSelectorStyles = () => { + const { euiTheme } = useEuiTheme(); + + return { + placeholder: css` + color: ${euiTheme.colors.primary}; + margin-right: ${euiTheme.size.xs}; + `, + optionDisplay: css` + margin-right: 8px; + overflow: hidden; + text-overflow: ellipsis; + `, + offset: css` + width: 24px; + `, + inputContainer: css` + .euiSuperSelectControl { + border: none; + box-shadow: none; + background: none; + padding-left: 0; + } + + .euiFormControlLayoutIcons { + right: 14px; + top: 2px; + } + `, + }; +}; diff --git a/x-pack/solutions/security/packages/connectors/src/connector_selector.tsx b/x-pack/solutions/security/packages/connectors/src/connector_selector.tsx new file mode 100644 index 0000000000000..5824e1486c9e1 --- /dev/null +++ b/x-pack/solutions/security/packages/connectors/src/connector_selector.tsx @@ -0,0 +1,145 @@ +/* + * 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 { + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiSuperSelect, + EuiText, + useEuiTheme, +} from '@elastic/eui'; +import React, { useCallback, useMemo } from 'react'; +import { some } from 'lodash'; +import * as i18n from './translations'; +import { useConnectorSelectorStyles } from './connector_selector.styles'; +import { ADD_NEW_CONNECTOR } from './constants'; + +export interface ConnectorDetails { + id: string; + name: string; + description: string; +} + +export interface ConnectorSelectorProps { + connectors: ConnectorDetails[]; + onChange: (connectorId: string) => void; + selectedId?: string; + onNewConnectorClicked?: () => void; + isDisabled?: boolean; +} + +export const ConnectorSelector = React.memo( + ({ connectors, onChange, selectedId, onNewConnectorClicked, isDisabled }) => { + const styles = useConnectorSelectorStyles(); + const { euiTheme } = useEuiTheme(); + + const addNewConnectorOption = useMemo(() => { + return { + value: ADD_NEW_CONNECTOR, + inputDisplay: i18n.ADD_NEW_CONNECTOR, + dropdownDisplay: ( + + + + {i18n.ADD_NEW_CONNECTOR} + + + + {/* Right offset to compensate for 'selected' icon of EuiSuperSelect since native footers aren't supported*/} +
+ + + ), + }; + }, [isDisabled, styles.offset]); + + const connectorExists = useMemo( + () => some(connectors, ['id', selectedId]), + [connectors, selectedId] + ); + + const mappedConnectorOptions = connectors.map((connector) => ({ + value: connector.id, + 'data-test-subj': connector.id, + inputDisplay: ( + + {connector.name} + + ), + dropdownDisplay: ( + + + + {connector.name} + +

{connector.description}

+
+
+
+
+ ), + })); + + const allConnectorOptions = useMemo( + () => + onNewConnectorClicked + ? [...mappedConnectorOptions, addNewConnectorOption] + : [...mappedConnectorOptions], + [onNewConnectorClicked, mappedConnectorOptions, addNewConnectorOption] + ); + + const onChangeConnector = useCallback( + (connectorId: string) => { + if (connectorId === ADD_NEW_CONNECTOR) { + onNewConnectorClicked?.(); + return; + } + onChange(connectorId); + }, + [onChange, onNewConnectorClicked] + ); + + return ( +
+ {!connectorExists && !connectors.length ? ( + onNewConnectorClicked?.()} + > + {i18n.ADD_CONNECTOR} + + ) : ( + + )} +
+ ); + } +); + +ConnectorSelector.displayName = 'ConnectorSelector'; diff --git a/x-pack/solutions/security/packages/connectors/src/constants.ts b/x-pack/solutions/security/packages/connectors/src/constants.ts new file mode 100644 index 0000000000000..f9a41a7e7d16d --- /dev/null +++ b/x-pack/solutions/security/packages/connectors/src/constants.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export const ADD_NEW_CONNECTOR = 'ADD_NEW_CONNECTOR'; diff --git a/x-pack/solutions/security/packages/connectors/src/translations.ts b/x-pack/solutions/security/packages/connectors/src/translations.ts new file mode 100644 index 0000000000000..9356a08c06d8a --- /dev/null +++ b/x-pack/solutions/security/packages/connectors/src/translations.ts @@ -0,0 +1,36 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const CONNECTOR_SELECTOR_TITLE = i18n.translate( + 'securitySolutionPackages.connectors.connectorSelector.ariaLabel', + { + defaultMessage: 'Connector Selector', + } +); + +export const ADD_NEW_CONNECTOR = i18n.translate( + 'securitySolutionPackages.connectors.connectorSelector.newConnectorOptions', + { + defaultMessage: 'Add new Connector...', + } +); + +export const ADD_CONNECTOR = i18n.translate( + 'securitySolutionPackages.connectors.connectorSelector.addConnectorButtonLabel', + { + defaultMessage: 'Add connector', + } +); + +export const CONNECTOR_SELECTOR_PLACEHOLDER = i18n.translate( + 'securitySolutionPackages.connectors.connectorSelectorInline.connectorPlaceholder', + { + defaultMessage: 'Select a connector', + } +); diff --git a/x-pack/solutions/security/packages/connectors/tsconfig.json b/x-pack/solutions/security/packages/connectors/tsconfig.json new file mode 100644 index 0000000000000..f93f3b17903b0 --- /dev/null +++ b/x-pack/solutions/security/packages/connectors/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "react", + "@emotion/react/types/css-prop", + "@testing-library/jest-dom", + "@testing-library/react", + ] + }, + "include": ["**/*.ts", "**/*.tsx"], + "kbn_references": [ + "@kbn/i18n", + ], + "exclude": ["target/**/*"] +} diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/hooks/use_stored_state.ts b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/hooks/use_stored_state.ts index 22867e1199705..98f865741d4a9 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/hooks/use_stored_state.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/hooks/use_stored_state.ts @@ -19,6 +19,7 @@ const LocalStorageKey = { selectedIntegrationTabId: 'securitySolution.onboarding.selectedIntegrationTabId', selectedCardItemId: 'securitySolution.onboarding.selectedCardItem', integrationSearchTerm: 'securitySolution.onboarding.integrationSearchTerm', + assistantConnectorId: 'securitySolution.onboarding.assistantCard.connectorId', } as const; /** @@ -80,3 +81,9 @@ export const useStoredIntegrationSearchTerm = (spaceId: string) => `${LocalStorageKey.integrationSearchTerm}.${spaceId}`, null ); + +/** + * Stores the integration search term per space + */ +export const useStoredAssistantConnectorId = (spaceId: string) => + useDefinedLocalStorage(`${LocalStorageKey.assistantConnectorId}.${spaceId}`, null); diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/assistant_card.tsx b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/assistant_card.tsx index c971bd2a6f0b7..10e3690a63a32 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/assistant_card.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/assistant_card.tsx @@ -8,15 +8,25 @@ import React, { useCallback, useMemo } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLink } from '@elastic/eui'; import { css } from '@emotion/css'; +import { useAssistantContext, type Conversation } from '@kbn/elastic-assistant'; +import { useCurrentConversation } from '@kbn/elastic-assistant/impl/assistant/use_current_conversation'; +import { useDataStreamApis } from '@kbn/elastic-assistant/impl/assistant/use_data_stream_apis'; +import { getDefaultConnector } from '@kbn/elastic-assistant/impl/assistant/helpers'; +import { getGenAiConfig } from '@kbn/elastic-assistant/impl/connectorland/helpers'; +import { useConversation } from '@kbn/elastic-assistant/impl/assistant/use_conversation'; +import { CenteredLoadingSpinner } from '../../../../../common/components/centered_loading_spinner'; import { OnboardingCardId } from '../../../../constants'; import type { OnboardingCardComponent } from '../../../../types'; import * as i18n from './translations'; +import { useStoredAssistantConnectorId } from '../../../hooks/use_stored_state'; +import { useOnboardingContext } from '../../../onboarding_context'; import { OnboardingCardContentPanel } from '../common/card_content_panel'; import { ConnectorCards } from '../common/connectors/connector_cards'; import { CardCallOut } from '../common/card_callout'; import { CardSubduedText } from '../common/card_subdued_text'; import type { AssistantCardMetadata } from './types'; import { MissingPrivilegesCallOut } from '../common/connectors/missing_privileges'; +import type { AIConnector } from '../common/connectors/types'; export const AssistantCard: OnboardingCardComponent = ({ isCardComplete, @@ -25,6 +35,9 @@ export const AssistantCard: OnboardingCardComponent = ({ checkComplete, isCardAvailable, }) => { + const { spaceId } = useOnboardingContext(); + const { connectors, canExecuteConnectors, canCreateConnectors } = checkCompleteMetadata ?? {}; + const isIntegrationsCardComplete = useMemo( () => isCardComplete(OnboardingCardId.integrations), [isCardComplete] @@ -39,9 +52,99 @@ export const AssistantCard: OnboardingCardComponent = ({ setExpandedCardId(OnboardingCardId.integrations, { scroll: true }); }, [setExpandedCardId]); - const connectors = checkCompleteMetadata?.connectors; - const canExecuteConnectors = checkCompleteMetadata?.canExecuteConnectors; - const canCreateConnectors = checkCompleteMetadata?.canCreateConnectors; + const [selectedConnectorId, setSelectedConnectorId] = useStoredAssistantConnectorId(spaceId); + + const defaultConnector = useMemo(() => getDefaultConnector(connectors), [connectors]); + + const { setApiConfig } = useConversation(); + + const { + http, + assistantAvailability: { isAssistantEnabled }, + baseConversations, + getLastConversationId, + } = useAssistantContext(); + const { + allSystemPrompts, + conversations, + isFetchedCurrentUserConversations, + isFetchedPrompts, + refetchCurrentUserConversations, + } = useDataStreamApis({ http, baseConversations, isAssistantEnabled }); + + const { currentConversation, handleOnConversationSelected } = useCurrentConversation({ + allSystemPrompts, + conversations, + defaultConnector, + refetchCurrentUserConversations, + conversationId: getLastConversationId(), + mayUpdateConversations: + isFetchedCurrentUserConversations && + isFetchedPrompts && + Object.keys(conversations).length > 0, + }); + + const onConversationChange = useCallback( + (updatedConversation: Conversation) => { + handleOnConversationSelected({ + cId: updatedConversation.id, + cTitle: updatedConversation.title, + }); + }, + [handleOnConversationSelected] + ); + + const onConnectorSelected = useCallback( + async (connector: AIConnector) => { + const connectorId = connector.id; + + const config = getGenAiConfig(connector); + const apiProvider = config?.apiProvider; + const model = config?.defaultModel; + + if (currentConversation != null) { + const conversation = await setApiConfig({ + conversation: currentConversation, + apiConfig: { + ...currentConversation.apiConfig, + actionTypeId: connector.actionTypeId, + connectorId, + // With the inline component, prefer config args to handle 'new connector' case + provider: apiProvider, + model, + }, + }); + + if (conversation && onConversationChange != null) { + onConversationChange(conversation); + } + } + + if (selectedConnectorId != null) { + setSelectedConnectorId(connectorId); + } + }, + [ + currentConversation, + selectedConnectorId, + setApiConfig, + onConversationChange, + setSelectedConnectorId, + ] + ); + + if (!checkCompleteMetadata) { + return ( + + + + ); + } + + const onNewConnectorSaved = (connectorId: string) => { + checkComplete(); + setSelectedConnectorId(connectorId); + }; return ( @@ -77,7 +180,9 @@ export const AssistantCard: OnboardingCardComponent = ({ )} diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/translations.ts b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/translations.ts index 1c526d4974a9a..a0f1a933ea939 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/translations.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/translations.ts @@ -18,7 +18,7 @@ export const ASSISTANT_CARD_DESCRIPTION = i18n.translate( 'xpack.securitySolution.onboarding.assistantCard.description', { defaultMessage: - 'Choose and configure any AI provider available to use with Elastic AI Assistant.', + 'The Elastic AI connector is currently configured, powered by OpenAI gpt 4.0 for optimal performance for migrating SIEM rules. However,any AI service provider can be configured. Read more about AI provider performance and other Elastic powered by AI features.', } ); diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/connector_cards.tsx b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/connector_cards.tsx index b8b51198c75ff..7c7ba2b31557a 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/connector_cards.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/connector_cards.tsx @@ -6,42 +6,39 @@ */ import React, { useCallback } from 'react'; -import { type AIConnector } from '@kbn/elastic-assistant/impl/connectorland/connector_selector'; -import { - EuiFlexGroup, - EuiFlexItem, - EuiPanel, - EuiLoadingSpinner, - EuiText, - EuiBadge, - EuiSpacer, - EuiCallOut, - useEuiTheme, -} from '@elastic/eui'; -import { css } from '@emotion/css'; -import { useKibana } from '../../../../../../common/lib/kibana'; -import { - CreateConnectorPopover, - type CreateConnectorPopoverProps, -} from './create_connector_popover'; -import { ConnectorSetup } from './connector_setup'; +import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiCallOut } from '@elastic/eui'; +import { css } from '@emotion/react'; +import type { AIConnector } from './types'; import * as i18n from './translations'; import { MissingPrivilegesDescription } from './missing_privileges'; +import { ConnectorSetup } from './connector_setup'; +import { ConnectorSelectorPanel } from './connector_selector_panel'; -interface ConnectorCardsProps - extends CreateConnectorPopoverProps, - Omit { +interface ConnectorCardsProps { + onNewConnectorSaved: (connectorId: string) => void; + canCreateConnectors?: boolean; connectors?: AIConnector[]; // make connectors optional to handle loading state + selectedConnectorId?: string | null; + onConnectorSelected: (connector: AIConnector) => void; } export const ConnectorCards = React.memo( ({ connectors, - onConnectorSaved, + onNewConnectorSaved, canCreateConnectors, selectedConnectorId, - setSelectedConnectorId, + onConnectorSelected, }) => { + const onNewConnectorStoredSave = useCallback( + (newConnector: AIConnector) => { + onNewConnectorSaved(newConnector.id); + // default select the new connector created + onConnectorSelected(newConnector); + }, + [onConnectorSelected, onNewConnectorSaved] + ); + if (!connectors) { return ; } @@ -59,95 +56,26 @@ export const ConnectorCards = React.memo( return ( <> - {hasConnectors ? ( - <> - - - - - ) : ( - - )} + + {hasConnectors && ( + + + + )} + + + + ); } ); ConnectorCards.displayName = 'ConnectorCards'; - -interface ConnectorListProps { - connectors: AIConnector[]; - selectedConnectorId?: string | null; - setSelectedConnectorId?: (id: string) => void; -} - -const ConnectorList = React.memo( - ({ connectors, selectedConnectorId, setSelectedConnectorId }) => { - const { euiTheme } = useEuiTheme(); - const { actionTypeRegistry } = useKibana().services.triggersActionsUi; - const onConnectorClick = useCallback( - (id: string) => { - setSelectedConnectorId?.(id); - }, - [setSelectedConnectorId] - ); - - const selectedCss = `border: 2px solid ${euiTheme.colors.primary};`; - - return ( - - {connectors.map((connector) => ( - - onConnectorClick(connector.id) : undefined} - css={css` - ${selectedConnectorId === connector.id ? selectedCss : ''} - `} - color={selectedConnectorId === connector.id ? 'primary' : 'plain'} - > - - - {connector.name} - - - - {actionTypeRegistry.get(connector.actionTypeId).actionTypeTitle} - - - - - - ))} - - ); - } -); - -ConnectorList.displayName = 'ConnectorList'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/connector_selector_panel.tsx b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/connector_selector_panel.tsx new file mode 100644 index 0000000000000..149b65e62a458 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/connector_selector_panel.tsx @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui'; +import { css } from '@emotion/react'; +import { ConnectorSelectorWithIcon } from './connector_selector_with_icon'; +import * as i18n from './translations'; +import type { AIConnector } from './types'; + +interface ConnectorSelectorPanelProps { + connectors: AIConnector[]; + selectedConnectorId?: string | null; + onConnectorSelected: (connector: AIConnector) => void; +} + +export const ConnectorSelectorPanel = React.memo( + ({ connectors, selectedConnectorId, onConnectorSelected }) => { + return ( + + + + {i18n.SELECTED_PROVIDER} + + + + + + + ); + } +); + +ConnectorSelectorPanel.displayName = 'ConnectorSelectorPanel'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/connector_selector_with_icon.tsx b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/connector_selector_with_icon.tsx new file mode 100644 index 0000000000000..bbfbd56f2fac7 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/connector_selector_with_icon.tsx @@ -0,0 +1,118 @@ +/* + * 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 { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLoadingSpinner } from '@elastic/eui'; +import React, { useMemo, useEffect, useCallback } from 'react'; +import { useAssistantContext } from '@kbn/elastic-assistant'; +import { ConnectorSelector } from '@kbn/security-solution-connectors'; +import { + getActionTypeTitle, + getGenAiConfig, +} from '@kbn/elastic-assistant/impl/connectorland/helpers'; +import { css } from '@emotion/react'; +import type { AIConnector } from './types'; +import { useFilteredActionTypes } from './hooks/use_load_action_types'; +import * as i18n from './translations'; + +interface Props { + isDisabled?: boolean; + selectedConnectorId?: string | null; + connectors: AIConnector[]; + onConnectorSelected: (connector: AIConnector) => void; +} + +/** + * A compact wrapper of the ConnectorSelector with a Selected Icon + */ +export const ConnectorSelectorWithIcon = React.memo( + ({ isDisabled = false, selectedConnectorId, connectors, onConnectorSelected }) => { + const { actionTypeRegistry, assistantAvailability } = useAssistantContext(); + + const actionTypes = useFilteredActionTypes(); + + const selectedConnector = useMemo( + () => connectors.find((connector) => connector.id === selectedConnectorId), + [connectors, selectedConnectorId] + ); + + useEffect(() => { + if (connectors.length === 1) { + onConnectorSelected(connectors[0]); + } + }, [connectors, onConnectorSelected]); + + const localIsDisabled = isDisabled || !assistantAvailability.hasConnectorsReadPrivilege; + + const connectorOptions = useMemo( + () => + (connectors ?? []).map((connector) => { + const connectorTypeTitle = + getGenAiConfig(connector)?.apiProvider ?? + getActionTypeTitle(actionTypeRegistry.get(connector.actionTypeId)); + const connectorDetails = connector.isPreconfigured + ? i18n.PRECONFIGURED_CONNECTOR + : connectorTypeTitle; + + return { + id: connector.id, + name: connector.name, + description: connectorDetails, + }; + }), + [actionTypeRegistry, connectors] + ); + + const onConnectorSelectionChange = useCallback( + (connectorId: string) => { + const connector = (connectors ?? []).find((c) => c.id === connectorId); + if (connector) { + onConnectorSelected(connector); + } + }, + [connectors, onConnectorSelected] + ); + + if (!actionTypes) { + return ; + } + + return ( + + {selectedConnector && ( + + + + )} + {selectedConnectorId && ( + + + + )} + + ); + } +); + +ConnectorSelectorWithIcon.displayName = 'ConnectorSelectorWithIcon'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/connector_setup.tsx b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/connector_setup.tsx index f41c77458edf9..16086bf5eb73d 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/connector_setup.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/connector_setup.tsx @@ -6,142 +6,105 @@ */ import React, { useCallback, useState } from 'react'; +import { type ActionConnector } from '@kbn/triggers-actions-ui-plugin/public/common/constants'; import { - useEuiTheme, EuiFlexGroup, EuiFlexItem, - EuiListGroup, EuiIcon, EuiPanel, EuiLoadingSpinner, - EuiText, - EuiLink, - EuiTextColor, + EuiButton, } from '@elastic/eui'; import { css } from '@emotion/css'; -import { - ConnectorAddModal, - type ActionConnector, -} from '@kbn/triggers-actions-ui-plugin/public/common/constants'; import type { ActionType } from '@kbn/actions-plugin/common'; +import { AddConnectorModal } from '@kbn/elastic-assistant/impl/connectorland/add_connector_modal'; +import * as i18n from './translations'; import { useKibana } from '../../../../../../common/lib/kibana'; import { useFilteredActionTypes } from './hooks/use_load_action_types'; -const usePanelCss = () => { - const { euiTheme } = useEuiTheme(); - return css` - .connectorSelectorPanel { - height: 160px; - &.euiPanel:hover { - background-color: ${euiTheme.colors.lightestShade}; - } - } - `; -}; - interface ConnectorSetupProps { onConnectorSaved?: (savedAction: ActionConnector) => void; onClose?: () => void; - compressed?: boolean; } -export const ConnectorSetup = React.memo( - ({ onConnectorSaved, onClose, compressed = false }) => { - const panelCss = usePanelCss(); - const { - http, - triggersActionsUi: { actionTypeRegistry }, - notifications: { toasts }, - } = useKibana().services; - const [selectedActionType, setSelectedActionType] = useState(null); - - const onModalClose = useCallback(() => { - setSelectedActionType(null); - onClose?.(); - }, [onClose]); +export const ConnectorSetup = React.memo(({ onConnectorSaved, onClose }) => { + const [isModalVisible, setIsModalVisible] = useState(false); + const [selectedActionType, setSelectedActionType] = useState(null); - const actionTypes = useFilteredActionTypes(http, toasts); + const { + triggersActionsUi: { actionTypeRegistry }, + } = useKibana().services; - if (!actionTypes) { - return ; - } + const onModalClose = useCallback(() => { + setSelectedActionType(null); + setIsModalVisible(false); + onClose?.(); + }, [onClose]); - return ( - <> - {compressed ? ( - ({ - key: actionType.id, - id: actionType.id, - label: actionType.name, - size: 's', - icon: ( - - ), - isDisabled: !actionType.enabled, - onClick: () => setSelectedActionType(actionType), - }))} - /> - ) : ( - - {actionTypes.map((actionType: ActionType) => ( - - setSelectedActionType(actionType)} - data-test-subj={`actionType-${actionType.id}`} - className={panelCss} - > - - - - - - - - {actionType.name} - - - - - - - ))} - - )} + const actionTypes = useFilteredActionTypes(); - {selectedActionType && ( - - )} - - ); + if (!actionTypes) { + return ; } -); + + return ( + <> + + + + + {actionTypes.map((actionType: ActionType) => ( + + + + + + + + ))} + + + + setIsModalVisible(true)} + isLoading={false} + > + {i18n.CREATE_NEW_CONNECTOR_BUTTON} + + + + + {isModalVisible && onConnectorSaved && ( + + )} + + ); +}); ConnectorSetup.displayName = 'ConnectorSetup'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/create_connector_popover.tsx b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/create_connector_popover.tsx deleted file mode 100644 index c6c378fc8e29f..0000000000000 --- a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/create_connector_popover.tsx +++ /dev/null @@ -1,59 +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 React, { useCallback, useState } from 'react'; -import { css } from '@emotion/css'; -import { EuiPopover, EuiLink, EuiText } from '@elastic/eui'; -import { ConnectorSetup } from './connector_setup'; -import * as i18n from './translations'; -import { MissingPrivilegesTooltip } from './missing_privileges'; - -export interface CreateConnectorPopoverProps { - onConnectorSaved: () => void; - canCreateConnectors?: boolean; -} - -export const CreateConnectorPopover = React.memo( - ({ onConnectorSaved, canCreateConnectors }) => { - const [isOpen, setIsPopoverOpen] = useState(false); - const closePopover = useCallback(() => setIsPopoverOpen(false), []); - - const onButtonClick = useCallback( - () => setIsPopoverOpen((isPopoverOpen) => !isPopoverOpen), - [] - ); - if (!canCreateConnectors) { - return ( - - - {i18n.ASSISTANT_CARD_CREATE_NEW_CONNECTOR_POPOVER} - - - ); - } - - return ( - - - {i18n.ASSISTANT_CARD_CREATE_NEW_CONNECTOR_POPOVER} - - - } - isOpen={isOpen} - closePopover={closePopover} - data-test-subj="createConnectorPopover" - > - - - ); - } -); -CreateConnectorPopover.displayName = 'CreateConnectorPopover'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/hooks/use_load_action_types.ts b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/hooks/use_load_action_types.ts index 48b8fdfc20d59..5c1c18df71a45 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/hooks/use_load_action_types.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/hooks/use_load_action_types.ts @@ -7,11 +7,11 @@ import { useMemo } from 'react'; import { useLoadActionTypes as loadActionTypes } from '@kbn/elastic-assistant/impl/connectorland/use_load_action_types'; -import type { HttpSetup } from '@kbn/core-http-browser'; -import type { IToasts } from '@kbn/core-notifications-browser'; +import { useAssistantContext } from '@kbn/elastic-assistant'; import { AIActionTypeIds } from '../constants'; -export const useFilteredActionTypes = (http: HttpSetup, toasts: IToasts) => { +export const useFilteredActionTypes = () => { + const { http, toasts } = useAssistantContext(); const { data } = loadActionTypes({ http, toasts }); return useMemo(() => data?.filter(({ id }) => AIActionTypeIds.includes(id)), [data]); }; diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/translations.ts b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/translations.ts index 983a6a67f5b32..a9ba5ac647ff8 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/translations.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/translations.ts @@ -7,10 +7,16 @@ import { i18n } from '@kbn/i18n'; -export const ASSISTANT_CARD_CREATE_NEW_CONNECTOR_POPOVER = i18n.translate( +export const CREATE_NEW_CONNECTOR_BUTTON = i18n.translate( 'xpack.securitySolution.onboarding.assistantCard.createNewConnectorPopover', { - defaultMessage: 'Create new connector', + defaultMessage: 'AI service provider', + } +); +export const SELECTED_PROVIDER = i18n.translate( + 'xpack.securitySolution.onboarding.assistantCard.selectedProvider', + { + defaultMessage: 'Selected provider', } ); @@ -21,6 +27,13 @@ export const PRIVILEGES_MISSING_TITLE = i18n.translate( } ); +export const PRECONFIGURED_CONNECTOR = i18n.translate( + 'xpack.securitySolution.onboarding.assistantCard.preconfiguredTitle', + { + defaultMessage: 'Preconfigured', + } +); + export const PRIVILEGES_REQUIRED_TITLE = i18n.translate( 'xpack.securitySolution.onboarding.assistantCard.requiredPrivileges', { diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/types.ts b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/types.ts new file mode 100644 index 0000000000000..0f15a8132210c --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/common/connectors/types.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public'; + +import type { OpenAiProviderType } from '@kbn/stack-connectors-plugin/common/openai/constants'; + +export type AIConnector = ActionConnector & { + // related to OpenAI connectors, ex: Azure OpenAI, OpenAI + apiProvider?: OpenAiProviderType; +}; diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/ai_connector/ai_connector_card.tsx b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/ai_connector/ai_connector_card.tsx index 1786c9cbee85c..ccb98aef00c84 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/ai_connector/ai_connector_card.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/onboarding/components/onboarding_body/cards/siem_migrations/ai_connector/ai_connector_card.tsx @@ -17,6 +17,7 @@ import { ConnectorCards } from '../../common/connectors/connector_cards'; import { CardSubduedText } from '../../common/card_subdued_text'; import type { AIConnectorCardMetadata } from './types'; import { MissingPrivilegesCallOut } from '../../common/connectors/missing_privileges'; +import type { AIConnector } from '../../common/connectors/types'; export const AIConnectorCard: OnboardingCardComponent = ({ checkCompleteMetadata, @@ -24,13 +25,13 @@ export const AIConnectorCard: OnboardingCardComponent = setComplete, }) => { const { siemMigrations } = useKibana().services; - const [storedConnectorId, setStoredConnectorId] = useDefinedLocalStorage( + const [storedConnectorId, setStoredConnectorId] = useDefinedLocalStorage( siemMigrations.rules.connectorIdStorage.key, - null + '' ); - const setSelectedConnectorId = useCallback( - (connectorId: string) => { - setStoredConnectorId(connectorId); + const setSelectedConnector = useCallback( + (connector: AIConnector) => { + setStoredConnectorId(connector.id); setComplete(true); }, [setComplete, setStoredConnectorId] @@ -57,9 +58,9 @@ export const AIConnectorCard: OnboardingCardComponent = diff --git a/x-pack/solutions/security/plugins/security_solution/tsconfig.json b/x-pack/solutions/security/plugins/security_solution/tsconfig.json index 47c7b6cc08a8b..b5c11ab231323 100644 --- a/x-pack/solutions/security/plugins/security_solution/tsconfig.json +++ b/x-pack/solutions/security/plugins/security_solution/tsconfig.json @@ -230,6 +230,7 @@ "@kbn/react-hooks", "@kbn/index-adapter", "@kbn/core-http-server-utils", + "@kbn/security-solution-connectors", "@kbn/core-chrome-browser-mocks", "@kbn/ai-assistant-icon", "@kbn/llm-tasks-plugin", diff --git a/yarn.lock b/yarn.lock index 6c10f5465b2cb..6b5731e0cdee3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7113,6 +7113,10 @@ version "0.0.0" uid "" +"@kbn/security-solution-connectors@link:x-pack/solutions/security/packages/connectors": + version "0.0.0" + uid "" + "@kbn/security-solution-distribution-bar@link:x-pack/solutions/security/packages/distribution_bar": version "0.0.0" uid ""