From 5f4939be76dd3172eaac4bac4deb60e12a6ba9c5 Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Thu, 18 Mar 2021 18:25:39 -0600 Subject: [PATCH 01/13] Revert "[Security Solution] [Cases] Move create page components and dependencies to Cases (#94444)" (#94975) This reverts commit c497239d55574bb00fe1b2dff631a5288a5eb852. --- packages/kbn-optimizer/limits.yml | 1 - x-pack/plugins/cases/common/api/index.ts | 1 - x-pack/plugins/cases/common/constants.ts | 5 +- x-pack/plugins/cases/common/index.ts | 9 - x-pack/plugins/cases/kibana.json | 5 +- x-pack/plugins/cases/public/common/errors.ts | 39 - .../common/lib/kibana/__mocks__/index.ts | 30 - .../cases/public/common/lib/kibana/index.ts | 9 - .../common/lib/kibana/kibana_react.mock.ts | 35 - .../public/common/lib/kibana/kibana_react.ts | 16 - .../public/common/lib/kibana/services.ts | 42 -- .../plugins/cases/public/common/mock/index.ts | 8 - .../public/common/mock/kibana_react.mock.ts | 23 - .../public/common/mock/test_providers.tsx | 58 -- .../cases/public/common/shared_imports.ts | 33 - .../plugins/cases/public/common/test_utils.ts | 12 - .../cases/public/common/translations.ts | 252 ------- .../cases/public/components/__mock__/form.ts | 50 -- .../configure_cases/__mock__/index.tsx | 60 -- .../configure_cases/closure_options.test.tsx | 58 -- .../configure_cases/closure_options.tsx | 54 -- .../closure_options_radio.test.tsx | 77 -- .../configure_cases/closure_options_radio.tsx | 60 -- .../configure_cases/connectors.test.tsx | 115 --- .../components/configure_cases/connectors.tsx | 119 --- .../connectors_dropdown.test.tsx | 203 ------ .../configure_cases/connectors_dropdown.tsx | 121 ---- .../configure_cases/field_mapping.test.tsx | 55 -- .../configure_cases/field_mapping.tsx | 73 -- .../field_mapping_row_static.tsx | 60 -- .../components/configure_cases/index.test.tsx | 591 --------------- .../components/configure_cases/index.tsx | 224 ------ .../configure_cases/mapping.test.tsx | 47 -- .../components/configure_cases/mapping.tsx | 62 -- .../configure_cases/translations.ts | 227 ------ .../components/configure_cases/utils.test.tsx | 64 -- .../components/configure_cases/utils.ts | 80 -- .../connector_selector/form.test.tsx | 67 -- .../components/connector_selector/form.tsx | 70 -- .../public/components/connectors/card.tsx | 71 -- .../connectors/case/alert_fields.tsx | 106 --- .../connectors/case/cases_dropdown.tsx | 73 -- .../connectors/case/existing_case.tsx | 76 -- .../components/connectors/case/index.ts | 42 -- .../connectors/case/translations.ts | 109 --- .../components/connectors/case/types.ts | 18 - .../public/components/connectors/config.ts | 39 - .../connectors/connectors_registry.ts | 49 -- .../components/connectors/fields_form.tsx | 54 -- .../public/components/connectors/index.ts | 57 -- .../connectors/jira/__mocks__/api.ts | 45 -- .../components/connectors/jira/api.test.ts | 160 ---- .../public/components/connectors/jira/api.ts | 93 --- .../connectors/jira/case_fields.test.tsx | 262 ------- .../connectors/jira/case_fields.tsx | 214 ------ .../components/connectors/jira/index.ts | 27 - .../connectors/jira/search_issues.tsx | 95 --- .../connectors/jira/translations.ts | 68 -- .../components/connectors/jira/types.ts | 22 - .../use_get_fields_by_issue_type.test.tsx | 105 --- .../jira/use_get_fields_by_issue_type.tsx | 96 --- .../jira/use_get_issue_types.test.tsx | 107 --- .../connectors/jira/use_get_issue_types.tsx | 102 --- .../connectors/jira/use_get_issues.test.tsx | 80 -- .../connectors/jira/use_get_issues.tsx | 97 --- .../jira/use_get_single_issue.test.tsx | 80 -- .../connectors/jira/use_get_single_issue.tsx | 95 --- .../public/components/connectors/mock.ts | 121 ---- .../connectors/resilient/__mocks__/api.ts | 16 - .../components/connectors/resilient/api.ts | 42 -- .../connectors/resilient/case_fields.test.tsx | 134 ---- .../connectors/resilient/case_fields.tsx | 189 ----- .../components/connectors/resilient/index.ts | 26 - .../connectors/resilient/translations.ts | 40 - .../components/connectors/resilient/types.ts | 9 - .../resilient/use_get_incident_types.test.tsx | 71 -- .../resilient/use_get_incident_types.tsx | 94 --- .../resilient/use_get_severity.test.tsx | 77 -- .../connectors/resilient/use_get_severity.tsx | 91 --- .../connectors/servicenow/__mocks__/api.ts | 19 - .../connectors/servicenow/api.test.ts | 40 - .../components/connectors/servicenow/api.ts | 31 - .../connectors/servicenow/helpers.ts | 12 - .../components/connectors/servicenow/index.ts | 32 - .../servicenow_itsm_case_fields.test.tsx | 164 ----- .../servicenow_itsm_case_fields.tsx | 235 ------ .../servicenow_sir_case_fields.test.tsx | 221 ------ .../servicenow/servicenow_sir_case_fields.tsx | 282 -------- .../connectors/servicenow/translations.ts | 75 -- .../components/connectors/servicenow/types.ts | 15 - .../servicenow/use_get_choices.test.tsx | 144 ---- .../connectors/servicenow/use_get_choices.tsx | 101 --- .../public/components/connectors/types.ts | 53 -- .../components/create/connector.test.tsx | 187 ----- .../public/components/create/connector.tsx | 103 --- .../components/create/description.test.tsx | 62 -- .../public/components/create/description.tsx | 31 - .../public/components/create/flyout.test.tsx | 115 --- .../cases/public/components/create/flyout.tsx | 71 -- .../public/components/create/form.test.tsx | 109 --- .../cases/public/components/create/form.tsx | 119 --- .../components/create/form_context.test.tsx | 682 ------------------ .../public/components/create/form_context.tsx | 120 --- .../public/components/create/index.test.tsx | 126 ---- .../cases/public/components/create/index.tsx | 57 -- .../cases/public/components/create/mock.ts | 101 --- .../optional_field_label/index.test.tsx | 19 - .../create/optional_field_label/index.tsx | 17 - .../cases/public/components/create/schema.tsx | 58 -- .../components/create/submit_button.test.tsx | 88 --- .../components/create/submit_button.tsx | 31 - .../create/sync_alerts_toggle.test.tsx | 79 -- .../components/create/sync_alerts_toggle.tsx | 38 - .../public/components/create/tags.test.tsx | 79 -- .../cases/public/components/create/tags.tsx | 49 -- .../public/components/create/title.test.tsx | 72 -- .../cases/public/components/create/title.tsx | 33 - .../public/components/create/translations.ts | 26 - .../components/markdown_editor/editor.tsx | 66 -- .../components/markdown_editor/eui_form.tsx | 66 -- .../components/markdown_editor/index.tsx | 11 - .../markdown_editor/markdown_link.tsx | 35 - .../markdown_editor/renderer.test.tsx | 63 -- .../components/markdown_editor/renderer.tsx | 41 -- .../markdown_editor/translations.ts | 19 - .../components/markdown_editor/types.ts | 11 - .../public/components/status/button.test.tsx | 90 --- .../cases/public/components/status/button.tsx | 52 -- .../cases/public/components/status/config.ts | 82 --- .../cases/public/components/status/index.ts | 11 - .../public/components/status/stats.test.tsx | 66 -- .../cases/public/components/status/stats.tsx | 40 - .../public/components/status/status.test.tsx | 72 -- .../cases/public/components/status/status.tsx | 43 -- .../public/components/status/translations.ts | 69 -- .../cases/public/components/status/types.ts | 43 -- .../modal_all_errors.test.tsx.snap | 48 -- .../public/components/toasters/errors.ts | 19 - .../public/components/toasters/index.test.tsx | 307 -------- .../public/components/toasters/index.tsx | 136 ---- .../toasters/modal_all_errors.test.tsx | 70 -- .../components/toasters/modal_all_errors.tsx | 75 -- .../components/toasters/translations.ts | 20 - .../public/components/toasters/utils.test.ts | 128 ---- .../cases/public/components/toasters/utils.ts | 149 ---- .../create_case_modal.test.tsx | 126 ---- .../create_case_modal.tsx | 67 -- .../use_create_case_modal/index.test.tsx | 151 ---- .../use_create_case_modal/index.tsx | 60 -- .../public/components/wrappers/index.tsx | 26 - .../cases/public/containers/__mocks__/api.ts | 114 --- .../cases/public/containers/api.test.tsx | 465 ------------ x-pack/plugins/cases/public/containers/api.ts | 347 --------- .../containers/configure/__mocks__/api.ts | 36 - .../public/containers/configure/api.test.ts | 154 ---- .../cases/public/containers/configure/api.ts | 103 --- .../cases/public/containers/configure/mock.ts | 160 ---- .../containers/configure/translations.ts | 14 - .../public/containers/configure/types.ts | 46 -- .../configure/use_action_types.test.tsx | 102 --- .../containers/configure/use_action_types.tsx | 73 -- .../configure/use_configure.test.tsx | 326 --------- .../containers/configure/use_configure.tsx | 361 --------- .../configure/use_connectors.test.tsx | 96 --- .../containers/configure/use_connectors.tsx | 72 -- .../cases/public/containers/constants.ts | 9 - .../plugins/cases/public/containers/mock.ts | 377 ---------- .../cases/public/containers/translations.ts | 90 --- .../plugins/cases/public/containers/types.ts | 175 ----- .../public/containers/use_get_cases.test.tsx | 204 ------ .../cases/public/containers/use_get_cases.tsx | 255 ------- .../public/containers/use_get_tags.test.tsx | 89 --- .../cases/public/containers/use_get_tags.tsx | 100 --- .../public/containers/use_post_case.test.tsx | 114 --- .../cases/public/containers/use_post_case.tsx | 91 --- .../use_post_push_to_service.test.tsx | 101 --- .../containers/use_post_push_to_service.tsx | 112 --- .../cases/public/containers/utils.test.ts | 170 ----- .../plugins/cases/public/containers/utils.ts | 150 ---- .../plugins/cases/public/get_create_case.tsx | 19 - x-pack/plugins/cases/public/index.tsx | 20 - x-pack/plugins/cases/public/plugin.ts | 33 - x-pack/plugins/cases/public/types.ts | 29 - .../client/alerts/update_status.test.ts | 2 +- .../cases/server/client/cases/create.test.ts | 7 +- .../cases/server/client/cases/create.ts | 2 +- .../plugins/cases/server/client/cases/get.ts | 2 +- .../plugins/cases/server/client/cases/mock.ts | 2 +- .../plugins/cases/server/client/cases/push.ts | 2 +- .../cases/server/client/cases/types.ts | 2 +- .../cases/server/client/cases/update.test.ts | 2 +- .../cases/server/client/cases/update.ts | 2 +- .../cases/server/client/cases/utils.test.ts | 4 +- .../cases/server/client/cases/utils.ts | 4 +- x-pack/plugins/cases/server/client/client.ts | 2 +- .../cases/server/client/comments/add.test.ts | 2 +- .../cases/server/client/comments/add.ts | 4 +- .../client/configure/get_fields.test.ts | 2 +- .../server/client/configure/get_fields.ts | 2 +- .../client/configure/get_mappings.test.ts | 2 +- .../server/client/configure/get_mappings.ts | 2 +- .../cases/server/client/configure/mock.ts | 6 +- .../server/client/configure/utils.test.ts | 2 +- .../cases/server/client/configure/utils.ts | 6 +- x-pack/plugins/cases/server/client/types.ts | 2 +- .../cases/server/client/user_actions/get.ts | 2 +- .../server/common/models/commentable_case.ts | 2 +- .../plugins/cases/server/common/utils.test.ts | 2 +- x-pack/plugins/cases/server/common/utils.ts | 8 +- .../server/connectors/case/index.test.ts | 2 +- .../cases/server/connectors/case/index.ts | 7 +- .../cases/server/connectors/case/schema.ts | 2 +- .../cases/server/connectors/case/types.ts | 2 +- .../plugins/cases/server/connectors/index.ts | 2 +- .../jira/external_service_formatter.test.ts | 2 +- .../jira/external_service_formatter.ts | 2 +- .../external_service_formatter.test.ts | 2 +- .../resilient/external_service_formatter.ts | 2 +- .../connectors/servicenow/itsm_formatter.ts | 2 +- .../servicenow/itsm_formmater.test.ts | 2 +- .../servicenow/sir_formatter.test.ts | 2 +- .../connectors/servicenow/sir_formatter.ts | 2 +- .../plugins/cases/server/connectors/types.ts | 2 +- x-pack/plugins/cases/server/plugin.ts | 2 +- .../api/__fixtures__/mock_saved_objects.ts | 2 +- .../routes/api/__mocks__/request_responses.ts | 2 +- .../api/cases/comments/delete_all_comments.ts | 3 +- .../api/cases/comments/delete_comment.test.ts | 2 +- .../api/cases/comments/delete_comment.ts | 2 +- .../api/cases/comments/find_comments.ts | 4 +- .../api/cases/comments/get_all_comment.ts | 4 +- .../api/cases/comments/get_comment.test.ts | 2 +- .../routes/api/cases/comments/get_comment.ts | 4 +- .../api/cases/comments/patch_comment.test.ts | 4 +- .../api/cases/comments/patch_comment.ts | 4 +- .../api/cases/comments/post_comment.test.ts | 4 +- .../routes/api/cases/comments/post_comment.ts | 4 +- .../api/cases/configure/get_configure.test.ts | 3 +- .../api/cases/configure/get_configure.ts | 4 +- .../cases/configure/get_connectors.test.ts | 2 +- .../api/cases/configure/get_connectors.ts | 5 +- .../cases/configure/patch_configure.test.ts | 3 +- .../api/cases/configure/patch_configure.ts | 4 +- .../cases/configure/post_configure.test.ts | 3 +- .../api/cases/configure/post_configure.ts | 4 +- .../routes/api/cases/delete_cases.test.ts | 2 +- .../server/routes/api/cases/delete_cases.ts | 2 +- .../routes/api/cases/find_cases.test.ts | 2 +- .../server/routes/api/cases/find_cases.ts | 4 +- .../server/routes/api/cases/get_case.test.ts | 4 +- .../cases/server/routes/api/cases/get_case.ts | 2 +- .../server/routes/api/cases/helpers.test.ts | 2 +- .../cases/server/routes/api/cases/helpers.ts | 11 +- .../routes/api/cases/patch_cases.test.ts | 2 +- .../server/routes/api/cases/patch_cases.ts | 4 +- .../server/routes/api/cases/post_case.test.ts | 4 +- .../server/routes/api/cases/post_case.ts | 4 +- .../server/routes/api/cases/push_case.test.ts | 2 +- .../server/routes/api/cases/push_case.ts | 4 +- .../api/cases/reporters/get_reporters.ts | 4 +- .../api/cases/status/get_status.test.ts | 4 +- .../routes/api/cases/status/get_status.ts | 4 +- .../api/cases/sub_case/delete_sub_cases.ts | 2 +- .../api/cases/sub_case/find_sub_cases.ts | 4 +- .../routes/api/cases/sub_case/get_sub_case.ts | 4 +- .../api/cases/sub_case/patch_sub_cases.ts | 4 +- .../server/routes/api/cases/tags/get_tags.ts | 2 +- .../user_actions/get_all_user_actions.ts | 2 +- .../cases/server/routes/api/utils.test.ts | 2 +- .../plugins/cases/server/routes/api/utils.ts | 2 +- .../server/saved_object_types/migrations.ts | 2 +- .../cases/server/scripts/sub_cases/index.ts | 4 +- .../server/services/alerts/index.test.ts | 2 +- .../cases/server/services/alerts/index.ts | 2 +- .../cases/server/services/configure/index.ts | 2 +- .../services/connector_mappings/index.ts | 2 +- x-pack/plugins/cases/server/services/index.ts | 2 +- .../services/reporters/read_reporters.ts | 2 +- .../cases/server/services/tags/read_tags.ts | 2 +- .../server/services/user_actions/helpers.ts | 2 +- .../server/services/user_actions/index.ts | 2 +- .../security_solution/common/constants.ts | 9 - x-pack/plugins/security_solution/kibana.json | 1 - .../components/add_comment/index.test.tsx | 2 +- .../cases/components/add_comment/index.tsx | 4 +- .../cases/components/add_comment/schema.tsx | 2 +- .../cases/components/all_cases/actions.tsx | 2 +- .../cases/components/all_cases/columns.tsx | 2 +- .../components/all_cases/expanded_row.tsx | 2 +- .../cases/components/all_cases/helpers.ts | 2 +- .../cases/components/all_cases/index.test.tsx | 2 +- .../cases/components/all_cases/index.tsx | 2 +- .../all_cases/status_filter.test.tsx | 2 +- .../all_cases/table_filters.test.tsx | 2 +- .../components/all_cases/table_filters.tsx | 2 +- .../cases/components/bulk_actions/index.tsx | 2 +- .../case_action_bar/helpers.test.ts | 2 +- .../components/case_action_bar/helpers.ts | 2 +- .../components/case_action_bar/index.tsx | 2 +- .../status_context_menu.test.tsx | 2 +- .../case_action_bar/status_context_menu.tsx | 2 +- .../components/case_view/helpers.test.tsx | 2 +- .../cases/components/case_view/helpers.ts | 2 +- .../cases/components/case_view/index.test.tsx | 3 +- .../cases/components/case_view/index.tsx | 2 +- .../configure_cases/__mock__/index.tsx | 2 +- .../configure_cases/connectors.test.tsx | 2 +- .../components/configure_cases/connectors.tsx | 2 +- .../configure_cases/connectors_dropdown.tsx | 2 +- .../components/configure_cases/index.test.tsx | 2 +- .../components/configure_cases/index.tsx | 2 +- .../cases/components/configure_cases/utils.ts | 2 +- .../components/connector_selector/form.tsx | 2 +- .../cases/components/connectors/card.tsx | 2 +- .../connectors/case/alert_fields.tsx | 2 +- .../connectors/case/existing_case.tsx | 2 +- .../components/connectors/fields_form.tsx | 2 +- .../cases/components/connectors/index.ts | 2 +- .../connectors/jira/case_fields.tsx | 2 +- .../cases/components/connectors/jira/index.ts | 2 +- .../connectors/resilient/case_fields.tsx | 2 +- .../components/connectors/resilient/index.ts | 2 +- .../components/connectors/servicenow/index.ts | 5 +- .../servicenow_itsm_case_fields.tsx | 5 +- .../servicenow/servicenow_sir_case_fields.tsx | 5 +- .../cases/components/connectors/types.ts | 4 +- .../cases/components/create/connector.tsx | 2 +- .../components/create/form_context.test.tsx | 2 +- .../cases/components/create/form_context.tsx | 2 +- .../public/cases/components/create/index.tsx | 62 +- .../public/cases/components/create/mock.ts | 3 +- .../public/cases/components/create/schema.tsx | 2 +- .../cases/components/edit_connector/index.tsx | 3 +- .../cases/components/status/button.test.tsx | 2 +- .../public/cases/components/status/button.tsx | 2 +- .../public/cases/components/status/config.ts | 2 +- .../cases/components/status/stats.test.tsx | 2 +- .../public/cases/components/status/stats.tsx | 2 +- .../cases/components/status/status.test.tsx | 2 +- .../public/cases/components/status/types.ts | 2 +- .../timeline_actions/add_to_case_action.tsx | 2 +- .../use_all_cases_modal/all_cases_modal.tsx | 2 +- .../components/use_all_cases_modal/index.tsx | 2 +- .../create_case_modal.tsx | 2 +- .../use_create_case_modal/index.tsx | 2 +- .../use_push_to_service/index.test.tsx | 3 +- .../components/use_push_to_service/index.tsx | 2 +- .../user_action_tree/helpers.test.tsx | 2 +- .../components/user_action_tree/helpers.tsx | 2 +- .../components/user_action_tree/index.tsx | 2 +- .../user_action_alert_comment_event.test.tsx | 2 +- .../user_action_alert_comment_event.tsx | 2 +- .../public/cases/containers/api.ts | 32 +- .../public/cases/containers/configure/api.ts | 14 +- .../containers/configure/use_configure.tsx | 2 +- .../public/cases/containers/types.ts | 18 +- .../cases/containers/use_bulk_update_case.tsx | 2 +- .../public/cases/containers/utils.ts | 16 +- .../use_manage_case_action.tsx | 2 +- .../plugins/security_solution/public/index.ts | 4 +- .../public/timelines/containers/api.ts | 2 +- .../plugins/security_solution/public/types.ts | 2 - 362 files changed, 324 insertions(+), 17515 deletions(-) delete mode 100644 x-pack/plugins/cases/common/index.ts delete mode 100644 x-pack/plugins/cases/public/common/errors.ts delete mode 100644 x-pack/plugins/cases/public/common/lib/kibana/__mocks__/index.ts delete mode 100644 x-pack/plugins/cases/public/common/lib/kibana/index.ts delete mode 100644 x-pack/plugins/cases/public/common/lib/kibana/kibana_react.mock.ts delete mode 100644 x-pack/plugins/cases/public/common/lib/kibana/kibana_react.ts delete mode 100644 x-pack/plugins/cases/public/common/lib/kibana/services.ts delete mode 100644 x-pack/plugins/cases/public/common/mock/index.ts delete mode 100644 x-pack/plugins/cases/public/common/mock/kibana_react.mock.ts delete mode 100644 x-pack/plugins/cases/public/common/mock/test_providers.tsx delete mode 100644 x-pack/plugins/cases/public/common/shared_imports.ts delete mode 100644 x-pack/plugins/cases/public/common/test_utils.ts delete mode 100644 x-pack/plugins/cases/public/common/translations.ts delete mode 100644 x-pack/plugins/cases/public/components/__mock__/form.ts delete mode 100644 x-pack/plugins/cases/public/components/configure_cases/__mock__/index.tsx delete mode 100644 x-pack/plugins/cases/public/components/configure_cases/closure_options.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/configure_cases/closure_options.tsx delete mode 100644 x-pack/plugins/cases/public/components/configure_cases/closure_options_radio.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/configure_cases/closure_options_radio.tsx delete mode 100644 x-pack/plugins/cases/public/components/configure_cases/connectors.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/configure_cases/connectors.tsx delete mode 100644 x-pack/plugins/cases/public/components/configure_cases/connectors_dropdown.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/configure_cases/connectors_dropdown.tsx delete mode 100644 x-pack/plugins/cases/public/components/configure_cases/field_mapping.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/configure_cases/field_mapping.tsx delete mode 100644 x-pack/plugins/cases/public/components/configure_cases/field_mapping_row_static.tsx delete mode 100644 x-pack/plugins/cases/public/components/configure_cases/index.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/configure_cases/index.tsx delete mode 100644 x-pack/plugins/cases/public/components/configure_cases/mapping.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/configure_cases/mapping.tsx delete mode 100644 x-pack/plugins/cases/public/components/configure_cases/translations.ts delete mode 100644 x-pack/plugins/cases/public/components/configure_cases/utils.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/configure_cases/utils.ts delete mode 100644 x-pack/plugins/cases/public/components/connector_selector/form.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/connector_selector/form.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/card.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/case/alert_fields.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/case/cases_dropdown.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/case/existing_case.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/case/index.ts delete mode 100644 x-pack/plugins/cases/public/components/connectors/case/translations.ts delete mode 100644 x-pack/plugins/cases/public/components/connectors/case/types.ts delete mode 100644 x-pack/plugins/cases/public/components/connectors/config.ts delete mode 100644 x-pack/plugins/cases/public/components/connectors/connectors_registry.ts delete mode 100644 x-pack/plugins/cases/public/components/connectors/fields_form.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/index.ts delete mode 100644 x-pack/plugins/cases/public/components/connectors/jira/__mocks__/api.ts delete mode 100644 x-pack/plugins/cases/public/components/connectors/jira/api.test.ts delete mode 100644 x-pack/plugins/cases/public/components/connectors/jira/api.ts delete mode 100644 x-pack/plugins/cases/public/components/connectors/jira/case_fields.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/jira/case_fields.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/jira/index.ts delete mode 100644 x-pack/plugins/cases/public/components/connectors/jira/search_issues.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/jira/translations.ts delete mode 100644 x-pack/plugins/cases/public/components/connectors/jira/types.ts delete mode 100644 x-pack/plugins/cases/public/components/connectors/jira/use_get_fields_by_issue_type.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/jira/use_get_fields_by_issue_type.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/jira/use_get_issue_types.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/jira/use_get_issue_types.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/jira/use_get_issues.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/jira/use_get_issues.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/jira/use_get_single_issue.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/jira/use_get_single_issue.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/mock.ts delete mode 100644 x-pack/plugins/cases/public/components/connectors/resilient/__mocks__/api.ts delete mode 100644 x-pack/plugins/cases/public/components/connectors/resilient/api.ts delete mode 100644 x-pack/plugins/cases/public/components/connectors/resilient/case_fields.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/resilient/case_fields.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/resilient/index.ts delete mode 100644 x-pack/plugins/cases/public/components/connectors/resilient/translations.ts delete mode 100644 x-pack/plugins/cases/public/components/connectors/resilient/types.ts delete mode 100644 x-pack/plugins/cases/public/components/connectors/resilient/use_get_incident_types.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/resilient/use_get_incident_types.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/resilient/use_get_severity.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/resilient/use_get_severity.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/servicenow/__mocks__/api.ts delete mode 100644 x-pack/plugins/cases/public/components/connectors/servicenow/api.test.ts delete mode 100644 x-pack/plugins/cases/public/components/connectors/servicenow/api.ts delete mode 100644 x-pack/plugins/cases/public/components/connectors/servicenow/helpers.ts delete mode 100644 x-pack/plugins/cases/public/components/connectors/servicenow/index.ts delete mode 100644 x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_itsm_case_fields.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_itsm_case_fields.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_sir_case_fields.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_sir_case_fields.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/servicenow/translations.ts delete mode 100644 x-pack/plugins/cases/public/components/connectors/servicenow/types.ts delete mode 100644 x-pack/plugins/cases/public/components/connectors/servicenow/use_get_choices.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/servicenow/use_get_choices.tsx delete mode 100644 x-pack/plugins/cases/public/components/connectors/types.ts delete mode 100644 x-pack/plugins/cases/public/components/create/connector.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/create/connector.tsx delete mode 100644 x-pack/plugins/cases/public/components/create/description.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/create/description.tsx delete mode 100644 x-pack/plugins/cases/public/components/create/flyout.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/create/flyout.tsx delete mode 100644 x-pack/plugins/cases/public/components/create/form.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/create/form.tsx delete mode 100644 x-pack/plugins/cases/public/components/create/form_context.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/create/form_context.tsx delete mode 100644 x-pack/plugins/cases/public/components/create/index.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/create/index.tsx delete mode 100644 x-pack/plugins/cases/public/components/create/mock.ts delete mode 100644 x-pack/plugins/cases/public/components/create/optional_field_label/index.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/create/optional_field_label/index.tsx delete mode 100644 x-pack/plugins/cases/public/components/create/schema.tsx delete mode 100644 x-pack/plugins/cases/public/components/create/submit_button.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/create/submit_button.tsx delete mode 100644 x-pack/plugins/cases/public/components/create/sync_alerts_toggle.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/create/sync_alerts_toggle.tsx delete mode 100644 x-pack/plugins/cases/public/components/create/tags.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/create/tags.tsx delete mode 100644 x-pack/plugins/cases/public/components/create/title.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/create/title.tsx delete mode 100644 x-pack/plugins/cases/public/components/create/translations.ts delete mode 100644 x-pack/plugins/cases/public/components/markdown_editor/editor.tsx delete mode 100644 x-pack/plugins/cases/public/components/markdown_editor/eui_form.tsx delete mode 100644 x-pack/plugins/cases/public/components/markdown_editor/index.tsx delete mode 100644 x-pack/plugins/cases/public/components/markdown_editor/markdown_link.tsx delete mode 100644 x-pack/plugins/cases/public/components/markdown_editor/renderer.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/markdown_editor/renderer.tsx delete mode 100644 x-pack/plugins/cases/public/components/markdown_editor/translations.ts delete mode 100644 x-pack/plugins/cases/public/components/markdown_editor/types.ts delete mode 100644 x-pack/plugins/cases/public/components/status/button.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/status/button.tsx delete mode 100644 x-pack/plugins/cases/public/components/status/config.ts delete mode 100644 x-pack/plugins/cases/public/components/status/index.ts delete mode 100644 x-pack/plugins/cases/public/components/status/stats.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/status/stats.tsx delete mode 100644 x-pack/plugins/cases/public/components/status/status.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/status/status.tsx delete mode 100644 x-pack/plugins/cases/public/components/status/translations.ts delete mode 100644 x-pack/plugins/cases/public/components/status/types.ts delete mode 100644 x-pack/plugins/cases/public/components/toasters/__snapshots__/modal_all_errors.test.tsx.snap delete mode 100644 x-pack/plugins/cases/public/components/toasters/errors.ts delete mode 100644 x-pack/plugins/cases/public/components/toasters/index.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/toasters/index.tsx delete mode 100644 x-pack/plugins/cases/public/components/toasters/modal_all_errors.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/toasters/modal_all_errors.tsx delete mode 100644 x-pack/plugins/cases/public/components/toasters/translations.ts delete mode 100644 x-pack/plugins/cases/public/components/toasters/utils.test.ts delete mode 100644 x-pack/plugins/cases/public/components/toasters/utils.ts delete mode 100644 x-pack/plugins/cases/public/components/use_create_case_modal/create_case_modal.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/use_create_case_modal/create_case_modal.tsx delete mode 100644 x-pack/plugins/cases/public/components/use_create_case_modal/index.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/use_create_case_modal/index.tsx delete mode 100644 x-pack/plugins/cases/public/components/wrappers/index.tsx delete mode 100644 x-pack/plugins/cases/public/containers/__mocks__/api.ts delete mode 100644 x-pack/plugins/cases/public/containers/api.test.tsx delete mode 100644 x-pack/plugins/cases/public/containers/api.ts delete mode 100644 x-pack/plugins/cases/public/containers/configure/__mocks__/api.ts delete mode 100644 x-pack/plugins/cases/public/containers/configure/api.test.ts delete mode 100644 x-pack/plugins/cases/public/containers/configure/api.ts delete mode 100644 x-pack/plugins/cases/public/containers/configure/mock.ts delete mode 100644 x-pack/plugins/cases/public/containers/configure/translations.ts delete mode 100644 x-pack/plugins/cases/public/containers/configure/types.ts delete mode 100644 x-pack/plugins/cases/public/containers/configure/use_action_types.test.tsx delete mode 100644 x-pack/plugins/cases/public/containers/configure/use_action_types.tsx delete mode 100644 x-pack/plugins/cases/public/containers/configure/use_configure.test.tsx delete mode 100644 x-pack/plugins/cases/public/containers/configure/use_configure.tsx delete mode 100644 x-pack/plugins/cases/public/containers/configure/use_connectors.test.tsx delete mode 100644 x-pack/plugins/cases/public/containers/configure/use_connectors.tsx delete mode 100644 x-pack/plugins/cases/public/containers/constants.ts delete mode 100644 x-pack/plugins/cases/public/containers/mock.ts delete mode 100644 x-pack/plugins/cases/public/containers/translations.ts delete mode 100644 x-pack/plugins/cases/public/containers/types.ts delete mode 100644 x-pack/plugins/cases/public/containers/use_get_cases.test.tsx delete mode 100644 x-pack/plugins/cases/public/containers/use_get_cases.tsx delete mode 100644 x-pack/plugins/cases/public/containers/use_get_tags.test.tsx delete mode 100644 x-pack/plugins/cases/public/containers/use_get_tags.tsx delete mode 100644 x-pack/plugins/cases/public/containers/use_post_case.test.tsx delete mode 100644 x-pack/plugins/cases/public/containers/use_post_case.tsx delete mode 100644 x-pack/plugins/cases/public/containers/use_post_push_to_service.test.tsx delete mode 100644 x-pack/plugins/cases/public/containers/use_post_push_to_service.tsx delete mode 100644 x-pack/plugins/cases/public/containers/utils.test.ts delete mode 100644 x-pack/plugins/cases/public/containers/utils.ts delete mode 100644 x-pack/plugins/cases/public/get_create_case.tsx delete mode 100644 x-pack/plugins/cases/public/index.tsx delete mode 100644 x-pack/plugins/cases/public/plugin.ts delete mode 100644 x-pack/plugins/cases/public/types.ts diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 1af74aa3d8828..f93849e011d41 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -108,4 +108,3 @@ pageLoadAssetSize: fileUpload: 25664 banners: 17946 mapsEms: 26072 - cases: 102558 diff --git a/x-pack/plugins/cases/common/api/index.ts b/x-pack/plugins/cases/common/api/index.ts index 2ef03dd96e315..7780564089d3d 100644 --- a/x-pack/plugins/cases/common/api/index.ts +++ b/x-pack/plugins/cases/common/api/index.ts @@ -7,7 +7,6 @@ export * from './cases'; export * from './connectors'; -export * from './helpers'; export * from './runtime_types'; export * from './saved_object'; export * from './user'; diff --git a/x-pack/plugins/cases/common/constants.ts b/x-pack/plugins/cases/common/constants.ts index d779ccd0b7ab0..1e7cff99a00bd 100644 --- a/x-pack/plugins/cases/common/constants.ts +++ b/x-pack/plugins/cases/common/constants.ts @@ -5,9 +5,8 @@ * 2.0. */ -// The DEFAULT_MAX_SIGNALS value should match the one in `x-pack/plugins/security_solution/common/constants.ts` -// If either changes, engineer should ensure both values are updated -const DEFAULT_MAX_SIGNALS = 100; +import { DEFAULT_MAX_SIGNALS } from '../../security_solution/common/constants'; + export const APP_ID = 'cases'; /** diff --git a/x-pack/plugins/cases/common/index.ts b/x-pack/plugins/cases/common/index.ts deleted file mode 100644 index 37c11172b50b2..0000000000000 --- a/x-pack/plugins/cases/common/index.ts +++ /dev/null @@ -1,9 +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. - */ - -export * from './constants'; -export * from './api'; diff --git a/x-pack/plugins/cases/kibana.json b/x-pack/plugins/cases/kibana.json index 27b36d7e86e1f..1aaf84decbe36 100644 --- a/x-pack/plugins/cases/kibana.json +++ b/x-pack/plugins/cases/kibana.json @@ -2,13 +2,12 @@ "configPath": ["xpack", "cases"], "id": "cases", "kibanaVersion": "kibana", - "extraPublicDirs": ["common"], - "requiredPlugins": ["actions", "esUiShared", "kibanaReact", "triggersActionsUi"], + "requiredPlugins": ["actions", "securitySolution"], "optionalPlugins": [ "spaces", "security" ], "server": true, - "ui": true, + "ui": false, "version": "8.0.0" } diff --git a/x-pack/plugins/cases/public/common/errors.ts b/x-pack/plugins/cases/public/common/errors.ts deleted file mode 100644 index 6edef08c1f4b1..0000000000000 --- a/x-pack/plugins/cases/public/common/errors.ts +++ /dev/null @@ -1,39 +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 { has } from 'lodash/fp'; - -export interface AppError { - name: string; - message: string; - body: { - message: string; - }; -} - -export interface KibanaError extends AppError { - body: { - message: string; - statusCode: number; - }; -} - -export interface CasesAppError extends AppError { - body: { - message: string; - status_code: number; - }; -} - -export const isKibanaError = (error: unknown): error is KibanaError => - has('message', error) && has('body.message', error) && has('body.statusCode', error); - -export const isCasesAppError = (error: unknown): error is CasesAppError => - has('message', error) && has('body.message', error) && has('body.status_code', error); - -export const isAppError = (error: unknown): error is AppError => - isKibanaError(error) || isCasesAppError(error); diff --git a/x-pack/plugins/cases/public/common/lib/kibana/__mocks__/index.ts b/x-pack/plugins/cases/public/common/lib/kibana/__mocks__/index.ts deleted file mode 100644 index 392b71befe2b4..0000000000000 --- a/x-pack/plugins/cases/public/common/lib/kibana/__mocks__/index.ts +++ /dev/null @@ -1,30 +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 { notificationServiceMock } from '../../../../../../../../src/core/public/mocks'; -import { - createKibanaContextProviderMock, - createStartServicesMock, - createWithKibanaMock, -} from '../kibana_react.mock'; - -export const KibanaServices = { get: jest.fn(), getKibanaVersion: jest.fn(() => '8.0.0') }; -export const useKibana = jest.fn().mockReturnValue({ - services: createStartServicesMock(), -}); - -export const useHttp = jest.fn().mockReturnValue(createStartServicesMock().http); -export const useTimeZone = jest.fn(); -export const useDateFormat = jest.fn(); -export const useBasePath = jest.fn(() => '/test/base/path'); -export const useToasts = jest - .fn() - .mockReturnValue(notificationServiceMock.createStartContract().toasts); -export const useCurrentUser = jest.fn(); -export const withKibana = jest.fn(createWithKibanaMock()); -export const KibanaContextProvider = jest.fn(createKibanaContextProviderMock()); -export const useGetUserSavedObjectPermissions = jest.fn(); diff --git a/x-pack/plugins/cases/public/common/lib/kibana/index.ts b/x-pack/plugins/cases/public/common/lib/kibana/index.ts deleted file mode 100644 index a7f3c1e70ced5..0000000000000 --- a/x-pack/plugins/cases/public/common/lib/kibana/index.ts +++ /dev/null @@ -1,9 +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. - */ - -export * from './kibana_react'; -export * from './services'; diff --git a/x-pack/plugins/cases/public/common/lib/kibana/kibana_react.mock.ts b/x-pack/plugins/cases/public/common/lib/kibana/kibana_react.mock.ts deleted file mode 100644 index 326163f6cdc03..0000000000000 --- a/x-pack/plugins/cases/public/common/lib/kibana/kibana_react.mock.ts +++ /dev/null @@ -1,35 +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 from 'react'; - -import { RecursivePartial } from '@elastic/eui/src/components/common'; -import { coreMock } from '../../../../../../../src/core/public/mocks'; -import { KibanaContextProvider } from '../../../../../../../src/plugins/kibana_react/public'; -import { StartServices } from '../../../types'; -import { EuiTheme } from '../../../../../../../src/plugins/kibana_react/common'; - -export const createStartServicesMock = (): StartServices => - (coreMock.createStart() as unknown) as StartServices; - -export const createWithKibanaMock = () => { - const services = createStartServicesMock(); - - return (Component: unknown) => (props: unknown) => { - return React.createElement(Component as string, { ...(props as object), kibana: { services } }); - }; -}; - -export const createKibanaContextProviderMock = () => { - const services = createStartServicesMock(); - - return ({ children }: { children: React.ReactNode }) => - React.createElement(KibanaContextProvider, { services }, children); -}; - -export const getMockTheme = (partialTheme: RecursivePartial): EuiTheme => - partialTheme as EuiTheme; diff --git a/x-pack/plugins/cases/public/common/lib/kibana/kibana_react.ts b/x-pack/plugins/cases/public/common/lib/kibana/kibana_react.ts deleted file mode 100644 index e23fad392040c..0000000000000 --- a/x-pack/plugins/cases/public/common/lib/kibana/kibana_react.ts +++ /dev/null @@ -1,16 +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 { - KibanaContextProvider, - useKibana, -} from '../../../../../../../src/plugins/kibana_react/public'; -import { StartServices } from '../../../types'; - -const useTypedKibana = () => useKibana(); - -export { KibanaContextProvider, useTypedKibana as useKibana }; diff --git a/x-pack/plugins/cases/public/common/lib/kibana/services.ts b/x-pack/plugins/cases/public/common/lib/kibana/services.ts deleted file mode 100644 index 94487bd3ca5e9..0000000000000 --- a/x-pack/plugins/cases/public/common/lib/kibana/services.ts +++ /dev/null @@ -1,42 +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 { CoreStart } from 'kibana/public'; - -type GlobalServices = Pick; - -export class KibanaServices { - private static kibanaVersion?: string; - private static services?: GlobalServices; - - public static init({ http, kibanaVersion }: GlobalServices & { kibanaVersion: string }) { - this.services = { http }; - this.kibanaVersion = kibanaVersion; - } - - public static get(): GlobalServices { - if (!this.services) { - this.throwUninitializedError(); - } - - return this.services; - } - - public static getKibanaVersion(): string { - if (!this.kibanaVersion) { - this.throwUninitializedError(); - } - - return this.kibanaVersion; - } - - private static throwUninitializedError(): never { - throw new Error( - 'Kibana services not initialized - are you trying to import this module from outside of the Cases app?' - ); - } -} diff --git a/x-pack/plugins/cases/public/common/mock/index.ts b/x-pack/plugins/cases/public/common/mock/index.ts deleted file mode 100644 index add4c1c206dd4..0000000000000 --- a/x-pack/plugins/cases/public/common/mock/index.ts +++ /dev/null @@ -1,8 +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. - */ - -export * from './test_providers'; diff --git a/x-pack/plugins/cases/public/common/mock/kibana_react.mock.ts b/x-pack/plugins/cases/public/common/mock/kibana_react.mock.ts deleted file mode 100644 index 274462aec575d..0000000000000 --- a/x-pack/plugins/cases/public/common/mock/kibana_react.mock.ts +++ /dev/null @@ -1,23 +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 { CoreStart } from 'kibana/public'; -import { coreMock } from '../../../../../../src/core/public/mocks'; -import React from 'react'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public/context'; - -export const createStartServicesMock = (): CoreStart => { - const core = coreMock.createStart(); - return (core as unknown) as CoreStart; -}; -export const createKibanaContextProviderMock = () => { - const services = coreMock.createStart(); - - return ({ children }: { children: React.ReactNode }) => - React.createElement(KibanaContextProvider, { services }, children); -}; diff --git a/x-pack/plugins/cases/public/common/mock/test_providers.tsx b/x-pack/plugins/cases/public/common/mock/test_providers.tsx deleted file mode 100644 index 4e40f3b3cb745..0000000000000 --- a/x-pack/plugins/cases/public/common/mock/test_providers.tsx +++ /dev/null @@ -1,58 +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 euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json'; -import { I18nProvider } from '@kbn/i18n/react'; -import React from 'react'; -import { BehaviorSubject } from 'rxjs'; -import { ThemeProvider } from 'styled-components'; -import { createKibanaContextProviderMock, createStartServicesMock } from './kibana_react.mock'; -import { FieldHook } from '../shared_imports'; - -interface Props { - children: React.ReactNode; -} - -export const kibanaObservable = new BehaviorSubject(createStartServicesMock()); - -window.scrollTo = jest.fn(); -const MockKibanaContextProvider = createKibanaContextProviderMock(); - -/** A utility for wrapping children in the providers required to run most tests */ -const TestProvidersComponent: React.FC = ({ children }) => ( - - - ({ eui: euiDarkVars, darkMode: true })}>{children} - - -); - -export const TestProviders = React.memo(TestProvidersComponent); - -export const useFormFieldMock = (options?: Partial>): FieldHook => { - return { - path: 'path', - type: 'type', - value: ('mockedValue' as unknown) as T, - isPristine: false, - isValidating: false, - isValidated: false, - isChangingValue: false, - errors: [], - isValid: true, - getErrorsMessages: jest.fn(), - onChange: jest.fn(), - setValue: jest.fn(), - setErrors: jest.fn(), - clearErrors: jest.fn(), - validate: jest.fn(), - reset: jest.fn(), - __isIncludedInOutput: true, - __serializeValue: jest.fn(), - ...options, - }; -}; diff --git a/x-pack/plugins/cases/public/common/shared_imports.ts b/x-pack/plugins/cases/public/common/shared_imports.ts deleted file mode 100644 index 675204076b02a..0000000000000 --- a/x-pack/plugins/cases/public/common/shared_imports.ts +++ /dev/null @@ -1,33 +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. - */ - -export { - getUseField, - getFieldValidityAndErrorMessage, - FieldHook, - FieldValidateResponse, - FIELD_TYPES, - Form, - FormData, - FormDataProvider, - FormHook, - FormSchema, - UseField, - UseMultiFields, - useForm, - useFormContext, - useFormData, - ValidationError, - ValidationFunc, - VALIDATION_TYPES, -} from '../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib'; -export { - Field, - SelectField, -} from '../../../../../src/plugins/es_ui_shared/static/forms/components'; -export { fieldValidators } from '../../../../../src/plugins/es_ui_shared/static/forms/helpers'; -export { ERROR_CODE } from '../../../../../src/plugins/es_ui_shared/static/forms/helpers/field_validators/types'; diff --git a/x-pack/plugins/cases/public/common/test_utils.ts b/x-pack/plugins/cases/public/common/test_utils.ts deleted file mode 100644 index f6ccf28bcb643..0000000000000 --- a/x-pack/plugins/cases/public/common/test_utils.ts +++ /dev/null @@ -1,12 +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. - */ - -/** - * Convenience utility to remove text appended to links by EUI - */ -export const removeExternalLinkText = (str: string) => - str.replace(/\(opens in a new tab or window\)/g, ''); diff --git a/x-pack/plugins/cases/public/common/translations.ts b/x-pack/plugins/cases/public/common/translations.ts deleted file mode 100644 index 881acb9d4c90e..0000000000000 --- a/x-pack/plugins/cases/public/common/translations.ts +++ /dev/null @@ -1,252 +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 { i18n } from '@kbn/i18n'; - -export const SAVED_OBJECT_NO_PERMISSIONS_TITLE = i18n.translate( - 'xpack.cases.caseSavedObjectNoPermissionsTitle', - { - defaultMessage: 'Kibana feature privileges required', - } -); - -export const SAVED_OBJECT_NO_PERMISSIONS_MSG = i18n.translate( - 'xpack.cases.caseSavedObjectNoPermissionsMessage', - { - defaultMessage: - 'To view cases, you must have privileges for the Saved Object Management feature in the Kibana space. For more information, contact your Kibana administrator.', - } -); - -export const BACK_TO_ALL = i18n.translate('xpack.cases.caseView.backLabel', { - defaultMessage: 'Back to cases', -}); - -export const CANCEL = i18n.translate('xpack.cases.caseView.cancel', { - defaultMessage: 'Cancel', -}); - -export const DELETE_CASE = i18n.translate('xpack.cases.confirmDeleteCase.deleteCase', { - defaultMessage: 'Delete case', -}); - -export const DELETE_CASES = i18n.translate('xpack.cases.confirmDeleteCase.deleteCases', { - defaultMessage: 'Delete cases', -}); - -export const NAME = i18n.translate('xpack.cases.caseView.name', { - defaultMessage: 'Name', -}); - -export const OPENED_ON = i18n.translate('xpack.cases.caseView.openedOn', { - defaultMessage: 'Opened on', -}); - -export const CLOSED_ON = i18n.translate('xpack.cases.caseView.closedOn', { - defaultMessage: 'Closed on', -}); - -export const REPORTER = i18n.translate('xpack.cases.caseView.reporterLabel', { - defaultMessage: 'Reporter', -}); - -export const PARTICIPANTS = i18n.translate('xpack.cases.caseView.particpantsLabel', { - defaultMessage: 'Participants', -}); - -export const CREATE_BC_TITLE = i18n.translate('xpack.cases.caseView.breadcrumb', { - defaultMessage: 'Create', -}); - -export const CREATE_TITLE = i18n.translate('xpack.cases.caseView.create', { - defaultMessage: 'Create new case', -}); - -export const DESCRIPTION = i18n.translate('xpack.cases.caseView.description', { - defaultMessage: 'Description', -}); - -export const DESCRIPTION_REQUIRED = i18n.translate( - 'xpack.cases.createCase.descriptionFieldRequiredError', - { - defaultMessage: 'A description is required.', - } -); - -export const COMMENT_REQUIRED = i18n.translate('xpack.cases.caseView.commentFieldRequiredError', { - defaultMessage: 'A comment is required.', -}); - -export const REQUIRED_FIELD = i18n.translate('xpack.cases.caseView.fieldRequiredError', { - defaultMessage: 'Required field', -}); - -export const EDIT = i18n.translate('xpack.cases.caseView.edit', { - defaultMessage: 'Edit', -}); - -export const OPTIONAL = i18n.translate('xpack.cases.caseView.optional', { - defaultMessage: 'Optional', -}); - -export const PAGE_TITLE = i18n.translate('xpack.cases.pageTitle', { - defaultMessage: 'Cases', -}); - -export const CREATE_CASE = i18n.translate('xpack.cases.caseView.createCase', { - defaultMessage: 'Create case', -}); - -export const CLOSE_CASE = i18n.translate('xpack.cases.caseView.closeCase', { - defaultMessage: 'Close case', -}); - -export const MARK_CASE_IN_PROGRESS = i18n.translate('xpack.cases.caseView.markInProgress', { - defaultMessage: 'Mark in progress', -}); - -export const REOPEN_CASE = i18n.translate('xpack.cases.caseView.reopenCase', { - defaultMessage: 'Reopen case', -}); - -export const OPEN_CASE = i18n.translate('xpack.cases.caseView.openCase', { - defaultMessage: 'Open case', -}); - -export const CASE_NAME = i18n.translate('xpack.cases.caseView.caseName', { - defaultMessage: 'Case name', -}); - -export const TO = i18n.translate('xpack.cases.caseView.to', { - defaultMessage: 'to', -}); - -export const TAGS = i18n.translate('xpack.cases.caseView.tags', { - defaultMessage: 'Tags', -}); - -export const ACTIONS = i18n.translate('xpack.cases.allCases.actions', { - defaultMessage: 'Actions', -}); - -export const NO_TAGS_AVAILABLE = i18n.translate('xpack.cases.allCases.noTagsAvailable', { - defaultMessage: 'No tags available', -}); - -export const NO_REPORTERS_AVAILABLE = i18n.translate('xpack.cases.caseView.noReportersAvailable', { - defaultMessage: 'No reporters available.', -}); - -export const COMMENTS = i18n.translate('xpack.cases.allCases.comments', { - defaultMessage: 'Comments', -}); - -export const TAGS_HELP = i18n.translate('xpack.cases.createCase.fieldTagsHelpText', { - defaultMessage: - 'Type one or more custom identifying tags for this case. Press enter after each tag to begin a new one.', -}); - -export const NO_TAGS = i18n.translate('xpack.cases.caseView.noTags', { - defaultMessage: 'No tags are currently assigned to this case.', -}); - -export const TITLE_REQUIRED = i18n.translate('xpack.cases.createCase.titleFieldRequiredError', { - defaultMessage: 'A title is required.', -}); - -export const CONFIGURE_CASES_PAGE_TITLE = i18n.translate('xpack.cases.configureCases.headerTitle', { - defaultMessage: 'Configure cases', -}); - -export const CONFIGURE_CASES_BUTTON = i18n.translate('xpack.cases.configureCasesButton', { - defaultMessage: 'Edit external connection', -}); - -export const ADD_COMMENT = i18n.translate('xpack.cases.caseView.comment.addComment', { - defaultMessage: 'Add comment', -}); - -export const ADD_COMMENT_HELP_TEXT = i18n.translate( - 'xpack.cases.caseView.comment.addCommentHelpText', - { - defaultMessage: 'Add a new comment...', - } -); - -export const SAVE = i18n.translate('xpack.cases.caseView.description.save', { - defaultMessage: 'Save', -}); - -export const GO_TO_DOCUMENTATION = i18n.translate('xpack.cases.caseView.goToDocumentationButton', { - defaultMessage: 'View documentation', -}); - -export const CONNECTORS = i18n.translate('xpack.cases.caseView.connectors', { - defaultMessage: 'External Incident Management System', -}); - -export const EDIT_CONNECTOR = i18n.translate('xpack.cases.caseView.editConnector', { - defaultMessage: 'Change external incident management system', -}); - -export const NO_CONNECTOR = i18n.translate('xpack.cases.common.noConnector', { - defaultMessage: 'No connector selected', -}); - -export const UNKNOWN = i18n.translate('xpack.cases.caseView.unknown', { - defaultMessage: 'Unknown', -}); - -export const MARKED_CASE_AS = i18n.translate('xpack.cases.caseView.markedCaseAs', { - defaultMessage: 'marked case as', -}); - -export const OPEN_CASES = i18n.translate('xpack.cases.caseTable.openCases', { - defaultMessage: 'Open cases', -}); - -export const CLOSED_CASES = i18n.translate('xpack.cases.caseTable.closedCases', { - defaultMessage: 'Closed cases', -}); - -export const IN_PROGRESS_CASES = i18n.translate('xpack.cases.caseTable.inProgressCases', { - defaultMessage: 'In progress cases', -}); - -export const SYNC_ALERTS_SWITCH_LABEL_ON = i18n.translate( - 'xpack.cases.settings.syncAlertsSwitchLabelOn', - { - defaultMessage: 'On', - } -); - -export const SYNC_ALERTS_SWITCH_LABEL_OFF = i18n.translate( - 'xpack.cases.settings.syncAlertsSwitchLabelOff', - { - defaultMessage: 'Off', - } -); - -export const SYNC_ALERTS_HELP = i18n.translate('xpack.cases.components.create.syncAlertHelpText', { - defaultMessage: - 'Enabling this option will sync the status of alerts in this case with the case status.', -}); - -export const ALERT = i18n.translate('xpack.cases.common.alertLabel', { - defaultMessage: 'Alert', -}); - -export const ALERT_ADDED_TO_CASE = i18n.translate('xpack.cases.common.alertAddedToCase', { - defaultMessage: 'added to case', -}); - -export const SELECTABLE_MESSAGE_COLLECTIONS = i18n.translate( - 'xpack.cases.common.allCases.table.selectableMessageCollections', - { - defaultMessage: 'Cases with sub-cases cannot be selected', - } -); diff --git a/x-pack/plugins/cases/public/components/__mock__/form.ts b/x-pack/plugins/cases/public/components/__mock__/form.ts deleted file mode 100644 index 6d3e8353e630a..0000000000000 --- a/x-pack/plugins/cases/public/components/__mock__/form.ts +++ /dev/null @@ -1,50 +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 { useForm } from '../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form'; -import { useFormData } from '../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form_data'; - -jest.mock('../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form'); -jest.mock( - '../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form_data' -); - -export const mockFormHook = { - isSubmitted: false, - isSubmitting: false, - isValid: true, - submit: jest.fn(), - subscribe: jest.fn(), - setFieldValue: jest.fn(), - setFieldErrors: jest.fn(), - getFields: jest.fn(), - getFormData: jest.fn(), - /* Returns a list of all errors in the form */ - getErrors: jest.fn(), - reset: jest.fn(), - __options: {}, - __formData$: {}, - __addField: jest.fn(), - __removeField: jest.fn(), - __validateFields: jest.fn(), - __updateFormDataAt: jest.fn(), - __readFieldConfigFromSchema: jest.fn(), - __getFieldDefaultValue: jest.fn(), -}; - -export const getFormMock = (sampleData: any) => ({ - ...mockFormHook, - submit: () => - Promise.resolve({ - data: sampleData, - isValid: true, - }), - getFormData: () => sampleData, -}); - -export const useFormMock = useForm as jest.Mock; -export const useFormDataMock = useFormData as jest.Mock; diff --git a/x-pack/plugins/cases/public/components/configure_cases/__mock__/index.tsx b/x-pack/plugins/cases/public/components/configure_cases/__mock__/index.tsx deleted file mode 100644 index e3abbeadd2d3c..0000000000000 --- a/x-pack/plugins/cases/public/components/configure_cases/__mock__/index.tsx +++ /dev/null @@ -1,60 +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 { ConnectorTypes } from '../../../../common'; -import { ActionConnector } from '../../../containers/configure/types'; -import { UseConnectorsResponse } from '../../../containers/configure/use_connectors'; -import { ReturnUseCaseConfigure } from '../../../containers/configure/use_configure'; -import { UseActionTypesResponse } from '../../../containers/configure/use_action_types'; -import { connectorsMock, actionTypesMock } from '../../../containers/configure/mock'; -export { mappings } from '../../../containers/configure/mock'; -export const connectors: ActionConnector[] = connectorsMock; - -export const searchURL = - '?timerange=(global:(linkTo:!(),timerange:(from:1585487656371,fromStr:now-24h,kind:relative,to:1585574056371,toStr:now)),timeline:(linkTo:!(),timerange:(from:1585227005527,kind:absolute,to:1585313405527)))'; - -export const useCaseConfigureResponse: ReturnUseCaseConfigure = { - closureType: 'close-by-user', - connector: { - id: 'none', - name: 'none', - type: ConnectorTypes.none, - fields: null, - }, - currentConfiguration: { - connector: { - id: 'none', - name: 'none', - type: ConnectorTypes.none, - fields: null, - }, - closureType: 'close-by-user', - }, - firstLoad: false, - loading: false, - mappings: [], - persistCaseConfigure: jest.fn(), - persistLoading: false, - refetchCaseConfigure: jest.fn(), - setClosureType: jest.fn(), - setConnector: jest.fn(), - setCurrentConfiguration: jest.fn(), - setMappings: jest.fn(), - version: '', -}; - -export const useConnectorsResponse: UseConnectorsResponse = { - loading: false, - connectors, - refetchConnectors: jest.fn(), -}; - -export const useActionTypesResponse: UseActionTypesResponse = { - loading: false, - actionTypes: actionTypesMock, - refetchActionTypes: jest.fn(), -}; diff --git a/x-pack/plugins/cases/public/components/configure_cases/closure_options.test.tsx b/x-pack/plugins/cases/public/components/configure_cases/closure_options.test.tsx deleted file mode 100644 index 56123a934d51f..0000000000000 --- a/x-pack/plugins/cases/public/components/configure_cases/closure_options.test.tsx +++ /dev/null @@ -1,58 +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 from 'react'; -import { mount, ReactWrapper } from 'enzyme'; - -import { ClosureOptions, ClosureOptionsProps } from './closure_options'; -import { TestProviders } from '../../common/mock'; -import { ClosureOptionsRadio } from './closure_options_radio'; - -describe('ClosureOptions', () => { - let wrapper: ReactWrapper; - const onChangeClosureType = jest.fn(); - const props: ClosureOptionsProps = { - disabled: false, - closureTypeSelected: 'close-by-user', - onChangeClosureType, - }; - - beforeAll(() => { - wrapper = mount(, { wrappingComponent: TestProviders }); - }); - - test('it shows the closure options form group', () => { - expect( - wrapper.find('[data-test-subj="case-closure-options-form-group"]').first().exists() - ).toBe(true); - }); - - test('it shows the closure options form row', () => { - expect(wrapper.find('[data-test-subj="case-closure-options-form-row"]').first().exists()).toBe( - true - ); - }); - - test('it shows closure options', () => { - expect(wrapper.find('[data-test-subj="case-closure-options-radio"]').first().exists()).toBe( - true - ); - }); - - test('it pass the correct props to child', () => { - const closureOptionsRadioComponent = wrapper.find(ClosureOptionsRadio); - expect(closureOptionsRadioComponent.props().disabled).toEqual(false); - expect(closureOptionsRadioComponent.props().closureTypeSelected).toEqual('close-by-user'); - expect(closureOptionsRadioComponent.props().onChangeClosureType).toEqual(onChangeClosureType); - }); - - test('the closure type is changed successfully', () => { - wrapper.find('input[id="close-by-pushing"]').simulate('change'); - - expect(onChangeClosureType).toHaveBeenCalledWith('close-by-pushing'); - }); -}); diff --git a/x-pack/plugins/cases/public/components/configure_cases/closure_options.tsx b/x-pack/plugins/cases/public/components/configure_cases/closure_options.tsx deleted file mode 100644 index ba892116320ce..0000000000000 --- a/x-pack/plugins/cases/public/components/configure_cases/closure_options.tsx +++ /dev/null @@ -1,54 +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 from 'react'; -import { EuiDescribedFormGroup, EuiFormRow } from '@elastic/eui'; - -import { ClosureType } from '../../containers/configure/types'; -import { ClosureOptionsRadio } from './closure_options_radio'; -import * as i18n from './translations'; - -export interface ClosureOptionsProps { - closureTypeSelected: ClosureType; - disabled: boolean; - onChangeClosureType: (newClosureType: ClosureType) => void; -} - -const ClosureOptionsComponent: React.FC = ({ - closureTypeSelected, - disabled, - onChangeClosureType, -}) => { - return ( - {i18n.CASE_CLOSURE_OPTIONS_TITLE}} - description={ - <> -

{i18n.CASE_CLOSURE_OPTIONS_DESC}

-

{i18n.CASE_COLSURE_OPTIONS_SUB_CASES}

- - } - data-test-subj="case-closure-options-form-group" - > - - - -
- ); -}; - -export const ClosureOptions = React.memo(ClosureOptionsComponent); diff --git a/x-pack/plugins/cases/public/components/configure_cases/closure_options_radio.test.tsx b/x-pack/plugins/cases/public/components/configure_cases/closure_options_radio.test.tsx deleted file mode 100644 index b9885b4e07d48..0000000000000 --- a/x-pack/plugins/cases/public/components/configure_cases/closure_options_radio.test.tsx +++ /dev/null @@ -1,77 +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 from 'react'; -import { ReactWrapper, mount } from 'enzyme'; - -import { ClosureOptionsRadio, ClosureOptionsRadioComponentProps } from './closure_options_radio'; -import { TestProviders } from '../../common/mock'; - -describe('ClosureOptionsRadio', () => { - let wrapper: ReactWrapper; - const onChangeClosureType = jest.fn(); - const props: ClosureOptionsRadioComponentProps = { - disabled: false, - closureTypeSelected: 'close-by-user', - onChangeClosureType, - }; - - beforeAll(() => { - wrapper = mount(, { wrappingComponent: TestProviders }); - }); - - test('it renders', () => { - expect(wrapper.find('[data-test-subj="closure-options-radio-group"]').first().exists()).toBe( - true - ); - }); - - test('it shows the correct number of radio buttons', () => { - expect(wrapper.find('input[name="closure_options"]')).toHaveLength(2); - }); - - test('it renders close by user radio button', () => { - expect(wrapper.find('input[id="close-by-user"]').exists()).toBeTruthy(); - }); - - test('it renders close by pushing radio button', () => { - expect(wrapper.find('input[id="close-by-pushing"]').exists()).toBeTruthy(); - }); - - test('it disables the close by user radio button', () => { - const newWrapper = mount(, { - wrappingComponent: TestProviders, - }); - - expect(newWrapper.find('input[id="close-by-user"]').prop('disabled')).toEqual(true); - }); - - test('it disables correctly the close by pushing radio button', () => { - const newWrapper = mount(, { - wrappingComponent: TestProviders, - }); - - expect(newWrapper.find('input[id="close-by-pushing"]').prop('disabled')).toEqual(true); - }); - - test('it selects the correct radio button', () => { - const newWrapper = mount( - , - { - wrappingComponent: TestProviders, - } - ); - expect(newWrapper.find('input[id="close-by-pushing"]').prop('checked')).toEqual(true); - }); - - test('it calls the onChangeClosureType function', () => { - wrapper.find('input[id="close-by-pushing"]').simulate('change'); - wrapper.update(); - expect(onChangeClosureType).toHaveBeenCalled(); - expect(onChangeClosureType).toHaveBeenCalledWith('close-by-pushing'); - }); -}); diff --git a/x-pack/plugins/cases/public/components/configure_cases/closure_options_radio.tsx b/x-pack/plugins/cases/public/components/configure_cases/closure_options_radio.tsx deleted file mode 100644 index cb6fa0953a796..0000000000000 --- a/x-pack/plugins/cases/public/components/configure_cases/closure_options_radio.tsx +++ /dev/null @@ -1,60 +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, { ReactNode, useCallback } from 'react'; -import { EuiRadioGroup } from '@elastic/eui'; - -import { ClosureType } from '../../containers/configure/types'; -import * as i18n from './translations'; - -interface ClosureRadios { - id: ClosureType; - label: ReactNode; -} - -const radios: ClosureRadios[] = [ - { - id: 'close-by-user', - label: i18n.CASE_CLOSURE_OPTIONS_MANUAL, - }, - { - id: 'close-by-pushing', - label: i18n.CASE_CLOSURE_OPTIONS_NEW_INCIDENT, - }, -]; - -export interface ClosureOptionsRadioComponentProps { - closureTypeSelected: ClosureType; - disabled: boolean; - onChangeClosureType: (newClosureType: ClosureType) => void; -} - -const ClosureOptionsRadioComponent: React.FC = ({ - closureTypeSelected, - disabled, - onChangeClosureType, -}) => { - const onChangeLocal = useCallback( - (id: string) => { - onChangeClosureType(id as ClosureType); - }, - [onChangeClosureType] - ); - - return ( - - ); -}; - -export const ClosureOptionsRadio = React.memo(ClosureOptionsRadioComponent); diff --git a/x-pack/plugins/cases/public/components/configure_cases/connectors.test.tsx b/x-pack/plugins/cases/public/components/configure_cases/connectors.test.tsx deleted file mode 100644 index d5b9a885f2c6d..0000000000000 --- a/x-pack/plugins/cases/public/components/configure_cases/connectors.test.tsx +++ /dev/null @@ -1,115 +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 from 'react'; -import { mount, ReactWrapper } from 'enzyme'; - -import { Connectors, Props } from './connectors'; -import { TestProviders } from '../../common/mock'; -import { ConnectorsDropdown } from './connectors_dropdown'; -import { connectors } from './__mock__'; -import { ConnectorTypes } from '../../../common'; - -describe('Connectors', () => { - let wrapper: ReactWrapper; - const onChangeConnector = jest.fn(); - const handleShowEditFlyout = jest.fn(); - - const props: Props = { - connectors, - disabled: false, - handleShowEditFlyout, - isLoading: false, - mappings: [], - onChangeConnector, - selectedConnector: { id: 'none', type: ConnectorTypes.none }, - updateConnectorDisabled: false, - }; - - beforeAll(() => { - wrapper = mount(, { wrappingComponent: TestProviders }); - }); - - test('it shows the connectors from group', () => { - expect(wrapper.find('[data-test-subj="case-connectors-form-group"]').first().exists()).toBe( - true - ); - }); - - test('it shows the connectors form row', () => { - expect(wrapper.find('[data-test-subj="case-connectors-form-row"]').first().exists()).toBe(true); - }); - - test('it shows the connectors dropdown', () => { - expect(wrapper.find('[data-test-subj="case-connectors-dropdown"]').first().exists()).toBe(true); - }); - - test('it pass the correct props to child', () => { - const connectorsDropdownProps = wrapper.find(ConnectorsDropdown).props(); - expect(connectorsDropdownProps).toMatchObject({ - disabled: false, - isLoading: false, - connectors, - selectedConnector: 'none', - onChange: props.onChangeConnector, - }); - }); - - test('the connector is changed successfully', () => { - wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); - wrapper.find('button[data-test-subj="dropdown-connector-resilient-2"]').simulate('click'); - - expect(onChangeConnector).toHaveBeenCalled(); - expect(onChangeConnector).toHaveBeenCalledWith('resilient-2'); - }); - - test('the connector is changed successfully to none', () => { - onChangeConnector.mockClear(); - const newWrapper = mount( - , - { - wrappingComponent: TestProviders, - } - ); - - newWrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); - newWrapper.find('button[data-test-subj="dropdown-connector-no-connector"]').simulate('click'); - - expect(onChangeConnector).toHaveBeenCalled(); - expect(onChangeConnector).toHaveBeenCalledWith('none'); - }); - - test('it shows the add connector button', () => { - wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); - wrapper.update(); - - expect( - wrapper.find('button[data-test-subj="dropdown-connector-add-connector"]').exists() - ).toBeTruthy(); - }); - - test('the text of the update button is shown correctly', () => { - const newWrapper = mount( - , - { - wrappingComponent: TestProviders, - } - ); - - expect( - newWrapper - .find('button[data-test-subj="case-configure-update-selected-connector-button"]') - .text() - ).toBe('Update My Connector'); - }); -}); diff --git a/x-pack/plugins/cases/public/components/configure_cases/connectors.tsx b/x-pack/plugins/cases/public/components/configure_cases/connectors.tsx deleted file mode 100644 index 45be02e05e1f0..0000000000000 --- a/x-pack/plugins/cases/public/components/configure_cases/connectors.tsx +++ /dev/null @@ -1,119 +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, { useMemo } from 'react'; -import { - EuiDescribedFormGroup, - EuiFormRow, - EuiFlexGroup, - EuiFlexItem, - EuiLink, -} from '@elastic/eui'; - -import styled from 'styled-components'; - -import { ConnectorsDropdown } from './connectors_dropdown'; -import * as i18n from './translations'; - -import { ActionConnector, CaseConnectorMapping } from '../../containers/configure/types'; -import { Mapping } from './mapping'; -import { ConnectorTypes } from '../../../common'; - -const EuiFormRowExtended = styled(EuiFormRow)` - .euiFormRow__labelWrapper { - .euiFormRow__label { - width: 100%; - } - } -`; - -export interface Props { - connectors: ActionConnector[]; - disabled: boolean; - handleShowEditFlyout: () => void; - isLoading: boolean; - mappings: CaseConnectorMapping[]; - onChangeConnector: (id: string) => void; - selectedConnector: { id: string; type: string }; - updateConnectorDisabled: boolean; -} -const ConnectorsComponent: React.FC = ({ - connectors, - disabled, - handleShowEditFlyout, - isLoading, - mappings, - onChangeConnector, - selectedConnector, - updateConnectorDisabled, -}) => { - const connectorsName = useMemo( - () => connectors.find((c) => c.id === selectedConnector.id)?.name ?? 'none', - [connectors, selectedConnector.id] - ); - - const dropDownLabel = useMemo( - () => ( - - {i18n.INCIDENT_MANAGEMENT_SYSTEM_LABEL} - - {connectorsName !== 'none' && ( - - {i18n.UPDATE_SELECTED_CONNECTOR(connectorsName)} - - )} - - - ), - [connectorsName, handleShowEditFlyout, updateConnectorDisabled] - ); - return ( - <> - {i18n.INCIDENT_MANAGEMENT_SYSTEM_TITLE}} - description={i18n.INCIDENT_MANAGEMENT_SYSTEM_DESC} - data-test-subj="case-connectors-form-group" - > - - - - - - {selectedConnector.type !== ConnectorTypes.none ? ( - - - - ) : null} - - - - - ); -}; - -export const Connectors = React.memo(ConnectorsComponent); diff --git a/x-pack/plugins/cases/public/components/configure_cases/connectors_dropdown.test.tsx b/x-pack/plugins/cases/public/components/configure_cases/connectors_dropdown.test.tsx deleted file mode 100644 index 5149052d9a4bf..0000000000000 --- a/x-pack/plugins/cases/public/components/configure_cases/connectors_dropdown.test.tsx +++ /dev/null @@ -1,203 +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 from 'react'; -import { mount, ReactWrapper } from 'enzyme'; -import { EuiSuperSelect } from '@elastic/eui'; - -import { ConnectorsDropdown, Props } from './connectors_dropdown'; -import { TestProviders } from '../../common/mock'; -import { connectors } from './__mock__'; - -describe('ConnectorsDropdown', () => { - let wrapper: ReactWrapper; - const props: Props = { - disabled: false, - connectors, - isLoading: false, - onChange: jest.fn(), - selectedConnector: 'none', - }; - - beforeAll(() => { - wrapper = mount(, { wrappingComponent: TestProviders }); - }); - - test('it renders', () => { - expect(wrapper.find('[data-test-subj="dropdown-connectors"]').first().exists()).toBe(true); - }); - - test('it formats the connectors correctly', () => { - const selectProps = wrapper.find(EuiSuperSelect).props(); - - expect(selectProps.options).toMatchInlineSnapshot(` - Array [ - Object { - "data-test-subj": "dropdown-connector-no-connector", - "inputDisplay": - - - - - - No connector selected - - - , - "value": "none", - }, - Object { - "data-test-subj": "dropdown-connector-servicenow-1", - "inputDisplay": - - - - - - My Connector - - - , - "value": "servicenow-1", - }, - Object { - "data-test-subj": "dropdown-connector-resilient-2", - "inputDisplay": - - - - - - My Connector 2 - - - , - "value": "resilient-2", - }, - Object { - "data-test-subj": "dropdown-connector-jira-1", - "inputDisplay": - - - - - - Jira - - - , - "value": "jira-1", - }, - Object { - "data-test-subj": "dropdown-connector-servicenow-sir", - "inputDisplay": - - - - - - My Connector SIR - - - , - "value": "servicenow-sir", - }, - ] - `); - }); - - test('it disables the dropdown', () => { - const newWrapper = mount(, { - wrappingComponent: TestProviders, - }); - - expect( - newWrapper.find('[data-test-subj="dropdown-connectors"]').first().prop('disabled') - ).toEqual(true); - }); - - test('it loading correctly', () => { - const newWrapper = mount(, { - wrappingComponent: TestProviders, - }); - - expect( - newWrapper.find('[data-test-subj="dropdown-connectors"]').first().prop('isLoading') - ).toEqual(true); - }); - - test('it selects the correct connector', () => { - const newWrapper = mount(, { - wrappingComponent: TestProviders, - }); - - expect(newWrapper.find('button span:not([data-euiicon-type])').text()).toEqual('My Connector'); - }); - - test('if the props hideConnectorServiceNowSir is true, the connector should not be part of the list of options ', () => { - const newWrapper = mount( - , - { - wrappingComponent: TestProviders, - } - ); - const selectProps = newWrapper.find(EuiSuperSelect).props(); - const options = selectProps.options as Array<{ 'data-test-subj': string }>; - expect( - options.some((o) => o['data-test-subj'] === 'dropdown-connector-servicenow-1') - ).toBeTruthy(); - expect( - options.some((o) => o['data-test-subj'] === 'dropdown-connector-servicenow-sir') - ).toBeFalsy(); - }); -}); diff --git a/x-pack/plugins/cases/public/components/configure_cases/connectors_dropdown.tsx b/x-pack/plugins/cases/public/components/configure_cases/connectors_dropdown.tsx deleted file mode 100644 index 21ef5c490b17a..0000000000000 --- a/x-pack/plugins/cases/public/components/configure_cases/connectors_dropdown.tsx +++ /dev/null @@ -1,121 +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, { useMemo } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiSuperSelect } from '@elastic/eui'; -import styled from 'styled-components'; - -import { ConnectorTypes } from '../../../common'; -import { ActionConnector } from '../../containers/configure/types'; -import { connectorsConfiguration } from '../connectors'; -import * as i18n from './translations'; - -export interface Props { - connectors: ActionConnector[]; - disabled: boolean; - isLoading: boolean; - onChange: (id: string) => void; - selectedConnector: string; - appendAddConnectorButton?: boolean; - hideConnectorServiceNowSir?: boolean; -} - -const ICON_SIZE = 'm'; - -const EuiIconExtended = styled(EuiIcon)` - margin-right: 13px; - margin-bottom: 0 !important; -`; - -const noConnectorOption = { - value: 'none', - inputDisplay: ( - - - - - - {i18n.NO_CONNECTOR} - - - ), - 'data-test-subj': 'dropdown-connector-no-connector', -}; - -const addNewConnector = { - value: 'add-connector', - inputDisplay: ( - - {i18n.ADD_NEW_CONNECTOR} - - ), - 'data-test-subj': 'dropdown-connector-add-connector', -}; - -const ConnectorsDropdownComponent: React.FC = ({ - connectors, - disabled, - isLoading, - onChange, - selectedConnector, - appendAddConnectorButton = false, - hideConnectorServiceNowSir = false, -}) => { - const connectorsAsOptions = useMemo(() => { - const connectorsFormatted = connectors.reduce( - (acc, connector) => { - if (hideConnectorServiceNowSir && connector.actionTypeId === ConnectorTypes.serviceNowSIR) { - return acc; - } - - return [ - ...acc, - { - value: connector.id, - inputDisplay: ( - - - - - - {connector.name} - - - ), - 'data-test-subj': `dropdown-connector-${connector.id}`, - }, - ]; - }, - [noConnectorOption] - ); - - if (appendAddConnectorButton) { - return [...connectorsFormatted, addNewConnector]; - } - - return connectorsFormatted; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [connectors]); - - return ( - - ); -}; - -export const ConnectorsDropdown = React.memo(ConnectorsDropdownComponent); diff --git a/x-pack/plugins/cases/public/components/configure_cases/field_mapping.test.tsx b/x-pack/plugins/cases/public/components/configure_cases/field_mapping.test.tsx deleted file mode 100644 index 8c2a66ad7ee53..0000000000000 --- a/x-pack/plugins/cases/public/components/configure_cases/field_mapping.test.tsx +++ /dev/null @@ -1,55 +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 from 'react'; -import { mount, ReactWrapper } from 'enzyme'; - -import { FieldMapping, FieldMappingProps } from './field_mapping'; -import { mappings } from './__mock__'; -import { TestProviders } from '../../common/mock'; -import { FieldMappingRowStatic } from './field_mapping_row_static'; - -describe('FieldMappingRow', () => { - let wrapper: ReactWrapper; - const props: FieldMappingProps = { - isLoading: false, - mappings, - connectorActionTypeId: '.servicenow', - }; - - beforeAll(() => { - wrapper = mount(, { wrappingComponent: TestProviders }); - }); - test('it renders', () => { - expect( - wrapper.find('[data-test-subj="case-configure-field-mappings-row-wrapper"]').first().exists() - ).toBe(true); - - expect(wrapper.find(FieldMappingRowStatic).length).toEqual(3); - }); - - test('it does not render without mappings', () => { - const newWrapper = mount(, { - wrappingComponent: TestProviders, - }); - expect( - newWrapper - .find('[data-test-subj="case-configure-field-mappings-row-wrapper"]') - .first() - .exists() - ).toBe(false); - }); - - test('it pass the corrects props to mapping row', () => { - const rows = wrapper.find(FieldMappingRowStatic); - rows.forEach((row, index) => { - expect(row.prop('casesField')).toEqual(mappings[index].source); - expect(row.prop('selectedActionType')).toEqual(mappings[index].actionType); - expect(row.prop('selectedThirdParty')).toEqual(mappings[index].target); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/configure_cases/field_mapping.tsx b/x-pack/plugins/cases/public/components/configure_cases/field_mapping.tsx deleted file mode 100644 index ef7e8ecda0c87..0000000000000 --- a/x-pack/plugins/cases/public/components/configure_cases/field_mapping.tsx +++ /dev/null @@ -1,73 +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, { useMemo } from 'react'; -import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; -import styled from 'styled-components'; - -import { FieldMappingRowStatic } from './field_mapping_row_static'; -import * as i18n from './translations'; - -import { CaseConnectorMapping } from '../../containers/configure/types'; -import { connectorsConfiguration } from '../connectors'; - -const FieldRowWrapper = styled.div` - margin: 10px 0; - font-size: 14px; -`; - -export interface FieldMappingProps { - connectorActionTypeId: string; - isLoading: boolean; - mappings: CaseConnectorMapping[]; -} - -const FieldMappingComponent: React.FC = ({ - connectorActionTypeId, - isLoading, - mappings, -}) => { - const selectedConnector = useMemo( - () => connectorsConfiguration[connectorActionTypeId] ?? { fields: {} }, - [connectorActionTypeId] - ); - return mappings.length ? ( - - - {' '} - - - {i18n.FIELD_MAPPING_FIRST_COL} - - - - {i18n.FIELD_MAPPING_SECOND_COL(selectedConnector.name)} - - - - {i18n.FIELD_MAPPING_THIRD_COL} - - - - - - {mappings.map((item) => ( - - ))} - - - - ) : null; -}; - -export const FieldMapping = React.memo(FieldMappingComponent); diff --git a/x-pack/plugins/cases/public/components/configure_cases/field_mapping_row_static.tsx b/x-pack/plugins/cases/public/components/configure_cases/field_mapping_row_static.tsx deleted file mode 100644 index 52672197ecb55..0000000000000 --- a/x-pack/plugins/cases/public/components/configure_cases/field_mapping_row_static.tsx +++ /dev/null @@ -1,60 +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, { useMemo } from 'react'; -import { EuiCode, EuiFlexItem, EuiFlexGroup, EuiIcon, EuiLoadingSpinner } from '@elastic/eui'; - -import { capitalize } from 'lodash/fp'; -import { CaseField, ActionType, ThirdPartyField } from '../../containers/configure/types'; - -export interface RowProps { - isLoading: boolean; - casesField: CaseField; - selectedActionType: ActionType; - selectedThirdParty: ThirdPartyField; -} - -const FieldMappingRowComponent: React.FC = ({ - isLoading, - casesField, - selectedActionType, - selectedThirdParty, -}) => { - const selectedActionTypeCapitalized = useMemo(() => capitalize(selectedActionType), [ - selectedActionType, - ]); - return ( - - - - - {casesField} - - - - - - - - - - {isLoading ? ( - - ) : ( - {selectedThirdParty} - )} - - - - - {isLoading ? : selectedActionTypeCapitalized} - - - ); -}; - -export const FieldMappingRowStatic = React.memo(FieldMappingRowComponent); diff --git a/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx b/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx deleted file mode 100644 index 898d6cde19a77..0000000000000 --- a/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx +++ /dev/null @@ -1,591 +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 from 'react'; -import { ReactWrapper, mount } from 'enzyme'; - -import { ConfigureCases } from '.'; -import { TestProviders } from '../../common/mock'; -import { Connectors } from './connectors'; -import { ClosureOptions } from './closure_options'; -import { - ActionConnector, - ConnectorAddFlyout, - ConnectorEditFlyout, - TriggersAndActionsUIPublicPluginStart, -} from '../../../../triggers_actions_ui/public'; -import { actionTypeRegistryMock } from '../../../../triggers_actions_ui/public/application/action_type_registry.mock'; - -import { useKibana } from '../../common/lib/kibana'; -import { useConnectors } from '../../containers/configure/use_connectors'; -import { useCaseConfigure } from '../../containers/configure/use_configure'; -import { useActionTypes } from '../../containers/configure/use_action_types'; - -import { - connectors, - searchURL, - useCaseConfigureResponse, - useConnectorsResponse, - useActionTypesResponse, -} from './__mock__'; -import { ConnectorTypes } from '../../../common'; - -jest.mock('../../common/lib/kibana'); -jest.mock('../../containers/configure/use_connectors'); -jest.mock('../../containers/configure/use_configure'); -jest.mock('../../containers/configure/use_action_types'); - -const useKibanaMock = useKibana as jest.Mocked; -const useConnectorsMock = useConnectors as jest.Mock; -const useCaseConfigureMock = useCaseConfigure as jest.Mock; -const useGetUrlSearchMock = jest.fn(); -const useActionTypesMock = useActionTypes as jest.Mock; - -describe('ConfigureCases', () => { - beforeEach(() => { - useKibanaMock().services.triggersActionsUi = ({ - actionTypeRegistry: actionTypeRegistryMock.create(), - getAddConnectorFlyout: jest.fn().mockImplementation(() => ( - {}} - actionTypeRegistry={actionTypeRegistryMock.create()} - actionTypes={[ - { - id: '.servicenow', - name: 'servicenow', - enabled: true, - enabledInConfig: true, - enabledInLicense: true, - minimumLicenseRequired: 'gold', - }, - { - id: '.jira', - name: 'jira', - enabled: true, - enabledInConfig: true, - enabledInLicense: true, - minimumLicenseRequired: 'gold', - }, - { - id: '.resilient', - name: 'resilient', - enabled: true, - enabledInConfig: true, - enabledInLicense: true, - minimumLicenseRequired: 'gold', - }, - ]} - /> - )), - getEditConnectorFlyout: jest - .fn() - .mockImplementation(() => ( - {}} - actionTypeRegistry={actionTypeRegistryMock.create()} - initialConnector={connectors[1] as ActionConnector} - /> - )), - } as unknown) as TriggersAndActionsUIPublicPluginStart; - - useActionTypesMock.mockImplementation(() => useActionTypesResponse); - }); - - describe('rendering', () => { - let wrapper: ReactWrapper; - beforeEach(() => { - useCaseConfigureMock.mockImplementation(() => useCaseConfigureResponse); - useConnectorsMock.mockImplementation(() => ({ ...useConnectorsResponse, connectors: [] })); - useGetUrlSearchMock.mockImplementation(() => searchURL); - - wrapper = mount(, { wrappingComponent: TestProviders }); - }); - - test('it renders the Connectors', () => { - expect(wrapper.find('[data-test-subj="dropdown-connectors"]').exists()).toBeTruthy(); - }); - - test('it renders the ClosureType', () => { - expect(wrapper.find('[data-test-subj="closure-options-radio-group"]').exists()).toBeTruthy(); - }); - - test('it does NOT render the ConnectorAddFlyout', () => { - // Components from triggersActionsUi do not have a data-test-subj - expect(wrapper.find(ConnectorAddFlyout).exists()).toBeFalsy(); - }); - - test('it does NOT render the ConnectorEditFlyout', () => { - // Components from triggersActionsUi do not have a data-test-subj - expect(wrapper.find(ConnectorEditFlyout).exists()).toBeFalsy(); - }); - - test('it does NOT render the EuiCallOut', () => { - expect( - wrapper.find('[data-test-subj="configure-cases-warning-callout"]').exists() - ).toBeFalsy(); - }); - }); - - describe('Unhappy path', () => { - let wrapper: ReactWrapper; - - beforeEach(() => { - useCaseConfigureMock.mockImplementation(() => ({ - ...useCaseConfigureResponse, - closureType: 'close-by-user', - connector: { - id: 'not-id', - name: 'unchanged', - type: ConnectorTypes.none, - fields: null, - }, - currentConfiguration: { - connector: { - id: 'not-id', - name: 'unchanged', - type: ConnectorTypes.none, - fields: null, - }, - closureType: 'close-by-user', - }, - })); - useConnectorsMock.mockImplementation(() => ({ ...useConnectorsResponse, connectors: [] })); - useGetUrlSearchMock.mockImplementation(() => searchURL); - wrapper = mount(, { wrappingComponent: TestProviders }); - }); - - test('it shows the warning callout when configuration is invalid', () => { - expect( - wrapper.find('[data-test-subj="configure-cases-warning-callout"]').exists() - ).toBeTruthy(); - }); - - test('it hides the update connector button when the connectorId is invalid', () => { - expect( - wrapper - .find('button[data-test-subj="case-configure-update-selected-connector-button"]') - .exists() - ).toBeFalsy(); - }); - }); - - describe('Happy path', () => { - let wrapper: ReactWrapper; - - beforeEach(() => { - useCaseConfigureMock.mockImplementation(() => ({ - ...useCaseConfigureResponse, - mappings: [], - closureType: 'close-by-user', - connector: { - id: 'servicenow-1', - name: 'unchanged', - type: ConnectorTypes.serviceNowITSM, - fields: null, - }, - currentConfiguration: { - connector: { - id: 'servicenow-1', - name: 'unchanged', - type: ConnectorTypes.serviceNowITSM, - fields: null, - }, - closureType: 'close-by-user', - }, - })); - useConnectorsMock.mockImplementation(() => useConnectorsResponse); - useGetUrlSearchMock.mockImplementation(() => searchURL); - - wrapper = mount(, { wrappingComponent: TestProviders }); - }); - - test('it renders with correct props', () => { - // Connector - expect(wrapper.find(Connectors).prop('connectors')).toEqual(connectors); - expect(wrapper.find(Connectors).prop('disabled')).toBe(false); - expect(wrapper.find(Connectors).prop('isLoading')).toBe(false); - expect(wrapper.find(Connectors).prop('selectedConnector').id).toBe('servicenow-1'); - - // ClosureOptions - expect(wrapper.find(ClosureOptions).prop('disabled')).toBe(false); - expect(wrapper.find(ClosureOptions).prop('closureTypeSelected')).toBe('close-by-user'); - - // Flyouts - expect(wrapper.find(ConnectorAddFlyout).exists()).toBe(false); - expect(wrapper.find(ConnectorEditFlyout).exists()).toBe(false); - }); - - test('it disables correctly when the user cannot crud', () => { - const newWrapper = mount(, { - wrappingComponent: TestProviders, - }); - - expect(newWrapper.find('button[data-test-subj="dropdown-connectors"]').prop('disabled')).toBe( - true - ); - - expect( - newWrapper - .find('button[data-test-subj="case-configure-update-selected-connector-button"]') - .prop('disabled') - ).toBe(true); - - // Two closure options - expect( - newWrapper - .find('[data-test-subj="closure-options-radio-group"] input') - .first() - .prop('disabled') - ).toBe(true); - - expect( - newWrapper - .find('[data-test-subj="closure-options-radio-group"] input') - .at(1) - .prop('disabled') - ).toBe(true); - }); - }); - - describe('loading connectors', () => { - let wrapper: ReactWrapper; - - beforeEach(() => { - useCaseConfigureMock.mockImplementation(() => ({ - ...useCaseConfigureResponse, - mapping: null, - closureType: 'close-by-user', - connector: { - id: 'resilient-2', - name: 'unchanged', - type: ConnectorTypes.resilient, - fields: null, - }, - currentConfiguration: { - connector: { - id: 'servicenow-1', - name: 'unchanged', - type: ConnectorTypes.serviceNowITSM, - fields: null, - }, - closureType: 'close-by-user', - }, - })); - - useConnectorsMock.mockImplementation(() => ({ - ...useConnectorsResponse, - loading: true, - })); - - useGetUrlSearchMock.mockImplementation(() => searchURL); - wrapper = mount(, { wrappingComponent: TestProviders }); - }); - - test('it disables correctly Connector when loading connectors', () => { - expect( - wrapper.find('button[data-test-subj="dropdown-connectors"]').prop('disabled') - ).toBeTruthy(); - }); - - test('it pass the correct value to isLoading attribute on Connector', () => { - expect(wrapper.find(Connectors).prop('isLoading')).toBe(true); - }); - - test('it disables correctly ClosureOptions when loading connectors', () => { - expect(wrapper.find(ClosureOptions).prop('disabled')).toBe(true); - }); - - test('it hides the update connector button when loading the connectors', () => { - expect( - wrapper - .find('button[data-test-subj="case-configure-update-selected-connector-button"]') - .prop('disabled') - ).toBe(true); - }); - - test('it shows isLoading when loading action types', () => { - useConnectorsMock.mockImplementation(() => ({ - ...useConnectorsResponse, - loading: false, - })); - - useActionTypesMock.mockImplementation(() => ({ ...useActionTypesResponse, loading: true })); - - wrapper = mount(, { wrappingComponent: TestProviders }); - expect(wrapper.find(Connectors).prop('isLoading')).toBe(true); - }); - }); - - describe('saving configuration', () => { - let wrapper: ReactWrapper; - - beforeEach(() => { - useCaseConfigureMock.mockImplementation(() => ({ - ...useCaseConfigureResponse, - connector: { - id: 'servicenow-1', - name: 'SN', - type: ConnectorTypes.serviceNowITSM, - fields: null, - }, - persistLoading: true, - })); - - useConnectorsMock.mockImplementation(() => useConnectorsResponse); - useGetUrlSearchMock.mockImplementation(() => searchURL); - wrapper = mount(, { wrappingComponent: TestProviders }); - }); - - test('it disables correctly Connector when saving configuration', () => { - expect(wrapper.find(Connectors).prop('disabled')).toBe(true); - }); - - test('it disables correctly ClosureOptions when saving configuration', () => { - expect( - wrapper - .find('[data-test-subj="closure-options-radio-group"] input') - .first() - .prop('disabled') - ).toBe(true); - - expect( - wrapper.find('[data-test-subj="closure-options-radio-group"] input').at(1).prop('disabled') - ).toBe(true); - }); - - test('it disables the update connector button when saving the configuration', () => { - expect( - wrapper - .find('button[data-test-subj="case-configure-update-selected-connector-button"]') - .prop('disabled') - ).toBe(true); - }); - }); - - describe('loading configuration', () => { - let wrapper: ReactWrapper; - - beforeEach(() => { - useCaseConfigureMock.mockImplementation(() => ({ - ...useCaseConfigureResponse, - loading: true, - })); - useConnectorsMock.mockImplementation(() => ({ - ...useConnectorsResponse, - })); - useGetUrlSearchMock.mockImplementation(() => searchURL); - wrapper = mount(, { wrappingComponent: TestProviders }); - }); - - test('it hides the update connector button when loading the configuration', () => { - expect( - wrapper - .find('button[data-test-subj="case-configure-update-selected-connector-button"]') - .exists() - ).toBeFalsy(); - }); - }); - - describe('connectors', () => { - let wrapper: ReactWrapper; - let persistCaseConfigure: jest.Mock; - - beforeEach(() => { - persistCaseConfigure = jest.fn(); - useCaseConfigureMock.mockImplementation(() => ({ - ...useCaseConfigureResponse, - mapping: null, - closureType: 'close-by-user', - connector: { - id: 'resilient-2', - name: 'My connector', - type: ConnectorTypes.resilient, - fields: null, - }, - currentConfiguration: { - connector: { - id: 'My connector', - name: 'My connector', - type: ConnectorTypes.jira, - fields: null, - }, - closureType: 'close-by-user', - }, - persistCaseConfigure, - })); - useConnectorsMock.mockImplementation(() => useConnectorsResponse); - useGetUrlSearchMock.mockImplementation(() => searchURL); - - wrapper = mount(, { wrappingComponent: TestProviders }); - }); - - test('it submits the configuration correctly when changing connector', () => { - wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); - wrapper.update(); - wrapper.find('button[data-test-subj="dropdown-connector-resilient-2"]').simulate('click'); - wrapper.update(); - - expect(persistCaseConfigure).toHaveBeenCalled(); - expect(persistCaseConfigure).toHaveBeenCalledWith({ - connector: { - id: 'resilient-2', - name: 'My Connector 2', - type: ConnectorTypes.resilient, - fields: null, - }, - closureType: 'close-by-user', - }); - }); - - test('the text of the update button is changed successfully', () => { - useCaseConfigureMock - .mockImplementationOnce(() => ({ - ...useCaseConfigureResponse, - connector: { - id: 'servicenow-1', - name: 'My connector', - type: ConnectorTypes.serviceNowITSM, - fields: null, - }, - })) - .mockImplementation(() => ({ - ...useCaseConfigureResponse, - connector: { - id: 'resilient-2', - name: 'My connector 2', - type: ConnectorTypes.resilient, - fields: null, - }, - })); - - wrapper = mount(, { wrappingComponent: TestProviders }); - - wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); - wrapper.update(); - wrapper.find('button[data-test-subj="dropdown-connector-resilient-2"]').simulate('click'); - wrapper.update(); - - expect( - wrapper - .find('button[data-test-subj="case-configure-update-selected-connector-button"]') - .text() - ).toBe('Update My Connector 2'); - }); - }); -}); - -describe('closure options', () => { - let wrapper: ReactWrapper; - let persistCaseConfigure: jest.Mock; - - beforeEach(() => { - persistCaseConfigure = jest.fn(); - useCaseConfigureMock.mockImplementation(() => ({ - ...useCaseConfigureResponse, - mapping: null, - closureType: 'close-by-user', - connector: { - id: 'servicenow-1', - name: 'My connector', - type: ConnectorTypes.serviceNowITSM, - fields: null, - }, - currentConfiguration: { - connector: { - id: 'My connector', - name: 'My connector', - type: ConnectorTypes.jira, - fields: null, - }, - closureType: 'close-by-user', - }, - persistCaseConfigure, - })); - useConnectorsMock.mockImplementation(() => useConnectorsResponse); - useGetUrlSearchMock.mockImplementation(() => searchURL); - - wrapper = mount(, { wrappingComponent: TestProviders }); - }); - - test('it submits the configuration correctly when changing closure type', () => { - wrapper.find('input[id="close-by-pushing"]').simulate('change'); - wrapper.update(); - - expect(persistCaseConfigure).toHaveBeenCalled(); - expect(persistCaseConfigure).toHaveBeenCalledWith({ - connector: { - id: 'servicenow-1', - name: 'My connector', - type: ConnectorTypes.serviceNowITSM, - fields: null, - }, - closureType: 'close-by-pushing', - }); - }); -}); - -describe('user interactions', () => { - beforeEach(() => { - useCaseConfigureMock.mockImplementation(() => ({ - ...useCaseConfigureResponse, - mapping: null, - closureType: 'close-by-user', - connector: { - id: 'resilient-2', - name: 'unchanged', - type: ConnectorTypes.resilient, - fields: null, - }, - currentConfiguration: { - connector: { - id: 'resilient-2', - name: 'unchanged', - type: ConnectorTypes.serviceNowITSM, - fields: null, - }, - closureType: 'close-by-user', - }, - })); - useConnectorsMock.mockImplementation(() => useConnectorsResponse); - useGetUrlSearchMock.mockImplementation(() => searchURL); - }); - - test('it show the add flyout when pressing the add connector button', () => { - const wrapper = mount(, { wrappingComponent: TestProviders }); - wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); - wrapper.update(); - wrapper.find('button[data-test-subj="dropdown-connector-add-connector"]').simulate('click'); - wrapper.update(); - - expect(wrapper.find(ConnectorAddFlyout).exists()).toBe(true); - expect(wrapper.find(ConnectorAddFlyout).prop('actionTypes')).toEqual([ - expect.objectContaining({ - id: '.servicenow', - }), - expect.objectContaining({ - id: '.jira', - }), - expect.objectContaining({ - id: '.resilient', - }), - ]); - }); - - test('it show the edit flyout when pressing the update connector button', () => { - const wrapper = mount(, { wrappingComponent: TestProviders }); - wrapper - .find('button[data-test-subj="case-configure-update-selected-connector-button"]') - .simulate('click'); - wrapper.update(); - - expect(wrapper.find(ConnectorEditFlyout).exists()).toBe(true); - expect(wrapper.find(ConnectorEditFlyout).prop('initialConnector')).toEqual(connectors[1]); - expect( - wrapper.find('[data-test-subj="case-configure-action-bottom-bar"]').exists() - ).toBeFalsy(); - }); -}); diff --git a/x-pack/plugins/cases/public/components/configure_cases/index.tsx b/x-pack/plugins/cases/public/components/configure_cases/index.tsx deleted file mode 100644 index 3e352f119e840..0000000000000 --- a/x-pack/plugins/cases/public/components/configure_cases/index.tsx +++ /dev/null @@ -1,224 +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, useEffect, useMemo, useState } from 'react'; -import styled, { css } from 'styled-components'; - -import { EuiCallOut } from '@elastic/eui'; - -import { SUPPORTED_CONNECTORS } from '../../../common'; -import { useKibana } from '../../common/lib/kibana'; -import { useConnectors } from '../../containers/configure/use_connectors'; -import { useActionTypes } from '../../containers/configure/use_action_types'; -import { useCaseConfigure } from '../../containers/configure/use_configure'; - -import { ClosureType } from '../../containers/configure/types'; - -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ActionConnectorTableItem } from '../../../../triggers_actions_ui/public/types'; - -import { SectionWrapper } from '../wrappers'; -import { Connectors } from './connectors'; -import { ClosureOptions } from './closure_options'; -import { - getConnectorById, - getNoneConnector, - normalizeActionConnector, - normalizeCaseConnector, -} from './utils'; -import * as i18n from './translations'; - -const FormWrapper = styled.div` - ${({ theme }) => css` - & > * { - margin-top 40px; - } - - & > :first-child { - margin-top: 0; - } - - padding-top: ${theme.eui.paddingSizes.xl}; - padding-bottom: ${theme.eui.paddingSizes.xl}; - .euiFlyout { - z-index: ${theme.eui.euiZNavigation + 1}; - } - `} -`; - -interface ConfigureCasesComponentProps { - userCanCrud: boolean; -} - -const ConfigureCasesComponent: React.FC = ({ userCanCrud }) => { - const { triggersActionsUi } = useKibana().services; - - const [connectorIsValid, setConnectorIsValid] = useState(true); - const [addFlyoutVisible, setAddFlyoutVisibility] = useState(false); - const [editFlyoutVisible, setEditFlyoutVisibility] = useState(false); - const [editedConnectorItem, setEditedConnectorItem] = useState( - null - ); - - const { - connector, - closureType, - loading: loadingCaseConfigure, - mappings, - persistLoading, - persistCaseConfigure, - refetchCaseConfigure, - setConnector, - setClosureType, - } = useCaseConfigure(); - - const { loading: isLoadingConnectors, connectors, refetchConnectors } = useConnectors(); - const { loading: isLoadingActionTypes, actionTypes, refetchActionTypes } = useActionTypes(); - const supportedActionTypes = useMemo( - () => actionTypes.filter((actionType) => SUPPORTED_CONNECTORS.includes(actionType.id)), - [actionTypes] - ); - - const onConnectorUpdate = useCallback(async () => { - refetchConnectors(); - refetchActionTypes(); - refetchCaseConfigure(); - }, [refetchActionTypes, refetchCaseConfigure, refetchConnectors]); - - const isLoadingAny = - isLoadingConnectors || persistLoading || loadingCaseConfigure || isLoadingActionTypes; - const updateConnectorDisabled = isLoadingAny || !connectorIsValid || connector.id === 'none'; - const onClickUpdateConnector = useCallback(() => { - setEditFlyoutVisibility(true); - }, []); - - const onCloseAddFlyout = useCallback(() => setAddFlyoutVisibility(false), [ - setAddFlyoutVisibility, - ]); - - const onCloseEditFlyout = useCallback(() => setEditFlyoutVisibility(false), []); - - const onChangeConnector = useCallback( - (id: string) => { - if (id === 'add-connector') { - setAddFlyoutVisibility(true); - return; - } - - const actionConnector = getConnectorById(id, connectors); - const caseConnector = - actionConnector != null ? normalizeActionConnector(actionConnector) : getNoneConnector(); - - setConnector(caseConnector); - persistCaseConfigure({ - connector: caseConnector, - closureType, - }); - }, - [connectors, closureType, persistCaseConfigure, setConnector] - ); - - const onChangeClosureType = useCallback( - (type: ClosureType) => { - setClosureType(type); - persistCaseConfigure({ - connector, - closureType: type, - }); - }, - [connector, persistCaseConfigure, setClosureType] - ); - - useEffect(() => { - if ( - !isLoadingConnectors && - connector.id !== 'none' && - !connectors.some((c) => c.id === connector.id) - ) { - setConnectorIsValid(false); - } else if ( - !isLoadingConnectors && - (connector.id === 'none' || connectors.some((c) => c.id === connector.id)) - ) { - setConnectorIsValid(true); - } - }, [connectors, connector, isLoadingConnectors]); - - useEffect(() => { - if (!isLoadingConnectors && connector.id !== 'none') { - setEditedConnectorItem( - normalizeCaseConnector(connectors, connector) as ActionConnectorTableItem - ); - } - }, [connectors, connector, isLoadingConnectors]); - - const ConnectorAddFlyout = useMemo( - () => - triggersActionsUi.getAddConnectorFlyout({ - consumer: 'case', - onClose: onCloseAddFlyout, - actionTypes: supportedActionTypes, - reloadConnectors: onConnectorUpdate, - }), - // eslint-disable-next-line react-hooks/exhaustive-deps - [supportedActionTypes] - ); - - const ConnectorEditFlyout = useMemo( - () => - editedConnectorItem && editFlyoutVisible - ? triggersActionsUi.getEditConnectorFlyout({ - initialConnector: editedConnectorItem, - consumer: 'case', - onClose: onCloseEditFlyout, - reloadConnectors: onConnectorUpdate, - }) - : null, - // eslint-disable-next-line react-hooks/exhaustive-deps - [connector.id, editFlyoutVisible] - ); - - return ( - - {!connectorIsValid && ( - - - {i18n.WARNING_NO_CONNECTOR_MESSAGE} - - - )} - - - - - - - {addFlyoutVisible && ConnectorAddFlyout} - {ConnectorEditFlyout} - - ); -}; - -export const ConfigureCases = React.memo(ConfigureCasesComponent); diff --git a/x-pack/plugins/cases/public/components/configure_cases/mapping.test.tsx b/x-pack/plugins/cases/public/components/configure_cases/mapping.test.tsx deleted file mode 100644 index 75b2410dde957..0000000000000 --- a/x-pack/plugins/cases/public/components/configure_cases/mapping.test.tsx +++ /dev/null @@ -1,47 +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 from 'react'; -import { mount } from 'enzyme'; - -import { TestProviders } from '../../common/mock'; -import { Mapping, MappingProps } from './mapping'; -import { mappings } from './__mock__'; - -describe('Mapping', () => { - const props: MappingProps = { - connectorActionTypeId: '.servicenow', - isLoading: false, - mappings, - }; - - beforeEach(() => { - jest.clearAllMocks(); - }); - test('it shows mapping form group', () => { - const wrapper = mount(, { wrappingComponent: TestProviders }); - expect(wrapper.find('[data-test-subj="static-mappings"]').first().exists()).toBe(true); - }); - - test('correctly maps fields', () => { - const wrapper = mount(, { wrappingComponent: TestProviders }); - expect(wrapper.find('[data-test-subj="field-mapping-source"] code').first().text()).toBe( - 'title' - ); - expect(wrapper.find('[data-test-subj="field-mapping-target"] code').first().text()).toBe( - 'short_description' - ); - }); - test('displays connection warning when isLoading: false and mappings: []', () => { - const wrapper = mount(, { - wrappingComponent: TestProviders, - }); - expect(wrapper.find('[data-test-subj="field-mapping-desc"]').first().text()).toBe( - 'Field mappings require an established connection to ServiceNow ITSM. Please check your connection credentials.' - ); - }); -}); diff --git a/x-pack/plugins/cases/public/components/configure_cases/mapping.tsx b/x-pack/plugins/cases/public/components/configure_cases/mapping.tsx deleted file mode 100644 index 5ec6a33f48b6a..0000000000000 --- a/x-pack/plugins/cases/public/components/configure_cases/mapping.tsx +++ /dev/null @@ -1,62 +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, { useMemo } from 'react'; - -import { EuiFlexGroup, EuiFlexItem, EuiText, EuiTextColor } from '@elastic/eui'; - -import { TextColor } from '@elastic/eui/src/components/text/text_color'; -import * as i18n from './translations'; - -import { FieldMapping } from './field_mapping'; -import { CaseConnectorMapping } from '../../containers/configure/types'; -import { connectorsConfiguration } from '../connectors'; - -export interface MappingProps { - connectorActionTypeId: string; - isLoading: boolean; - mappings: CaseConnectorMapping[]; -} - -const MappingComponent: React.FC = ({ - connectorActionTypeId, - isLoading, - mappings, -}) => { - const selectedConnector = useMemo(() => connectorsConfiguration[connectorActionTypeId], [ - connectorActionTypeId, - ]); - const fieldMappingDesc: { desc: string; color: TextColor } = useMemo( - () => - mappings.length > 0 || isLoading - ? { desc: i18n.FIELD_MAPPING_DESC(selectedConnector.name), color: 'subdued' } - : { desc: i18n.FIELD_MAPPING_DESC_ERR(selectedConnector.name), color: 'danger' }, - [isLoading, mappings.length, selectedConnector.name] - ); - return ( - - - -

{i18n.FIELD_MAPPING_TITLE(selectedConnector.name)}

- - {fieldMappingDesc.desc} - -
-
- - - -
- ); -}; - -export const Mapping = React.memo(MappingComponent); diff --git a/x-pack/plugins/cases/public/components/configure_cases/translations.ts b/x-pack/plugins/cases/public/components/configure_cases/translations.ts deleted file mode 100644 index 2fb2133ba470c..0000000000000 --- a/x-pack/plugins/cases/public/components/configure_cases/translations.ts +++ /dev/null @@ -1,227 +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 { i18n } from '@kbn/i18n'; - -export * from '../../common/translations'; - -export const INCIDENT_MANAGEMENT_SYSTEM_TITLE = i18n.translate( - 'xpack.cases.configureCases.incidentManagementSystemTitle', - { - defaultMessage: 'Connect to external incident management system', - } -); - -export const INCIDENT_MANAGEMENT_SYSTEM_DESC = i18n.translate( - 'xpack.cases.configureCases.incidentManagementSystemDesc', - { - defaultMessage: - 'You may optionally connect cases to an external incident management system of your choosing. This will allow you to push case data as an incident in your chosen third-party system.', - } -); - -export const INCIDENT_MANAGEMENT_SYSTEM_LABEL = i18n.translate( - 'xpack.cases.configureCases.incidentManagementSystemLabel', - { - defaultMessage: 'Incident management system', - } -); - -export const ADD_NEW_CONNECTOR = i18n.translate('xpack.cases.configureCases.addNewConnector', { - defaultMessage: 'Add new connector', -}); - -export const CASE_CLOSURE_OPTIONS_TITLE = i18n.translate( - 'xpack.cases.configureCases.caseClosureOptionsTitle', - { - defaultMessage: 'Case Closures', - } -); - -export const CASE_CLOSURE_OPTIONS_DESC = i18n.translate( - 'xpack.cases.configureCases.caseClosureOptionsDesc', - { - defaultMessage: - 'Define how you wish cases to be closed. Automated case closures require an established connection to an external incident management system.', - } -); - -export const CASE_COLSURE_OPTIONS_SUB_CASES = i18n.translate( - 'xpack.cases.configureCases.caseClosureOptionsSubCases', - { - defaultMessage: 'Automated closures of sub-cases is not currently supported.', - } -); - -export const CASE_CLOSURE_OPTIONS_LABEL = i18n.translate( - 'xpack.cases.configureCases.caseClosureOptionsLabel', - { - defaultMessage: 'Case closure options', - } -); - -export const CASE_CLOSURE_OPTIONS_MANUAL = i18n.translate( - 'xpack.cases.configureCases.caseClosureOptionsManual', - { - defaultMessage: 'Manually close cases', - } -); - -export const CASE_CLOSURE_OPTIONS_NEW_INCIDENT = i18n.translate( - 'xpack.cases.configureCases.caseClosureOptionsNewIncident', - { - defaultMessage: 'Automatically close cases when pushing new incident to external system', - } -); - -export const CASE_CLOSURE_OPTIONS_CLOSED_INCIDENT = i18n.translate( - 'xpack.cases.configureCases.caseClosureOptionsClosedIncident', - { - defaultMessage: 'Automatically close cases when incident is closed in external system', - } -); -export const FIELD_MAPPING_TITLE = (thirdPartyName: string): string => { - return i18n.translate('xpack.cases.configureCases.fieldMappingTitle', { - values: { thirdPartyName }, - defaultMessage: '{ thirdPartyName } field mappings', - }); -}; - -export const FIELD_MAPPING_DESC = (thirdPartyName: string): string => { - return i18n.translate('xpack.cases.configureCases.fieldMappingDesc', { - values: { thirdPartyName }, - defaultMessage: - 'Map Case fields to { thirdPartyName } fields when pushing data to { thirdPartyName }. Field mappings require an established connection to { thirdPartyName }.', - }); -}; - -export const FIELD_MAPPING_DESC_ERR = (thirdPartyName: string): string => { - return i18n.translate('xpack.cases.configureCases.fieldMappingDescErr', { - values: { thirdPartyName }, - defaultMessage: - 'Field mappings require an established connection to { thirdPartyName }. Please check your connection credentials.', - }); -}; -export const EDIT_FIELD_MAPPING_TITLE = (thirdPartyName: string): string => { - return i18n.translate('xpack.cases.configureCases.editFieldMappingTitle', { - values: { thirdPartyName }, - defaultMessage: 'Edit { thirdPartyName } field mappings', - }); -}; - -export const FIELD_MAPPING_FIRST_COL = i18n.translate( - 'xpack.cases.configureCases.fieldMappingFirstCol', - { - defaultMessage: 'Kibana case field', - } -); - -export const FIELD_MAPPING_SECOND_COL = (thirdPartyName: string): string => { - return i18n.translate('xpack.cases.configureCases.fieldMappingSecondCol', { - values: { thirdPartyName }, - defaultMessage: '{ thirdPartyName } field', - }); -}; - -export const FIELD_MAPPING_THIRD_COL = i18n.translate( - 'xpack.cases.configureCases.fieldMappingThirdCol', - { - defaultMessage: 'On edit and update', - } -); - -export const FIELD_MAPPING_EDIT_NOTHING = i18n.translate( - 'xpack.cases.configureCases.fieldMappingEditNothing', - { - defaultMessage: 'Nothing', - } -); - -export const FIELD_MAPPING_EDIT_OVERWRITE = i18n.translate( - 'xpack.cases.configureCases.fieldMappingEditOverwrite', - { - defaultMessage: 'Overwrite', - } -); - -export const FIELD_MAPPING_EDIT_APPEND = i18n.translate( - 'xpack.cases.configureCases.fieldMappingEditAppend', - { - defaultMessage: 'Append', - } -); - -export const CANCEL = i18n.translate('xpack.cases.configureCases.cancelButton', { - defaultMessage: 'Cancel', -}); - -export const SAVE = i18n.translate('xpack.cases.configureCases.saveButton', { - defaultMessage: 'Save', -}); - -export const SAVE_CLOSE = i18n.translate('xpack.cases.configureCases.saveAndCloseButton', { - defaultMessage: 'Save & close', -}); - -export const WARNING_NO_CONNECTOR_TITLE = i18n.translate( - 'xpack.cases.configureCases.warningTitle', - { - defaultMessage: 'Warning', - } -); - -export const WARNING_NO_CONNECTOR_MESSAGE = i18n.translate( - 'xpack.cases.configureCases.warningMessage', - { - defaultMessage: - 'The selected connector has been deleted. Either select a different connector or create a new one.', - } -); - -export const MAPPING_FIELD_NOT_MAPPED = i18n.translate( - 'xpack.cases.configureCases.mappingFieldNotMapped', - { - defaultMessage: 'Not mapped', - } -); - -export const COMMENT = i18n.translate('xpack.cases.configureCases.commentMapping', { - defaultMessage: 'Comments', -}); - -export const NO_FIELDS_ERROR = (connectorName: string): string => { - return i18n.translate('xpack.cases.configureCases.noFieldsError', { - values: { connectorName }, - defaultMessage: - 'No { connectorName } fields found. Please check your { connectorName } connector settings or your { connectorName } instance settings to resolve.', - }); -}; - -export const BLANK_MAPPINGS = (connectorName: string): string => { - return i18n.translate('xpack.cases.configureCases.blankMappings', { - values: { connectorName }, - defaultMessage: 'At least one field needs to be mapped to { connectorName }', - }); -}; - -export const REQUIRED_MAPPINGS = (connectorName: string, fields: string): string => { - return i18n.translate('xpack.cases.configureCases.requiredMappings', { - values: { connectorName, fields }, - defaultMessage: - 'At least one Case field needs to be mapped to the following required { connectorName } fields: { fields }', - }); -}; -export const UPDATE_FIELD_MAPPINGS = i18n.translate('xpack.cases.configureCases.updateConnector', { - defaultMessage: 'Update field mappings', -}); - -export const UPDATE_SELECTED_CONNECTOR = (connectorName: string): string => { - return i18n.translate('xpack.cases.configureCases.updateSelectedConnector', { - values: { connectorName }, - defaultMessage: 'Update { connectorName }', - }); -}; diff --git a/x-pack/plugins/cases/public/components/configure_cases/utils.test.tsx b/x-pack/plugins/cases/public/components/configure_cases/utils.test.tsx deleted file mode 100644 index 45bb7f1f5136d..0000000000000 --- a/x-pack/plugins/cases/public/components/configure_cases/utils.test.tsx +++ /dev/null @@ -1,64 +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 { mappings } from './__mock__'; -import { setActionTypeToMapping, setThirdPartyToMapping } from './utils'; -import { CaseConnectorMapping } from '../../containers/configure/types'; - -describe('FieldMappingRow', () => { - test('it should change the action type', () => { - const newMapping = setActionTypeToMapping('title', 'nothing', mappings); - expect(newMapping[0].actionType).toBe('nothing'); - }); - - test('it should not change other fields', () => { - const [newTitle, description, comments] = setActionTypeToMapping('title', 'nothing', mappings); - expect(newTitle).not.toEqual(mappings[0]); - expect(description).toEqual(mappings[1]); - expect(comments).toEqual(mappings[2]); - }); - - test('it should return a new array when changing action type', () => { - const newMapping = setActionTypeToMapping('title', 'nothing', mappings); - expect(newMapping).not.toBe(mappings); - }); - - test('it should change the third party', () => { - const newMapping = setThirdPartyToMapping('title', 'description', mappings); - expect(newMapping[0].target).toBe('description'); - }); - - test('it should not change other fields when there is not a conflict', () => { - const tempMapping: CaseConnectorMapping[] = [ - { - source: 'title', - target: 'short_description', - actionType: 'overwrite', - }, - { - source: 'comments', - target: 'comments', - actionType: 'append', - }, - ]; - - const [newTitle, comments] = setThirdPartyToMapping('title', 'description', tempMapping); - - expect(newTitle).not.toEqual(mappings[0]); - expect(comments).toEqual(tempMapping[1]); - }); - - test('it should return a new array when changing third party', () => { - const newMapping = setThirdPartyToMapping('title', 'description', mappings); - expect(newMapping).not.toBe(mappings); - }); - - test('it should change the target of the conflicting third party field to not_mapped', () => { - const newMapping = setThirdPartyToMapping('title', 'description', mappings); - expect(newMapping[1].target).toBe('not_mapped'); - }); -}); diff --git a/x-pack/plugins/cases/public/components/configure_cases/utils.ts b/x-pack/plugins/cases/public/components/configure_cases/utils.ts deleted file mode 100644 index ade1a5e0c2bba..0000000000000 --- a/x-pack/plugins/cases/public/components/configure_cases/utils.ts +++ /dev/null @@ -1,80 +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 { ConnectorTypeFields, ConnectorTypes } from '../../../common'; -import { - CaseField, - ActionType, - ThirdPartyField, - ActionConnector, - CaseConnector, - CaseConnectorMapping, -} from '../../containers/configure/types'; - -export const setActionTypeToMapping = ( - caseField: CaseField, - newActionType: ActionType, - mapping: CaseConnectorMapping[] -): CaseConnectorMapping[] => { - const findItemIndex = mapping.findIndex((item) => item.source === caseField); - - if (findItemIndex >= 0) { - return [ - ...mapping.slice(0, findItemIndex), - { ...mapping[findItemIndex], actionType: newActionType }, - ...mapping.slice(findItemIndex + 1), - ]; - } - - return [...mapping]; -}; - -export const setThirdPartyToMapping = ( - caseField: CaseField, - newThirdPartyField: ThirdPartyField, - mapping: CaseConnectorMapping[] -): CaseConnectorMapping[] => - mapping.map((item) => { - if (item.source !== caseField && item.target === newThirdPartyField) { - return { ...item, target: 'not_mapped' }; - } else if (item.source === caseField) { - return { ...item, target: newThirdPartyField }; - } - return item; - }); - -export const getNoneConnector = (): CaseConnector => ({ - id: 'none', - name: 'none', - type: ConnectorTypes.none, - fields: null, -}); - -export const getConnectorById = ( - id: string, - connectors: ActionConnector[] -): ActionConnector | null => connectors.find((c) => c.id === id) ?? null; - -export const normalizeActionConnector = ( - actionConnector: ActionConnector, - fields: CaseConnector['fields'] = null -): CaseConnector => { - const caseConnectorFieldsType = { - type: actionConnector.actionTypeId, - fields, - } as ConnectorTypeFields; - return { - id: actionConnector.id, - name: actionConnector.name, - ...caseConnectorFieldsType, - }; -}; - -export const normalizeCaseConnector = ( - connectors: ActionConnector[], - caseConnector: CaseConnector -): ActionConnector | null => connectors.find((c) => c.id === caseConnector.id) ?? null; diff --git a/x-pack/plugins/cases/public/components/connector_selector/form.test.tsx b/x-pack/plugins/cases/public/components/connector_selector/form.test.tsx deleted file mode 100644 index ec136989dd937..0000000000000 --- a/x-pack/plugins/cases/public/components/connector_selector/form.test.tsx +++ /dev/null @@ -1,67 +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 from 'react'; -import { mount } from 'enzyme'; -import { UseField, Form, useForm, FormHook } from '../../common/shared_imports'; -import { ConnectorSelector } from './form'; -import { connectorsMock } from '../../containers/mock'; -import { getFormMock } from '../__mock__/form'; - -jest.mock('../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form'); - -const useFormMock = useForm as jest.Mock; - -describe('ConnectorSelector', () => { - const formHookMock = getFormMock({ connectorId: connectorsMock[0].id }); - - beforeEach(() => { - jest.resetAllMocks(); - useFormMock.mockImplementation(() => ({ form: formHookMock })); - }); - - it('it should render', async () => { - const wrapper = mount( -
- - - ); - - expect(wrapper.find(`[data-test-subj="caseConnectors"]`).exists()).toBeTruthy(); - }); - - it('it should not render when is not in edit mode', async () => { - const wrapper = mount( -
- - - ); - - expect(wrapper.find(`[data-test-subj="caseConnectors"]`).exists()).toBeFalsy(); - }); -}); diff --git a/x-pack/plugins/cases/public/components/connector_selector/form.tsx b/x-pack/plugins/cases/public/components/connector_selector/form.tsx deleted file mode 100644 index 210334e93adb8..0000000000000 --- a/x-pack/plugins/cases/public/components/connector_selector/form.tsx +++ /dev/null @@ -1,70 +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 } from 'react'; -import { isEmpty } from 'lodash/fp'; -import { EuiFormRow } from '@elastic/eui'; - -import { FieldHook, getFieldValidityAndErrorMessage } from '../../common/shared_imports'; -import { ConnectorsDropdown } from '../configure_cases/connectors_dropdown'; -import { ActionConnector } from '../../../common'; - -interface ConnectorSelectorProps { - connectors: ActionConnector[]; - dataTestSubj: string; - disabled: boolean; - field: FieldHook; - idAria: string; - isEdit: boolean; - isLoading: boolean; - handleChange?: (newValue: string) => void; - hideConnectorServiceNowSir?: boolean; -} -export const ConnectorSelector = ({ - connectors, - dataTestSubj, - disabled = false, - field, - idAria, - isEdit = true, - isLoading = false, - handleChange, - hideConnectorServiceNowSir = false, -}: ConnectorSelectorProps) => { - const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(field); - const onChange = useCallback( - (val: string) => { - if (handleChange) { - handleChange(val); - } - field.setValue(val); - }, - [handleChange, field] - ); - - return isEdit ? ( - - - - ) : null; -}; diff --git a/x-pack/plugins/cases/public/components/connectors/card.tsx b/x-pack/plugins/cases/public/components/connectors/card.tsx deleted file mode 100644 index 82a508ccf3432..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/card.tsx +++ /dev/null @@ -1,71 +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, { memo, useMemo } from 'react'; -import { EuiCard, EuiIcon, EuiLoadingSpinner } from '@elastic/eui'; -import styled from 'styled-components'; - -import { connectorsConfiguration } from '.'; -import { ConnectorTypes } from '../../../common'; - -interface ConnectorCardProps { - connectorType: ConnectorTypes; - title: string; - listItems: Array<{ title: string; description: React.ReactNode }>; - isLoading: boolean; -} - -const StyledText = styled.span` - span { - display: block; - } -`; - -const ConnectorCardDisplay: React.FC = ({ - connectorType, - title, - listItems, - isLoading, -}) => { - const description = useMemo( - () => ( - - {listItems.length > 0 && - listItems.map((item, i) => ( - - {`${item.title}: `} - {item.description} - - ))} - - ), - [listItems] - ); - const icon = useMemo( - () => , - [connectorType] - ); - return ( - <> - {isLoading && } - {!isLoading && ( - - )} - - ); -}; - -export const ConnectorCard = memo(ConnectorCardDisplay); diff --git a/x-pack/plugins/cases/public/components/connectors/case/alert_fields.tsx b/x-pack/plugins/cases/public/components/connectors/case/alert_fields.tsx deleted file mode 100644 index 10955db69461c..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/case/alert_fields.tsx +++ /dev/null @@ -1,106 +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. - */ - -/* eslint-disable @kbn/eslint/no-restricted-paths */ - -import React, { useCallback, useEffect, useState } from 'react'; -import styled from 'styled-components'; -import { EuiCallOut, EuiSpacer } from '@elastic/eui'; - -import { ActionParamsProps } from '../../../../../triggers_actions_ui/public/types'; -import { CommentType } from '../../../../common'; - -import { CaseActionParams } from './types'; -import { ExistingCase } from './existing_case'; - -import * as i18n from './translations'; - -const Container = styled.div` - ${({ theme }) => ` - padding: ${theme.eui?.euiSizeS ?? '8px'} ${theme.eui?.euiSizeL ?? '24px'} ${ - theme.eui?.euiSizeL ?? '24px' - } ${theme.eui?.euiSizeL ?? '24px'}; - `} -`; - -const defaultAlertComment = { - type: CommentType.generatedAlert, - alerts: `[{{#context.alerts}}{"_id": "{{_id}}", "_index": "{{_index}}", "ruleId": "{{signal.rule.id}}", "ruleName": "{{signal.rule.name}}"}__SEPARATOR__{{/context.alerts}}]`, -}; - -const CaseParamsFields: React.FunctionComponent> = ({ - actionParams, - editAction, - index, - errors, - messageVariables, - actionConnector, -}) => { - const { caseId = null, comment = defaultAlertComment } = actionParams.subActionParams ?? {}; - - const [selectedCase, setSelectedCase] = useState(null); - - const editSubActionProperty = useCallback( - (key: string, value: unknown) => { - const newProps = { ...actionParams.subActionParams, [key]: value }; - editAction('subActionParams', newProps, index); - }, - // edit action causes re-renders - // eslint-disable-next-line react-hooks/exhaustive-deps - [actionParams.subActionParams, index] - ); - - const onCaseChanged = useCallback( - (id: string) => { - setSelectedCase(id); - editSubActionProperty('caseId', id); - }, - [editSubActionProperty] - ); - - useEffect(() => { - if (!actionParams.subAction) { - editAction('subAction', 'addComment', index); - } - - if (!actionParams.subActionParams?.caseId) { - editSubActionProperty('caseId', caseId); - } - - if (!actionParams.subActionParams?.comment) { - editSubActionProperty('comment', comment); - } - - if (caseId != null) { - setSelectedCase((prevCaseId) => (prevCaseId !== caseId ? caseId : prevCaseId)); - } - - // editAction creates an infinity loop. - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ - actionConnector, - index, - actionParams.subActionParams?.caseId, - actionParams.subActionParams?.comment, - caseId, - comment, - actionParams.subAction, - ]); - - return ( - - - - -

{i18n.CASE_CONNECTOR_CALL_OUT_MSG}

-
-
- ); -}; - -// eslint-disable-next-line import/no-default-export -export { CaseParamsFields as default }; diff --git a/x-pack/plugins/cases/public/components/connectors/case/cases_dropdown.tsx b/x-pack/plugins/cases/public/components/connectors/case/cases_dropdown.tsx deleted file mode 100644 index 3f3c7d4931192..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/case/cases_dropdown.tsx +++ /dev/null @@ -1,73 +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 { EuiFormRow, EuiSuperSelect, EuiSuperSelectOption } from '@elastic/eui'; -import React, { memo, useMemo, useCallback } from 'react'; -import { Case } from '../../../containers/types'; - -import * as i18n from './translations'; - -interface CaseDropdownProps { - isLoading: boolean; - cases: Case[]; - selectedCase?: string; - onCaseChanged: (id: string) => void; -} - -export const ADD_CASE_BUTTON_ID = 'add-case'; - -const addNewCase = { - value: ADD_CASE_BUTTON_ID, - inputDisplay: ( - - {i18n.CASE_CONNECTOR_ADD_NEW_CASE} - - ), - 'data-test-subj': 'dropdown-connector-add-connector', -}; - -const CasesDropdownComponent: React.FC = ({ - isLoading, - cases, - selectedCase, - onCaseChanged, -}) => { - const caseOptions: Array> = useMemo( - () => - cases.reduce>>( - (acc, theCase) => [ - ...acc, - { - value: theCase.id, - inputDisplay: {theCase.title}, - 'data-test-subj': `case-connector-cases-dropdown-${theCase.id}`, - }, - ], - [] - ), - [cases] - ); - - const options = useMemo(() => [...caseOptions, addNewCase], [caseOptions]); - const onChange = useCallback((id: string) => onCaseChanged(id), [onCaseChanged]); - - return ( - - - - ); -}; - -export const CasesDropdown = memo(CasesDropdownComponent); diff --git a/x-pack/plugins/cases/public/components/connectors/case/existing_case.tsx b/x-pack/plugins/cases/public/components/connectors/case/existing_case.tsx deleted file mode 100644 index 22798843dd856..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/case/existing_case.tsx +++ /dev/null @@ -1,76 +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, { memo, useMemo, useCallback } from 'react'; -import { CaseType } from '../../../../common'; -import { - useGetCases, - DEFAULT_QUERY_PARAMS, - DEFAULT_FILTER_OPTIONS, -} from '../../../containers/use_get_cases'; -import { useCreateCaseModal } from '../../use_create_case_modal'; -import { CasesDropdown, ADD_CASE_BUTTON_ID } from './cases_dropdown'; - -interface ExistingCaseProps { - selectedCase: string | null; - onCaseChanged: (id: string) => void; -} - -const ExistingCaseComponent: React.FC = ({ onCaseChanged, selectedCase }) => { - const { data: cases, loading: isLoadingCases, refetchCases } = useGetCases(DEFAULT_QUERY_PARAMS, { - ...DEFAULT_FILTER_OPTIONS, - onlyCollectionType: true, - }); - - const onCaseCreated = useCallback( - (newCase) => { - refetchCases(); - onCaseChanged(newCase.id); - }, - [onCaseChanged, refetchCases] - ); - - const { modal, openModal } = useCreateCaseModal({ - onCaseCreated, - caseType: CaseType.collection, - // FUTURE DEVELOPER - // We are making the assumption that this component is only used in rules creation - // that's why we want to hide ServiceNow SIR - hideConnectorServiceNowSir: true, - }); - - const onChange = useCallback( - (id: string) => { - if (id === ADD_CASE_BUTTON_ID) { - openModal(); - return; - } - - onCaseChanged(id); - }, - [onCaseChanged, openModal] - ); - - const isCasesLoading = useMemo( - () => isLoadingCases.includes('cases') || isLoadingCases.includes('caseUpdate'), - [isLoadingCases] - ); - - return ( - <> - - {modal} - - ); -}; - -export const ExistingCase = memo(ExistingCaseComponent); diff --git a/x-pack/plugins/cases/public/components/connectors/case/index.ts b/x-pack/plugins/cases/public/components/connectors/case/index.ts deleted file mode 100644 index c2cf4980da7ec..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/case/index.ts +++ /dev/null @@ -1,42 +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 { lazy } from 'react'; - -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ActionTypeModel } from '../../../../../triggers_actions_ui/public/types'; -import { CaseActionParams } from './types'; -import * as i18n from './translations'; - -interface ValidationResult { - errors: { - caseId: string[]; - }; -} - -const validateParams = (actionParams: CaseActionParams) => { - const validationResult: ValidationResult = { errors: { caseId: [] } }; - - if (actionParams.subActionParams && !actionParams.subActionParams.caseId) { - validationResult.errors.caseId.push(i18n.CASE_CONNECTOR_CASE_REQUIRED); - } - - return validationResult; -}; - -export function getActionType(): ActionTypeModel { - return { - id: '.case', - iconClass: 'securityAnalyticsApp', - selectMessage: i18n.CASE_CONNECTOR_DESC, - actionTypeTitle: i18n.CASE_CONNECTOR_TITLE, - validateConnector: () => ({ config: { errors: {} }, secrets: { errors: {} } }), - validateParams, - actionConnectorFields: null, - actionParamsFields: lazy(() => import('./alert_fields')), - }; -} diff --git a/x-pack/plugins/cases/public/components/connectors/case/translations.ts b/x-pack/plugins/cases/public/components/connectors/case/translations.ts deleted file mode 100644 index 8304aaef5765c..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/case/translations.ts +++ /dev/null @@ -1,109 +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 { i18n } from '@kbn/i18n'; - -export * from '../../../common/translations'; - -export const CASE_CONNECTOR_DESC = i18n.translate( - 'xpack.cases.components.connectors.cases.selectMessageText', - { - defaultMessage: 'Create or update a case.', - } -); - -export const CASE_CONNECTOR_TITLE = i18n.translate( - 'xpack.cases.components.connectors.cases.actionTypeTitle', - { - defaultMessage: 'Cases', - } -); - -export const CASE_CONNECTOR_COMMENT_LABEL = i18n.translate( - 'xpack.cases.components.connectors.cases.commentLabel', - { - defaultMessage: 'Comment', - } -); - -export const CASE_CONNECTOR_COMMENT_REQUIRED = i18n.translate( - 'xpack.cases.components.connectors.cases.commentRequired', - { - defaultMessage: 'Comment is required.', - } -); - -export const CASE_CONNECTOR_CASES_DROPDOWN_ROW_LABEL = i18n.translate( - 'xpack.cases.components.connectors.cases.casesDropdownRowLabel', - { - defaultMessage: 'Case allowing sub-cases', - } -); - -export const CASE_CONNECTOR_CASES_DROPDOWN_PLACEHOLDER = i18n.translate( - 'xpack.cases.components.connectors.cases.casesDropdownPlaceholder', - { - defaultMessage: 'Select case', - } -); - -export const CASE_CONNECTOR_CASES_OPTION_NEW_CASE = i18n.translate( - 'xpack.cases.components.connectors.cases.optionAddNewCase', - { - defaultMessage: 'Add to a new case', - } -); - -export const CASE_CONNECTOR_CASES_OPTION_EXISTING_CASE = i18n.translate( - 'xpack.cases.components.connectors.cases.optionAddToExistingCase', - { - defaultMessage: 'Add to existing case', - } -); - -export const CASE_CONNECTOR_CASE_REQUIRED = i18n.translate( - 'xpack.cases.components.connectors.cases.caseRequired', - { - defaultMessage: 'You must select a case.', - } -); - -export const CASE_CONNECTOR_CALL_OUT_TITLE = i18n.translate( - 'xpack.cases.components.connectors.cases.callOutTitle', - { - defaultMessage: 'Generated alerts will be attached to sub-cases', - } -); - -export const CASE_CONNECTOR_CALL_OUT_MSG = i18n.translate( - 'xpack.cases.components.connectors.cases.callOutMsg', - { - defaultMessage: - 'A case can contain multiple sub-cases to allow grouping of generated alerts. Sub-cases will give more granular control over the status of these generated alerts and prevents having too many alerts attached to one case.', - } -); - -export const CASE_CONNECTOR_ADD_NEW_CASE = i18n.translate( - 'xpack.cases.components.connectors.cases.addNewCaseOption', - { - defaultMessage: 'Add new case', - } -); - -export const CREATE_CASE = i18n.translate( - 'xpack.cases.components.connectors.cases.createCaseLabel', - { - defaultMessage: 'Create case', - } -); - -export const CONNECTED_CASE = i18n.translate( - 'xpack.cases.components.connectors.cases.connectedCaseLabel', - { - defaultMessage: 'Connected case', - } -); diff --git a/x-pack/plugins/cases/public/components/connectors/case/types.ts b/x-pack/plugins/cases/public/components/connectors/case/types.ts deleted file mode 100644 index aec9e09ea198c..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/case/types.ts +++ /dev/null @@ -1,18 +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. - */ - -export interface CaseActionParams { - subAction: string; - subActionParams: { - caseId: string; - comment: { - alertId: string; - index: string; - type: 'alert'; - }; - }; -} diff --git a/x-pack/plugins/cases/public/components/connectors/config.ts b/x-pack/plugins/cases/public/components/connectors/config.ts deleted file mode 100644 index e8d87511c7e17..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/config.ts +++ /dev/null @@ -1,39 +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 { - getResilientActionType, - getServiceNowITSMActionType, - getServiceNowSIRActionType, - getJiraActionType, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../triggers_actions_ui/public/common'; -import { ConnectorConfiguration } from './types'; - -const resilient = getResilientActionType(); -const serviceNowITSM = getServiceNowITSMActionType(); -const serviceNowSIR = getServiceNowSIRActionType(); -const jira = getJiraActionType(); - -export const connectorsConfiguration: Record = { - '.servicenow': { - name: serviceNowITSM.actionTypeTitle ?? '', - logo: serviceNowITSM.iconClass, - }, - '.servicenow-sir': { - name: serviceNowSIR.actionTypeTitle ?? '', - logo: serviceNowSIR.iconClass, - }, - '.jira': { - name: jira.actionTypeTitle ?? '', - logo: jira.iconClass, - }, - '.resilient': { - name: resilient.actionTypeTitle ?? '', - logo: resilient.iconClass, - }, -}; diff --git a/x-pack/plugins/cases/public/components/connectors/connectors_registry.ts b/x-pack/plugins/cases/public/components/connectors/connectors_registry.ts deleted file mode 100644 index 2e02cb290c3c8..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/connectors_registry.ts +++ /dev/null @@ -1,49 +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 { i18n } from '@kbn/i18n'; -import { CaseConnector, CaseConnectorsRegistry } from './types'; - -export const createCaseConnectorsRegistry = (): CaseConnectorsRegistry => { - const connectors: Map> = new Map(); - - const registry: CaseConnectorsRegistry = { - has: (id: string) => connectors.has(id), - register: (connector: CaseConnector) => { - if (connectors.has(connector.id)) { - throw new Error( - i18n.translate('xpack.cases.connecors.register.duplicateCaseConnectorErrorMessage', { - defaultMessage: 'Object type "{id}" is already registered.', - values: { - id: connector.id, - }, - }) - ); - } - - connectors.set(connector.id, connector); - }, - get: (id: string): CaseConnector => { - if (!connectors.has(id)) { - throw new Error( - i18n.translate('xpack.cases.connecors.get.missingCaseConnectorErrorMessage', { - defaultMessage: 'Object type "{id}" is not registered.', - values: { - id, - }, - }) - ); - } - return connectors.get(id)!; - }, - list: () => { - return Array.from(connectors).map(([id, connector]) => connector); - }, - }; - - return registry; -}; diff --git a/x-pack/plugins/cases/public/components/connectors/fields_form.tsx b/x-pack/plugins/cases/public/components/connectors/fields_form.tsx deleted file mode 100644 index d71da6f87689d..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/fields_form.tsx +++ /dev/null @@ -1,54 +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, { memo, Suspense } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; - -import { CaseActionConnector, ConnectorFieldsProps } from './types'; -import { getCaseConnectors } from '.'; -import { ConnectorTypeFields } from '../../../common'; - -interface Props extends Omit, 'connector'> { - connector: CaseActionConnector | null; -} - -const ConnectorFieldsFormComponent: React.FC = ({ connector, isEdit, onChange, fields }) => { - const { caseConnectorsRegistry } = getCaseConnectors(); - - if (connector == null || connector.actionTypeId == null || connector.actionTypeId === '.none') { - return null; - } - - const { fieldsComponent: FieldsComponent } = caseConnectorsRegistry.get(connector.actionTypeId); - - return ( - <> - {FieldsComponent != null ? ( - - - - - - } - > -
- -
-
- ) : null} - - ); -}; - -export const ConnectorFieldsForm = memo(ConnectorFieldsFormComponent); diff --git a/x-pack/plugins/cases/public/components/connectors/index.ts b/x-pack/plugins/cases/public/components/connectors/index.ts deleted file mode 100644 index 7444c403a3b60..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/index.ts +++ /dev/null @@ -1,57 +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 { CaseConnectorsRegistry } from './types'; -import { createCaseConnectorsRegistry } from './connectors_registry'; -import { getCaseConnector as getJiraCaseConnector } from './jira'; -import { getCaseConnector as getResilientCaseConnector } from './resilient'; -import { getServiceNowITSMCaseConnector, getServiceNowSIRCaseConnector } from './servicenow'; -import { - JiraFieldsType, - ServiceNowITSMFieldsType, - ServiceNowSIRFieldsType, - ResilientFieldsType, -} from '../../../common'; - -export { getActionType as getCaseConnectorUI } from './case'; - -export * from './config'; -export * from './types'; - -interface GetCaseConnectorsReturn { - caseConnectorsRegistry: CaseConnectorsRegistry; -} - -class CaseConnectors { - private caseConnectorsRegistry: CaseConnectorsRegistry; - - constructor() { - this.caseConnectorsRegistry = createCaseConnectorsRegistry(); - this.init(); - } - - private init() { - this.caseConnectorsRegistry.register(getJiraCaseConnector()); - this.caseConnectorsRegistry.register(getResilientCaseConnector()); - this.caseConnectorsRegistry.register( - getServiceNowITSMCaseConnector() - ); - this.caseConnectorsRegistry.register(getServiceNowSIRCaseConnector()); - } - - registry(): CaseConnectorsRegistry { - return this.caseConnectorsRegistry; - } -} - -const caseConnectors = new CaseConnectors(); - -export const getCaseConnectors = (): GetCaseConnectorsReturn => { - return { - caseConnectorsRegistry: caseConnectors.registry(), - }; -}; diff --git a/x-pack/plugins/cases/public/components/connectors/jira/__mocks__/api.ts b/x-pack/plugins/cases/public/components/connectors/jira/__mocks__/api.ts deleted file mode 100644 index 3a7b51545dfca..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/jira/__mocks__/api.ts +++ /dev/null @@ -1,45 +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 { GetIssueTypesProps, GetFieldsByIssueTypeProps, GetIssueTypeProps } from '../api'; -import { IssueTypes, Fields, Issues, Issue } from '../types'; -import { issues } from '../../mock'; - -const issueTypes = [ - { - id: '10006', - name: 'Task', - }, - { - id: '10007', - name: 'Bug', - }, -]; - -const fieldsByIssueType = { - summary: { allowedValues: [], defaultValue: {} }, - priority: { - allowedValues: [ - { - name: 'Medium', - id: '3', - }, - ], - defaultValue: { name: 'Medium', id: '3' }, - }, -}; - -export const getIssue = async (props: GetIssueTypeProps): Promise<{ data: Issue }> => - Promise.resolve({ data: issues[0] }); -export const getIssues = async (props: GetIssueTypesProps): Promise<{ data: Issues }> => - Promise.resolve({ data: issues }); -export const getIssueTypes = async (props: GetIssueTypesProps): Promise<{ data: IssueTypes }> => - Promise.resolve({ data: issueTypes }); - -export const getFieldsByIssueType = async ( - props: GetFieldsByIssueTypeProps -): Promise<{ data: Fields }> => Promise.resolve({ data: fieldsByIssueType }); diff --git a/x-pack/plugins/cases/public/components/connectors/jira/api.test.ts b/x-pack/plugins/cases/public/components/connectors/jira/api.test.ts deleted file mode 100644 index bbab8a14b5ed9..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/jira/api.test.ts +++ /dev/null @@ -1,160 +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 { httpServiceMock } from '../../../../../../../src/core/public/mocks'; -import { getIssueTypes, getFieldsByIssueType, getIssues, getIssue } from './api'; - -const issueTypesResponse = { - data: { - projects: [ - { - issuetypes: [ - { - id: '10006', - name: 'Task', - }, - { - id: '10007', - name: 'Bug', - }, - ], - }, - ], - }, -}; - -const fieldsResponse = { - data: { - projects: [ - { - issuetypes: [ - { - id: '10006', - name: 'Task', - fields: { - summary: { fieldId: 'summary' }, - priority: { - fieldId: 'priority', - allowedValues: [ - { - name: 'Highest', - id: '1', - }, - { - name: 'High', - id: '2', - }, - { - name: 'Medium', - id: '3', - }, - { - name: 'Low', - id: '4', - }, - { - name: 'Lowest', - id: '5', - }, - ], - defaultValue: { - name: 'Medium', - id: '3', - }, - }, - }, - }, - ], - }, - ], - }, -}; - -const issueResponse = { - id: '10267', - key: 'RJ-107', - fields: { summary: 'Test title' }, -}; - -const issuesResponse = [issueResponse]; - -describe('Jira API', () => { - const http = httpServiceMock.createStartContract(); - - beforeEach(() => jest.resetAllMocks()); - - describe('getIssueTypes', () => { - test('should call get issue types API', async () => { - const abortCtrl = new AbortController(); - http.post.mockResolvedValueOnce(issueTypesResponse); - const res = await getIssueTypes({ http, signal: abortCtrl.signal, connectorId: 'test' }); - - expect(res).toEqual(issueTypesResponse); - expect(http.post).toHaveBeenCalledWith('/api/actions/action/test/_execute', { - body: '{"params":{"subAction":"issueTypes","subActionParams":{}}}', - signal: abortCtrl.signal, - }); - }); - }); - - describe('getFieldsByIssueType', () => { - test('should call get fields API', async () => { - const abortCtrl = new AbortController(); - http.post.mockResolvedValueOnce(fieldsResponse); - const res = await getFieldsByIssueType({ - http, - signal: abortCtrl.signal, - connectorId: 'test', - id: '10006', - }); - - expect(res).toEqual(fieldsResponse); - expect(http.post).toHaveBeenCalledWith('/api/actions/action/test/_execute', { - body: '{"params":{"subAction":"fieldsByIssueType","subActionParams":{"id":"10006"}}}', - signal: abortCtrl.signal, - }); - }); - }); - - describe('getIssues', () => { - test('should call get fields API', async () => { - const abortCtrl = new AbortController(); - http.post.mockResolvedValueOnce(issuesResponse); - const res = await getIssues({ - http, - signal: abortCtrl.signal, - connectorId: 'test', - title: 'test issue', - }); - - expect(res).toEqual(issuesResponse); - expect(http.post).toHaveBeenCalledWith('/api/actions/action/test/_execute', { - body: '{"params":{"subAction":"issues","subActionParams":{"title":"test issue"}}}', - signal: abortCtrl.signal, - }); - }); - }); - - describe('getIssue', () => { - test('should call get fields API', async () => { - const abortCtrl = new AbortController(); - http.post.mockResolvedValueOnce(issuesResponse); - const res = await getIssue({ - http, - signal: abortCtrl.signal, - connectorId: 'test', - id: 'RJ-107', - }); - - expect(res).toEqual(issuesResponse); - expect(http.post).toHaveBeenCalledWith('/api/actions/action/test/_execute', { - body: '{"params":{"subAction":"issue","subActionParams":{"id":"RJ-107"}}}', - signal: abortCtrl.signal, - }); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/connectors/jira/api.ts b/x-pack/plugins/cases/public/components/connectors/jira/api.ts deleted file mode 100644 index dff3e3a5b41ab..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/jira/api.ts +++ /dev/null @@ -1,93 +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 { HttpSetup } from 'kibana/public'; -import { ActionTypeExecutorResult } from '../../../../../actions/common'; -import { IssueTypes, Fields, Issues, Issue } from './types'; - -export const BASE_ACTION_API_PATH = '/api/actions'; - -export interface GetIssueTypesProps { - http: HttpSetup; - signal: AbortSignal; - connectorId: string; -} - -export async function getIssueTypes({ http, signal, connectorId }: GetIssueTypesProps) { - return http.post>( - `${BASE_ACTION_API_PATH}/action/${connectorId}/_execute`, - { - body: JSON.stringify({ - params: { subAction: 'issueTypes', subActionParams: {} }, - }), - signal, - } - ); -} - -export interface GetFieldsByIssueTypeProps { - http: HttpSetup; - signal: AbortSignal; - connectorId: string; - id: string; -} - -export async function getFieldsByIssueType({ - http, - signal, - connectorId, - id, -}: GetFieldsByIssueTypeProps): Promise> { - return http.post(`${BASE_ACTION_API_PATH}/action/${connectorId}/_execute`, { - body: JSON.stringify({ - params: { subAction: 'fieldsByIssueType', subActionParams: { id } }, - }), - signal, - }); -} - -export interface GetIssuesTypeProps { - http: HttpSetup; - signal: AbortSignal; - connectorId: string; - title: string; -} - -export async function getIssues({ - http, - signal, - connectorId, - title, -}: GetIssuesTypeProps): Promise> { - return http.post(`${BASE_ACTION_API_PATH}/action/${connectorId}/_execute`, { - body: JSON.stringify({ - params: { subAction: 'issues', subActionParams: { title } }, - }), - signal, - }); -} - -export interface GetIssueTypeProps { - http: HttpSetup; - signal: AbortSignal; - connectorId: string; - id: string; -} - -export async function getIssue({ - http, - signal, - connectorId, - id, -}: GetIssueTypeProps): Promise> { - return http.post(`${BASE_ACTION_API_PATH}/action/${connectorId}/_execute`, { - body: JSON.stringify({ - params: { subAction: 'issue', subActionParams: { id } }, - }), - signal, - }); -} diff --git a/x-pack/plugins/cases/public/components/connectors/jira/case_fields.test.tsx b/x-pack/plugins/cases/public/components/connectors/jira/case_fields.test.tsx deleted file mode 100644 index 38a1e30616200..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/jira/case_fields.test.tsx +++ /dev/null @@ -1,262 +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 from 'react'; -import { mount } from 'enzyme'; -import { omit } from 'lodash/fp'; - -import { connector, issues } from '../mock'; -import { useGetIssueTypes } from './use_get_issue_types'; -import { useGetFieldsByIssueType } from './use_get_fields_by_issue_type'; -import Fields from './case_fields'; -import { waitFor } from '@testing-library/dom'; -import { useGetSingleIssue } from './use_get_single_issue'; -import { useGetIssues } from './use_get_issues'; -import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; - -jest.mock('./use_get_issue_types'); -jest.mock('./use_get_fields_by_issue_type'); -jest.mock('./use_get_single_issue'); -jest.mock('./use_get_issues'); -jest.mock('../../../common/lib/kibana'); -const useGetIssueTypesMock = useGetIssueTypes as jest.Mock; -const useGetFieldsByIssueTypeMock = useGetFieldsByIssueType as jest.Mock; -const useGetSingleIssueMock = useGetSingleIssue as jest.Mock; -const useGetIssuesMock = useGetIssues as jest.Mock; - -describe('Jira Fields', () => { - const useGetIssueTypesResponse = { - isLoading: false, - issueTypes: [ - { - id: '10006', - name: 'Task', - }, - { - id: '10007', - name: 'Bug', - }, - ], - }; - - const useGetFieldsByIssueTypeResponse = { - isLoading: false, - fields: { - summary: { allowedValues: [], defaultValue: {} }, - labels: { allowedValues: [], defaultValue: {} }, - description: { allowedValues: [], defaultValue: {} }, - priority: { - allowedValues: [ - { - name: 'Medium', - id: '3', - }, - { - name: 'Low', - id: '2', - }, - ], - defaultValue: { name: 'Medium', id: '3' }, - }, - }, - }; - - const useGetSingleIssueResponse = { - isLoading: false, - issue: { title: 'Parent Task', key: 'parentId' }, - }; - - const fields = { - issueType: '10006', - priority: 'High', - parent: null, - }; - - const useGetIssuesResponse = { - isLoading: false, - issues, - }; - - const onChange = jest.fn(); - - beforeEach(() => { - useGetIssueTypesMock.mockReturnValue(useGetIssueTypesResponse); - useGetFieldsByIssueTypeMock.mockReturnValue(useGetFieldsByIssueTypeResponse); - useGetSingleIssueMock.mockReturnValue(useGetSingleIssueResponse); - jest.clearAllMocks(); - }); - - test('all params fields are rendered - isEdit: true', () => { - const wrapper = mount(); - expect(wrapper.find('[data-test-subj="issueTypeSelect"]').first().prop('value')).toStrictEqual( - '10006' - ); - expect(wrapper.find('[data-test-subj="prioritySelect"]').first().prop('value')).toStrictEqual( - 'High' - ); - expect(wrapper.find('[data-test-subj="search-parent-issues"]').first().exists()).toBeFalsy(); - }); - - test('all params fields are rendered - isEdit: false', () => { - const wrapper = mount( - - ); - expect(wrapper.find('[data-test-subj="card-list-item"]').at(0).text()).toEqual( - 'Issue type: Task' - ); - expect(wrapper.find('[data-test-subj="card-list-item"]').at(1).text()).toEqual( - 'Parent issue: Parent Task' - ); - expect(wrapper.find('[data-test-subj="card-list-item"]').at(2).text()).toEqual( - 'Priority: High' - ); - }); - - test('it sets parent correctly', async () => { - useGetFieldsByIssueTypeMock.mockReturnValue({ - ...useGetFieldsByIssueTypeResponse, - fields: { - ...useGetFieldsByIssueTypeResponse.fields, - parent: {}, - }, - }); - useGetIssuesMock.mockReturnValue(useGetIssuesResponse); - const wrapper = mount(); - - await waitFor(() => - ((wrapper.find(EuiComboBox).props() as unknown) as { - onChange: (a: EuiComboBoxOptionOption[]) => void; - }).onChange([{ label: 'parentId', value: 'parentId' }]) - ); - wrapper.update(); - expect(onChange).toHaveBeenCalledWith({ - issueType: '10006', - parent: 'parentId', - priority: 'High', - }); - }); - test('it searches parent correctly', async () => { - useGetFieldsByIssueTypeMock.mockReturnValue({ - ...useGetFieldsByIssueTypeResponse, - fields: { - ...useGetFieldsByIssueTypeResponse.fields, - parent: {}, - }, - }); - useGetSingleIssueMock.mockReturnValue({ useGetSingleIssueResponse, issue: null }); - useGetIssuesMock.mockReturnValue(useGetIssuesResponse); - const wrapper = mount(); - - await waitFor(() => - ((wrapper.find(EuiComboBox).props() as unknown) as { - onSearchChange: (a: string) => void; - }).onSearchChange('womanId') - ); - wrapper.update(); - expect(useGetIssuesMock.mock.calls[2][0].query).toEqual('womanId'); - }); - - test('it disabled the fields when loading issue types', () => { - useGetIssueTypesMock.mockReturnValue({ ...useGetIssueTypesResponse, isLoading: true }); - - const wrapper = mount(); - - expect( - wrapper.find('[data-test-subj="issueTypeSelect"]').first().prop('disabled') - ).toBeTruthy(); - expect(wrapper.find('[data-test-subj="prioritySelect"]').first().prop('disabled')).toBeTruthy(); - }); - - test('it disabled the fields when loading fields', () => { - useGetFieldsByIssueTypeMock.mockReturnValue({ - ...useGetFieldsByIssueTypeResponse, - isLoading: true, - }); - - const wrapper = mount(); - - expect( - wrapper.find('[data-test-subj="issueTypeSelect"]').first().prop('disabled') - ).toBeTruthy(); - expect(wrapper.find('[data-test-subj="prioritySelect"]').first().prop('disabled')).toBeTruthy(); - }); - - test('it hides the priority if not supported', () => { - const response = omit('fields.priority', useGetFieldsByIssueTypeResponse); - - useGetFieldsByIssueTypeMock.mockReturnValue(response); - - const wrapper = mount(); - - expect(wrapper.find('[data-test-subj="prioritySelect"]').first().exists()).toBeFalsy(); - }); - - test('it sets issue type correctly', () => { - const wrapper = mount(); - - wrapper - .find('select[data-test-subj="issueTypeSelect"]') - .first() - .simulate('change', { - target: { value: '10007' }, - }); - - expect(onChange).toHaveBeenCalledWith({ issueType: '10007', parent: null, priority: null }); - }); - - test('it sets issue type when it comes as null', () => { - const wrapper = mount( - - ); - expect(wrapper.find('select[data-test-subj="issueTypeSelect"]').first().props().value).toEqual( - '10006' - ); - }); - - test('it sets issue type when it comes as unknown value', () => { - const wrapper = mount( - - ); - expect(wrapper.find('select[data-test-subj="issueTypeSelect"]').first().props().value).toEqual( - '10006' - ); - }); - - test('it sets priority correctly', () => { - const wrapper = mount(); - - wrapper - .find('select[data-test-subj="prioritySelect"]') - .first() - .simulate('change', { - target: { value: '2' }, - }); - - expect(onChange).toHaveBeenCalledWith({ issueType: '10006', parent: null, priority: '2' }); - }); - - test('it resets priority when changing issue type', () => { - const wrapper = mount(); - wrapper - .find('select[data-test-subj="issueTypeSelect"]') - .first() - .simulate('change', { - target: { value: '10007' }, - }); - - expect(onChange).toBeCalledWith({ issueType: '10007', parent: null, priority: null }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/connectors/jira/case_fields.tsx b/x-pack/plugins/cases/public/components/connectors/jira/case_fields.tsx deleted file mode 100644 index 6aff81f380015..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/jira/case_fields.tsx +++ /dev/null @@ -1,214 +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, useMemo, useEffect, useRef } from 'react'; -import { map } from 'lodash/fp'; -import { EuiFormRow, EuiSelect, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; -import * as i18n from './translations'; - -import { ConnectorTypes, JiraFieldsType } from '../../../../common'; -import { useKibana } from '../../../common/lib/kibana'; -import { ConnectorFieldsProps } from '../types'; -import { useGetIssueTypes } from './use_get_issue_types'; -import { useGetFieldsByIssueType } from './use_get_fields_by_issue_type'; -import { SearchIssues } from './search_issues'; -import { ConnectorCard } from '../card'; - -const JiraFieldsComponent: React.FunctionComponent> = ({ - connector, - fields, - isEdit = true, - onChange, -}) => { - const init = useRef(true); - const { issueType = null, priority = null, parent = null } = fields ?? {}; - const { http, notifications } = useKibana().services; - - const handleIssueType = useCallback( - (issueTypeSelectOptions: Array<{ value: string; text: string }>) => { - if (issueType == null && issueTypeSelectOptions.length > 0) { - // if there is no issue type set in the edit view, set it to default - if (isEdit) { - onChange({ - issueType: issueTypeSelectOptions[0].value, - parent, - priority, - }); - } - } - }, - [isEdit, issueType, onChange, parent, priority] - ); - const { isLoading: isLoadingIssueTypes, issueTypes } = useGetIssueTypes({ - connector, - http, - toastNotifications: notifications.toasts, - handleIssueType, - }); - - const issueTypesSelectOptions = useMemo( - () => - issueTypes.map((type) => ({ - text: type.name ?? '', - value: type.id ?? '', - })), - [issueTypes] - ); - - const currentIssueType = useMemo(() => { - if (!issueType && issueTypesSelectOptions.length > 0) { - return issueTypesSelectOptions[0].value; - } else if ( - issueTypesSelectOptions.length > 0 && - !issueTypesSelectOptions.some(({ value }) => value === issueType) - ) { - return issueTypesSelectOptions[0].value; - } - return issueType; - }, [issueType, issueTypesSelectOptions]); - - const { isLoading: isLoadingFields, fields: fieldsByIssueType } = useGetFieldsByIssueType({ - connector, - http, - issueType: currentIssueType, - toastNotifications: notifications.toasts, - }); - - const hasPriority = useMemo(() => fieldsByIssueType.priority != null, [fieldsByIssueType]); - - const hasParent = useMemo(() => fieldsByIssueType.parent != null, [fieldsByIssueType]); - - const prioritiesSelectOptions = useMemo(() => { - const priorities = fieldsByIssueType.priority?.allowedValues ?? []; - return map( - (p) => ({ - text: p.name, - value: p.name, - }), - priorities - ); - }, [fieldsByIssueType]); - - const listItems = useMemo( - () => [ - ...(issueType != null && issueType.length > 0 - ? [ - { - title: i18n.ISSUE_TYPE, - description: issueTypes.find((issue) => issue.id === issueType)?.name ?? '', - }, - ] - : []), - ...(parent != null && parent.length > 0 - ? [ - { - title: i18n.PARENT_ISSUE, - description: parent, - }, - ] - : []), - ...(priority != null && priority.length > 0 - ? [ - { - title: i18n.PRIORITY, - description: priority, - }, - ] - : []), - ], - [issueType, issueTypes, parent, priority] - ); - - const onFieldChange = useCallback( - (key, value) => { - if (key === 'issueType') { - return onChange({ ...fields, issueType: value, priority: null, parent: null }); - } - return onChange({ - ...fields, - issueType: currentIssueType, - parent, - priority, - [key]: value, - }); - }, - [currentIssueType, fields, onChange, parent, priority] - ); - - // Set field at initialization - useEffect(() => { - if (init.current) { - init.current = false; - onChange({ issueType, priority, parent }); - } - }, [issueType, onChange, parent, priority]); - - return isEdit ? ( -
- - onFieldChange('issueType', e.target.value)} - options={issueTypesSelectOptions} - value={currentIssueType ?? ''} - /> - - - <> - {hasParent && ( - <> - - - - onFieldChange('parent', parentIssueKey)} - selectedValue={parent} - /> - - - - - - )} - {hasPriority && ( - <> - - - - onFieldChange('priority', e.target.value)} - options={prioritiesSelectOptions} - value={priority ?? ''} - /> - - - - - )} - -
- ) : ( - - ); -}; - -// eslint-disable-next-line import/no-default-export -export { JiraFieldsComponent as default }; diff --git a/x-pack/plugins/cases/public/components/connectors/jira/index.ts b/x-pack/plugins/cases/public/components/connectors/jira/index.ts deleted file mode 100644 index ea408a1bd6664..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/jira/index.ts +++ /dev/null @@ -1,27 +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 { lazy } from 'react'; - -import { CaseConnector } from '../types'; -import { JiraFieldsType } from '../../../../common'; -import * as i18n from './translations'; - -export * from './types'; - -export const getCaseConnector = (): CaseConnector => { - return { - id: '.jira', - fieldsComponent: lazy(() => import('./case_fields')), - }; -}; - -export const fieldLabels = { - issueType: i18n.ISSUE_TYPE, - priority: i18n.PRIORITY, - parent: i18n.PARENT_ISSUE, -}; diff --git a/x-pack/plugins/cases/public/components/connectors/jira/search_issues.tsx b/x-pack/plugins/cases/public/components/connectors/jira/search_issues.tsx deleted file mode 100644 index 9270abed0881f..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/jira/search_issues.tsx +++ /dev/null @@ -1,95 +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, { useMemo, useEffect, useCallback, useState, memo } from 'react'; -import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; - -import { useKibana } from '../../../common/lib/kibana'; -import { ActionConnector } from '../../../containers/types'; -import { useGetIssues } from './use_get_issues'; -import { useGetSingleIssue } from './use_get_single_issue'; -import * as i18n from './translations'; - -interface Props { - selectedValue: string | null; - actionConnector?: ActionConnector; - onChange: (parentIssueKey: string) => void; -} - -const SearchIssuesComponent: React.FC = ({ selectedValue, actionConnector, onChange }) => { - const [query, setQuery] = useState(null); - const [selectedOptions, setSelectedOptions] = useState>>( - [] - ); - const [options, setOptions] = useState>>([]); - const { http, notifications } = useKibana().services; - - const { isLoading: isLoadingIssues, issues } = useGetIssues({ - http, - toastNotifications: notifications.toasts, - actionConnector, - query, - }); - - const { isLoading: isLoadingSingleIssue, issue: singleIssue } = useGetSingleIssue({ - http, - toastNotifications: notifications.toasts, - actionConnector, - id: selectedValue, - }); - - useEffect(() => setOptions(issues.map((issue) => ({ label: issue.title, value: issue.key }))), [ - issues, - ]); - - useEffect(() => { - if (isLoadingSingleIssue || singleIssue == null) { - return; - } - - const singleIssueAsOptions = [{ label: singleIssue.title, value: singleIssue.key }]; - setOptions(singleIssueAsOptions); - setSelectedOptions(singleIssueAsOptions); - }, [singleIssue, isLoadingSingleIssue]); - - const onSearchChange = useCallback((searchVal: string) => { - setQuery(searchVal); - }, []); - - const onChangeComboBox = useCallback( - (changedOptions) => { - setSelectedOptions(changedOptions); - onChange(changedOptions[0].value); - }, - [onChange] - ); - - const inputPlaceholder = useMemo( - (): string => - isLoadingIssues || isLoadingSingleIssue - ? i18n.SEARCH_ISSUES_LOADING - : i18n.SEARCH_ISSUES_PLACEHOLDER, - [isLoadingIssues, isLoadingSingleIssue] - ); - - return ( - - ); -}; - -export const SearchIssues = memo(SearchIssuesComponent); diff --git a/x-pack/plugins/cases/public/components/connectors/jira/translations.ts b/x-pack/plugins/cases/public/components/connectors/jira/translations.ts deleted file mode 100644 index 88dd7d0c7c27b..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/jira/translations.ts +++ /dev/null @@ -1,68 +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 { i18n } from '@kbn/i18n'; - -export const ISSUE_TYPES_API_ERROR = i18n.translate( - 'xpack.cases.connectors.jira.unableToGetIssueTypesMessage', - { - defaultMessage: 'Unable to get issue types', - } -); - -export const FIELDS_API_ERROR = i18n.translate( - 'xpack.cases.connectors.jira.unableToGetFieldsMessage', - { - defaultMessage: 'Unable to get connectors', - } -); - -export const ISSUES_API_ERROR = i18n.translate( - 'xpack.cases.connectors.jira.unableToGetIssuesMessage', - { - defaultMessage: 'Unable to get issues', - } -); - -export const GET_ISSUE_API_ERROR = (id: string) => - i18n.translate('xpack.cases.connectors.jira.unableToGetIssueMessage', { - defaultMessage: 'Unable to get issue with id {id}', - values: { id }, - }); - -export const SEARCH_ISSUES_COMBO_BOX_ARIA_LABEL = i18n.translate( - 'xpack.cases.connectors.jira.searchIssuesComboBoxAriaLabel', - { - defaultMessage: 'Type to search', - } -); - -export const SEARCH_ISSUES_PLACEHOLDER = i18n.translate( - 'xpack.cases.connectors.jira.searchIssuesComboBoxPlaceholder', - { - defaultMessage: 'Type to search', - } -); - -export const SEARCH_ISSUES_LOADING = i18n.translate( - 'xpack.cases.connectors.jira.searchIssuesLoading', - { - defaultMessage: 'Loading...', - } -); - -export const PRIORITY = i18n.translate('xpack.cases.connectors.jira.prioritySelectFieldLabel', { - defaultMessage: 'Priority', -}); - -export const ISSUE_TYPE = i18n.translate('xpack.cases.connectors.jira.issueTypesSelectFieldLabel', { - defaultMessage: 'Issue type', -}); - -export const PARENT_ISSUE = i18n.translate('xpack.cases.connectors.jira.parentIssueSearchLabel', { - defaultMessage: 'Parent issue', -}); diff --git a/x-pack/plugins/cases/public/components/connectors/jira/types.ts b/x-pack/plugins/cases/public/components/connectors/jira/types.ts deleted file mode 100644 index 76c08a852c679..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/jira/types.ts +++ /dev/null @@ -1,22 +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. - */ - -export type IssueTypes = Array<{ id: string; name: string }>; -export interface Fields { - [key: string]: { - allowedValues: Array<{ name: string; id: string }> | []; - defaultValue: { name: string; id: string } | {}; - }; -} - -export interface Issue { - id: string; - key: string; - title: string; -} - -export type Issues = Issue[]; diff --git a/x-pack/plugins/cases/public/components/connectors/jira/use_get_fields_by_issue_type.test.tsx b/x-pack/plugins/cases/public/components/connectors/jira/use_get_fields_by_issue_type.test.tsx deleted file mode 100644 index b4c2c848d79ed..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/jira/use_get_fields_by_issue_type.test.tsx +++ /dev/null @@ -1,105 +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 { renderHook, act } from '@testing-library/react-hooks'; - -import { useKibana } from '../../../common/lib/kibana'; -import { connector } from '../mock'; -import { useGetFieldsByIssueType, UseGetFieldsByIssueType } from './use_get_fields_by_issue_type'; -import * as api from './api'; - -jest.mock('../../../common/lib/kibana'); -jest.mock('./api'); - -const useKibanaMock = useKibana as jest.Mocked; - -describe('useGetFieldsByIssueType', () => { - const { http, notifications } = useKibanaMock().services; - - test('init', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useGetFieldsByIssueType({ http, toastNotifications: notifications.toasts, issueType: null }) - ); - await waitForNextUpdate(); - expect(result.current).toEqual({ isLoading: true, fields: {} }); - }); - }); - - test('does not fetch when issueType is not provided', async () => { - const spyOnGetFieldsByIssueType = jest.spyOn(api, 'getFieldsByIssueType'); - - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useGetFieldsByIssueType({ - http, - toastNotifications: notifications.toasts, - connector, - issueType: null, - }) - ); - - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(spyOnGetFieldsByIssueType).not.toHaveBeenCalled(); - expect(result.current).toEqual({ isLoading: false, fields: {} }); - }); - }); - - test('fetch fields', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useGetFieldsByIssueType({ - http, - toastNotifications: notifications.toasts, - connector, - issueType: 'Task', - }) - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(result.current).toEqual({ - isLoading: false, - fields: { - summary: { allowedValues: [], defaultValue: {} }, - priority: { - allowedValues: [ - { - name: 'Medium', - id: '3', - }, - ], - defaultValue: { name: 'Medium', id: '3' }, - }, - }, - }); - }); - }); - - test('unhappy path', async () => { - const spyOnGetCaseConfigure = jest.spyOn(api, 'getFieldsByIssueType'); - spyOnGetCaseConfigure.mockImplementation(() => { - throw new Error('Something went wrong'); - }); - - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useGetFieldsByIssueType({ - http, - toastNotifications: notifications.toasts, - connector, - issueType: null, - }) - ); - - await waitForNextUpdate(); - await waitForNextUpdate(); - - expect(result.current).toEqual({ isLoading: false, fields: {} }); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/connectors/jira/use_get_fields_by_issue_type.tsx b/x-pack/plugins/cases/public/components/connectors/jira/use_get_fields_by_issue_type.tsx deleted file mode 100644 index 03000e8916617..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/jira/use_get_fields_by_issue_type.tsx +++ /dev/null @@ -1,96 +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 { useState, useEffect, useRef } from 'react'; -import { HttpSetup, ToastsApi } from 'kibana/public'; -import { ActionConnector } from '../../../containers/types'; -import { getFieldsByIssueType } from './api'; -import { Fields } from './types'; -import * as i18n from './translations'; - -interface Props { - http: HttpSetup; - toastNotifications: Pick< - ToastsApi, - 'get$' | 'add' | 'remove' | 'addSuccess' | 'addWarning' | 'addDanger' | 'addError' - >; - issueType: string | null; - connector?: ActionConnector; -} - -export interface UseGetFieldsByIssueType { - fields: Fields; - isLoading: boolean; -} - -export const useGetFieldsByIssueType = ({ - http, - toastNotifications, - connector, - issueType, -}: Props): UseGetFieldsByIssueType => { - const [isLoading, setIsLoading] = useState(true); - const [fields, setFields] = useState({}); - const didCancel = useRef(false); - const abortCtrl = useRef(new AbortController()); - - useEffect(() => { - const fetchData = async () => { - if (!connector || !issueType) { - setIsLoading(false); - return; - } - - try { - abortCtrl.current = new AbortController(); - setIsLoading(true); - - const res = await getFieldsByIssueType({ - http, - signal: abortCtrl.current.signal, - connectorId: connector.id, - id: issueType, - }); - - if (!didCancel.current) { - setIsLoading(false); - setFields(res.data ?? {}); - if (res.status && res.status === 'error') { - toastNotifications.addDanger({ - title: i18n.FIELDS_API_ERROR, - text: `${res.serviceMessage ?? res.message}`, - }); - } - } - } catch (error) { - if (!didCancel.current) { - setIsLoading(false); - if (error.name !== 'AbortError') { - toastNotifications.addDanger({ - title: i18n.FIELDS_API_ERROR, - text: error.message, - }); - } - } - } - }; - - didCancel.current = false; - abortCtrl.current.abort(); - fetchData(); - - return () => { - didCancel.current = true; - abortCtrl.current.abort(); - }; - }, [http, connector, issueType, toastNotifications]); - - return { - isLoading, - fields, - }; -}; diff --git a/x-pack/plugins/cases/public/components/connectors/jira/use_get_issue_types.test.tsx b/x-pack/plugins/cases/public/components/connectors/jira/use_get_issue_types.test.tsx deleted file mode 100644 index 6c1a9b5fcab08..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/jira/use_get_issue_types.test.tsx +++ /dev/null @@ -1,107 +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 { renderHook, act } from '@testing-library/react-hooks'; - -import { useKibana } from '../../../common/lib/kibana'; -import { connector } from '../mock'; -import { useGetIssueTypes, UseGetIssueTypes } from './use_get_issue_types'; -import * as api from './api'; - -jest.mock('../../../common/lib/kibana'); -jest.mock('./api'); - -const useKibanaMock = useKibana as jest.Mocked; - -describe('useGetIssueTypes', () => { - const { http, notifications } = useKibanaMock().services; - const handleIssueType = jest.fn(); - - beforeEach(() => jest.clearAllMocks()); - - test('init', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useGetIssueTypes({ http, toastNotifications: notifications.toasts, handleIssueType }) - ); - await waitForNextUpdate(); - expect(result.current).toEqual({ isLoading: true, issueTypes: [] }); - }); - }); - - test('fetch issue types', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useGetIssueTypes({ - http, - toastNotifications: notifications.toasts, - connector, - handleIssueType, - }) - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(result.current).toEqual({ - isLoading: false, - issueTypes: [ - { - id: '10006', - name: 'Task', - }, - { - id: '10007', - name: 'Bug', - }, - ], - }); - }); - }); - - test('handleIssueType is called', async () => { - await act(async () => { - const { waitForNextUpdate } = renderHook(() => - useGetIssueTypes({ - http, - toastNotifications: notifications.toasts, - connector, - handleIssueType, - }) - ); - - await waitForNextUpdate(); - await waitForNextUpdate(); - - expect(handleIssueType).toHaveBeenCalledWith([ - { text: 'Task', value: '10006' }, - { text: 'Bug', value: '10007' }, - ]); - }); - }); - - test('unhappy path', async () => { - const spyOnGetCaseConfigure = jest.spyOn(api, 'getIssueTypes'); - spyOnGetCaseConfigure.mockImplementation(() => { - throw new Error('Something went wrong'); - }); - - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useGetIssueTypes({ - http, - toastNotifications: notifications.toasts, - connector, - handleIssueType, - }) - ); - - await waitForNextUpdate(); - await waitForNextUpdate(); - - expect(result.current).toEqual({ isLoading: false, issueTypes: [] }); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/connectors/jira/use_get_issue_types.tsx b/x-pack/plugins/cases/public/components/connectors/jira/use_get_issue_types.tsx deleted file mode 100644 index 3c35d315a2bcd..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/jira/use_get_issue_types.tsx +++ /dev/null @@ -1,102 +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 { useState, useEffect, useRef } from 'react'; -import { HttpSetup, ToastsApi } from 'kibana/public'; -import { ActionConnector } from '../../../containers/types'; -import { getIssueTypes } from './api'; -import { IssueTypes } from './types'; -import * as i18n from './translations'; - -interface Props { - http: HttpSetup; - toastNotifications: Pick< - ToastsApi, - 'get$' | 'add' | 'remove' | 'addSuccess' | 'addWarning' | 'addDanger' | 'addError' - >; - connector?: ActionConnector; - handleIssueType: (options: Array<{ value: string; text: string }>) => void; -} - -export interface UseGetIssueTypes { - issueTypes: IssueTypes; - isLoading: boolean; -} - -export const useGetIssueTypes = ({ - http, - connector, - toastNotifications, - handleIssueType, -}: Props): UseGetIssueTypes => { - const [isLoading, setIsLoading] = useState(true); - const [issueTypes, setIssueTypes] = useState([]); - const didCancel = useRef(false); - const abortCtrl = useRef(new AbortController()); - - useEffect(() => { - const fetchData = async () => { - if (!connector) { - setIsLoading(false); - return; - } - - try { - abortCtrl.current = new AbortController(); - setIsLoading(true); - - const res = await getIssueTypes({ - http, - signal: abortCtrl.current.signal, - connectorId: connector.id, - }); - - if (!didCancel.current) { - setIsLoading(false); - const asOptions = (res.data ?? []).map((type) => ({ - text: type.name ?? '', - value: type.id ?? '', - })); - setIssueTypes(res.data ?? []); - handleIssueType(asOptions); - if (res.status && res.status === 'error') { - toastNotifications.addDanger({ - title: i18n.ISSUE_TYPES_API_ERROR, - text: `${res.serviceMessage ?? res.message}`, - }); - } - } - } catch (error) { - if (!didCancel.current) { - setIsLoading(false); - if (error.name !== 'AbortError') { - toastNotifications.addDanger({ - title: i18n.ISSUE_TYPES_API_ERROR, - text: error.message, - }); - } - } - } - }; - - didCancel.current = false; - abortCtrl.current.abort(); - fetchData(); - - return () => { - didCancel.current = true; - abortCtrl.current.abort(); - }; - // handleIssueType unmounts the component at init causing the request to be aborted - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [http, connector, toastNotifications]); - - return { - issueTypes, - isLoading, - }; -}; diff --git a/x-pack/plugins/cases/public/components/connectors/jira/use_get_issues.test.tsx b/x-pack/plugins/cases/public/components/connectors/jira/use_get_issues.test.tsx deleted file mode 100644 index 2308fe604e710..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/jira/use_get_issues.test.tsx +++ /dev/null @@ -1,80 +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 { renderHook, act } from '@testing-library/react-hooks'; - -import { useKibana } from '../../../common/lib/kibana'; -import { connector as actionConnector, issues } from '../mock'; -import { useGetIssues, UseGetIssues } from './use_get_issues'; -import * as api from './api'; - -jest.mock('../../../common/lib/kibana'); -jest.mock('./api'); - -const useKibanaMock = useKibana as jest.Mocked; - -describe('useGetIssues', () => { - const { http, notifications } = useKibanaMock().services; - beforeEach(() => jest.clearAllMocks()); - - test('init', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useGetIssues({ - http, - toastNotifications: notifications.toasts, - actionConnector, - query: null, - }) - ); - await waitForNextUpdate(); - expect(result.current).toEqual({ isLoading: false, issues: [] }); - }); - }); - - test('fetch issues', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useGetIssues({ - http, - toastNotifications: notifications.toasts, - actionConnector, - query: 'Task', - }) - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(result.current).toEqual({ - isLoading: false, - issues, - }); - }); - }); - - test('unhappy path', async () => { - const spyOnGetCaseConfigure = jest.spyOn(api, 'getIssues'); - spyOnGetCaseConfigure.mockImplementation(() => { - throw new Error('Something went wrong'); - }); - - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useGetIssues({ - http, - toastNotifications: notifications.toasts, - actionConnector, - query: 'oh no', - }) - ); - - await waitForNextUpdate(); - await waitForNextUpdate(); - - expect(result.current).toEqual({ isLoading: false, issues: [] }); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/connectors/jira/use_get_issues.tsx b/x-pack/plugins/cases/public/components/connectors/jira/use_get_issues.tsx deleted file mode 100644 index b44b0558f1536..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/jira/use_get_issues.tsx +++ /dev/null @@ -1,97 +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 { isEmpty, debounce } from 'lodash/fp'; -import { useState, useEffect, useRef } from 'react'; -import { HttpSetup, ToastsApi } from 'kibana/public'; -import { ActionConnector } from '../../../containers/types'; -import { getIssues } from './api'; -import { Issues } from './types'; -import * as i18n from './translations'; - -interface Props { - http: HttpSetup; - toastNotifications: Pick< - ToastsApi, - 'get$' | 'add' | 'remove' | 'addSuccess' | 'addWarning' | 'addDanger' | 'addError' - >; - actionConnector?: ActionConnector; - query: string | null; -} - -export interface UseGetIssues { - issues: Issues; - isLoading: boolean; -} - -export const useGetIssues = ({ - http, - actionConnector, - toastNotifications, - query, -}: Props): UseGetIssues => { - const [isLoading, setIsLoading] = useState(false); - const [issues, setIssues] = useState([]); - const didCancel = useRef(false); - const abortCtrl = useRef(new AbortController()); - - useEffect(() => { - const fetchData = debounce(500, async () => { - if (!actionConnector || isEmpty(query)) { - setIsLoading(false); - return; - } - - try { - abortCtrl.current = new AbortController(); - setIsLoading(true); - - const res = await getIssues({ - http, - signal: abortCtrl.current.signal, - connectorId: actionConnector.id, - title: query ?? '', - }); - - if (!didCancel.current) { - setIsLoading(false); - setIssues(res.data ?? []); - if (res.status && res.status === 'error') { - toastNotifications.addDanger({ - title: i18n.ISSUES_API_ERROR, - text: `${res.serviceMessage ?? res.message}`, - }); - } - } - } catch (error) { - if (!didCancel.current) { - setIsLoading(false); - if (error.name !== 'AbortError') { - toastNotifications.addDanger({ - title: i18n.ISSUES_API_ERROR, - text: error.message, - }); - } - } - } - }); - - didCancel.current = false; - abortCtrl.current.abort(); - fetchData(); - - return () => { - didCancel.current = true; - abortCtrl.current.abort(); - }; - }, [http, actionConnector, toastNotifications, query]); - - return { - issues, - isLoading, - }; -}; diff --git a/x-pack/plugins/cases/public/components/connectors/jira/use_get_single_issue.test.tsx b/x-pack/plugins/cases/public/components/connectors/jira/use_get_single_issue.test.tsx deleted file mode 100644 index 28949b456ecdd..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/jira/use_get_single_issue.test.tsx +++ /dev/null @@ -1,80 +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 { renderHook, act } from '@testing-library/react-hooks'; - -import { useKibana } from '../../../common/lib/kibana'; -import { connector as actionConnector, issues } from '../mock'; -import { useGetSingleIssue, UseGetSingleIssue } from './use_get_single_issue'; -import * as api from './api'; - -jest.mock('../../../common/lib/kibana'); -jest.mock('./api'); - -const useKibanaMock = useKibana as jest.Mocked; - -describe('useGetSingleIssue', () => { - const { http, notifications } = useKibanaMock().services; - beforeEach(() => jest.clearAllMocks()); - - test('init', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useGetSingleIssue({ - http, - toastNotifications: notifications.toasts, - actionConnector, - id: null, - }) - ); - await waitForNextUpdate(); - expect(result.current).toEqual({ isLoading: false, issue: null }); - }); - }); - - test('fetch issues', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useGetSingleIssue({ - http, - toastNotifications: notifications.toasts, - actionConnector, - id: '123', - }) - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(result.current).toEqual({ - isLoading: false, - issue: issues[0], - }); - }); - }); - - test('unhappy path', async () => { - const spyOnGetCaseConfigure = jest.spyOn(api, 'getIssue'); - spyOnGetCaseConfigure.mockImplementation(() => { - throw new Error('Something went wrong'); - }); - - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useGetSingleIssue({ - http, - toastNotifications: notifications.toasts, - actionConnector, - id: '123', - }) - ); - - await waitForNextUpdate(); - await waitForNextUpdate(); - - expect(result.current).toEqual({ isLoading: false, issue: null }); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/connectors/jira/use_get_single_issue.tsx b/x-pack/plugins/cases/public/components/connectors/jira/use_get_single_issue.tsx deleted file mode 100644 index 6c70286426168..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/jira/use_get_single_issue.tsx +++ /dev/null @@ -1,95 +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 { useState, useEffect, useRef } from 'react'; -import { HttpSetup, ToastsApi } from 'kibana/public'; -import { ActionConnector } from '../../../containers/types'; -import { getIssue } from './api'; -import { Issue } from './types'; -import * as i18n from './translations'; - -interface Props { - http: HttpSetup; - toastNotifications: Pick< - ToastsApi, - 'get$' | 'add' | 'remove' | 'addSuccess' | 'addWarning' | 'addDanger' | 'addError' - >; - id: string | null; - actionConnector?: ActionConnector; -} - -export interface UseGetSingleIssue { - issue: Issue | null; - isLoading: boolean; -} - -export const useGetSingleIssue = ({ - http, - toastNotifications, - actionConnector, - id, -}: Props): UseGetSingleIssue => { - const [isLoading, setIsLoading] = useState(false); - const [issue, setIssue] = useState(null); - const didCancel = useRef(false); - const abortCtrl = useRef(new AbortController()); - - useEffect(() => { - const fetchData = async () => { - if (!actionConnector || !id) { - setIsLoading(false); - return; - } - - abortCtrl.current = new AbortController(); - setIsLoading(true); - try { - const res = await getIssue({ - http, - signal: abortCtrl.current.signal, - connectorId: actionConnector.id, - id, - }); - - if (!didCancel.current) { - setIsLoading(false); - setIssue(res.data ?? null); - if (res.status && res.status === 'error') { - toastNotifications.addDanger({ - title: i18n.GET_ISSUE_API_ERROR(id), - text: `${res.serviceMessage ?? res.message}`, - }); - } - } - } catch (error) { - if (!didCancel.current) { - setIsLoading(false); - if (error.name !== 'AbortError') { - toastNotifications.addDanger({ - title: i18n.GET_ISSUE_API_ERROR(id), - text: error.message, - }); - } - } - } - }; - - didCancel.current = false; - abortCtrl.current.abort(); - fetchData(); - - return () => { - didCancel.current = true; - abortCtrl.current.abort(); - }; - }, [http, actionConnector, id, toastNotifications]); - - return { - isLoading, - issue, - }; -}; diff --git a/x-pack/plugins/cases/public/components/connectors/mock.ts b/x-pack/plugins/cases/public/components/connectors/mock.ts deleted file mode 100644 index f5429fa2396aa..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/mock.ts +++ /dev/null @@ -1,121 +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. - */ - -export const connector = { - id: '123', - name: 'My connector', - actionTypeId: '.jira', - config: {}, - isPreconfigured: false, -}; - -export const issues = [ - { id: 'personId', title: 'Person Task', key: 'personKey' }, - { id: 'womanId', title: 'Woman Task', key: 'womanKey' }, - { id: 'manId', title: 'Man Task', key: 'manKey' }, - { id: 'cameraId', title: 'Camera Task', key: 'cameraKey' }, - { id: 'tvId', title: 'TV Task', key: 'tvKey' }, -]; - -export const choices = [ - { - dependent_value: '', - label: 'Priviledge Escalation', - value: 'Priviledge Escalation', - element: 'category', - }, - { - dependent_value: '', - label: 'Criminal activity/investigation', - value: 'Criminal activity/investigation', - element: 'category', - }, - { - dependent_value: '', - label: 'Denial of Service', - value: 'Denial of Service', - element: 'category', - }, - { - dependent_value: 'Denial of Service', - label: 'Inbound or outbound', - value: '12', - element: 'subcategory', - }, - { - dependent_value: 'Denial of Service', - label: 'Single or distributed (DoS or DDoS)', - value: '26', - element: 'subcategory', - }, - { - dependent_value: 'Denial of Service', - label: 'Inbound DDos', - value: 'inbound_ddos', - element: 'subcategory', - }, - { - dependent_value: '', - label: 'Software', - value: 'software', - element: 'category', - }, - { - dependent_value: 'software', - label: 'Operation System', - value: 'os', - element: 'subcategory', - }, - ...['severity', 'urgency', 'impact', 'priority'] - .map((element) => [ - { - dependent_value: '', - label: '1 - Critical', - value: '1', - element, - }, - { - dependent_value: '', - label: '2 - High', - value: '2', - element, - }, - { - dependent_value: '', - label: '3 - Moderate', - value: '3', - element, - }, - { - dependent_value: '', - label: '4 - Low', - value: '4', - element, - }, - ]) - .flat(), -]; - -export const severity = [ - { - id: 4, - name: 'Low', - }, - { - id: 5, - name: 'Medium', - }, - { - id: 6, - name: 'High', - }, -]; - -export const incidentTypes = [ - { id: 17, name: 'Communication error (fax; email)' }, - { id: 1001, name: 'Custom type' }, -]; diff --git a/x-pack/plugins/cases/public/components/connectors/resilient/__mocks__/api.ts b/x-pack/plugins/cases/public/components/connectors/resilient/__mocks__/api.ts deleted file mode 100644 index c27248288907d..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/resilient/__mocks__/api.ts +++ /dev/null @@ -1,16 +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 { incidentTypes, severity } from '../../mock'; -import { Props } from '../api'; -import { ResilientIncidentTypes, ResilientSeverity } from '../types'; - -export const getIncidentTypes = async (props: Props): Promise<{ data: ResilientIncidentTypes }> => - Promise.resolve({ data: incidentTypes }); - -export const getSeverity = async (props: Props): Promise<{ data: ResilientSeverity }> => - Promise.resolve({ data: severity }); diff --git a/x-pack/plugins/cases/public/components/connectors/resilient/api.ts b/x-pack/plugins/cases/public/components/connectors/resilient/api.ts deleted file mode 100644 index 5fec83f303950..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/resilient/api.ts +++ /dev/null @@ -1,42 +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 { HttpSetup } from 'kibana/public'; -import { ActionTypeExecutorResult } from '../../../../../actions/common'; -import { ResilientIncidentTypes, ResilientSeverity } from './types'; - -export const BASE_ACTION_API_PATH = '/api/actions'; - -export interface Props { - http: HttpSetup; - signal: AbortSignal; - connectorId: string; -} - -export async function getIncidentTypes({ http, signal, connectorId }: Props) { - return http.post>( - `${BASE_ACTION_API_PATH}/action/${connectorId}/_execute`, - { - body: JSON.stringify({ - params: { subAction: 'incidentTypes', subActionParams: {} }, - }), - signal, - } - ); -} - -export async function getSeverity({ http, signal, connectorId }: Props) { - return http.post>( - `${BASE_ACTION_API_PATH}/action/${connectorId}/_execute`, - { - body: JSON.stringify({ - params: { subAction: 'severity', subActionParams: {} }, - }), - signal, - } - ); -} diff --git a/x-pack/plugins/cases/public/components/connectors/resilient/case_fields.test.tsx b/x-pack/plugins/cases/public/components/connectors/resilient/case_fields.test.tsx deleted file mode 100644 index dda6ba5de95cc..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/resilient/case_fields.test.tsx +++ /dev/null @@ -1,134 +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 from 'react'; -import { mount } from 'enzyme'; -import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; -import { waitFor } from '@testing-library/react'; - -import { connector } from '../mock'; -import { useGetIncidentTypes } from './use_get_incident_types'; -import { useGetSeverity } from './use_get_severity'; -import Fields from './case_fields'; - -jest.mock('../../../common/lib/kibana'); -jest.mock('./use_get_incident_types'); -jest.mock('./use_get_severity'); - -const useGetIncidentTypesMock = useGetIncidentTypes as jest.Mock; -const useGetSeverityMock = useGetSeverity as jest.Mock; - -describe('ResilientParamsFields renders', () => { - const useGetIncidentTypesResponse = { - isLoading: false, - incidentTypes: [ - { - id: 19, - name: 'Malware', - }, - { - id: 21, - name: 'Denial of Service', - }, - ], - }; - - const useGetSeverityResponse = { - isLoading: false, - severity: [ - { - id: 4, - name: 'Low', - }, - { - id: 5, - name: 'Medium', - }, - { - id: 6, - name: 'High', - }, - ], - }; - - const fields = { - severityCode: '6', - incidentTypes: ['19'], - }; - - const onChange = jest.fn(); - - beforeEach(() => { - useGetIncidentTypesMock.mockReturnValue(useGetIncidentTypesResponse); - useGetSeverityMock.mockReturnValue(useGetSeverityResponse); - jest.clearAllMocks(); - }); - - test('all params fields are rendered', () => { - const wrapper = mount(); - expect(wrapper.find('[data-test-subj="incidentTypeComboBox"]').first().prop('options')).toEqual( - [ - { label: 'Malware', value: '19' }, - { label: 'Denial of Service', value: '21' }, - ] - ); - - expect( - wrapper.find('[data-test-subj="incidentTypeComboBox"]').first().prop('selectedOptions') - ).toEqual([{ label: 'Malware', value: '19' }]); - - expect(wrapper.find('[data-test-subj="severitySelect"]').first().prop('value')).toStrictEqual( - '6' - ); - }); - - test('it disabled the fields when loading incident types', () => { - useGetIncidentTypesMock.mockReturnValue({ ...useGetIncidentTypesResponse, isLoading: true }); - - const wrapper = mount(); - - expect( - wrapper.find('[data-test-subj="incidentTypeComboBox"]').first().prop('isDisabled') - ).toBeTruthy(); - }); - - test('it disabled the fields when loading severity', () => { - useGetSeverityMock.mockReturnValue({ - ...useGetSeverityResponse, - isLoading: true, - }); - - const wrapper = mount(); - - expect(wrapper.find('[data-test-subj="severitySelect"]').first().prop('disabled')).toBeTruthy(); - }); - - test('it sets issue type correctly', async () => { - const wrapper = mount(); - - await waitFor(() => { - ((wrapper.find(EuiComboBox).props() as unknown) as { - onChange: (a: EuiComboBoxOptionOption[]) => void; - }).onChange([{ value: '19', label: 'Denial of Service' }]); - }); - - expect(onChange).toHaveBeenCalledWith({ incidentTypes: ['19'], severityCode: '6' }); - }); - - test('it sets severity correctly', async () => { - const wrapper = mount(); - - wrapper - .find('select[data-test-subj="severitySelect"]') - .first() - .simulate('change', { - target: { value: '4' }, - }); - - expect(onChange).toHaveBeenCalledWith({ incidentTypes: ['19'], severityCode: '4' }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/connectors/resilient/case_fields.tsx b/x-pack/plugins/cases/public/components/connectors/resilient/case_fields.tsx deleted file mode 100644 index e1eeb13bf684c..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/resilient/case_fields.tsx +++ /dev/null @@ -1,189 +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, { useMemo, useCallback, useEffect, useRef } from 'react'; -import { - EuiComboBox, - EuiComboBoxOptionOption, - EuiFormRow, - EuiSelect, - EuiSelectOption, - EuiSpacer, -} from '@elastic/eui'; - -import { useKibana } from '../../../common/lib/kibana'; -import { ConnectorFieldsProps } from '../types'; -import { useGetIncidentTypes } from './use_get_incident_types'; -import { useGetSeverity } from './use_get_severity'; - -import * as i18n from './translations'; -import { ConnectorTypes, ResilientFieldsType } from '../../../../common'; -import { ConnectorCard } from '../card'; - -const ResilientFieldsComponent: React.FunctionComponent< - ConnectorFieldsProps -> = ({ isEdit = true, fields, connector, onChange }) => { - const init = useRef(true); - const { incidentTypes = null, severityCode = null } = fields ?? {}; - - const { http, notifications } = useKibana().services; - - const { - isLoading: isLoadingIncidentTypes, - incidentTypes: allIncidentTypes, - } = useGetIncidentTypes({ - http, - toastNotifications: notifications.toasts, - connector, - }); - - const { isLoading: isLoadingSeverity, severity } = useGetSeverity({ - http, - toastNotifications: notifications.toasts, - connector, - }); - - const severitySelectOptions: EuiSelectOption[] = useMemo( - () => - severity.map((s) => ({ - value: s.id.toString(), - text: s.name, - })), - [severity] - ); - - const incidentTypesComboBoxOptions: Array> = useMemo( - () => - allIncidentTypes - ? allIncidentTypes.map((type: { id: number; name: string }) => ({ - label: type.name, - value: type.id.toString(), - })) - : [], - [allIncidentTypes] - ); - const listItems = useMemo( - () => [ - ...(incidentTypes != null && incidentTypes.length > 0 - ? [ - { - title: i18n.INCIDENT_TYPES_LABEL, - description: allIncidentTypes - .filter((type) => incidentTypes.includes(type.id.toString())) - .map((type) => type.name) - .join(', '), - }, - ] - : []), - ...(severityCode != null && severityCode.length > 0 - ? [ - { - title: i18n.SEVERITY_LABEL, - description: - severity.find((severityObj) => severityObj.id.toString() === severityCode)?.name ?? - '', - }, - ] - : []), - ], - [incidentTypes, severityCode, allIncidentTypes, severity] - ); - - const onFieldChange = useCallback( - (key, value) => { - onChange({ - ...fields, - incidentTypes, - severityCode, - [key]: value, - }); - }, - [incidentTypes, severityCode, onChange, fields] - ); - - const selectedIncidentTypesComboBoxOptionsMemo = useMemo(() => { - const allIncidentTypesAsObject = allIncidentTypes.reduce( - (acc, type) => ({ ...acc, [type.id.toString()]: type.name }), - {} as Record - ); - return incidentTypes - ? incidentTypes - .map((type) => ({ - label: allIncidentTypesAsObject[type.toString()], - value: type.toString(), - })) - .filter((type) => type.label != null) - : []; - }, [allIncidentTypes, incidentTypes]); - - const onIncidentChange = useCallback( - (selectedOptions: Array<{ label: string; value?: string }>) => { - onFieldChange( - 'incidentTypes', - selectedOptions.map((selectedOption) => selectedOption.value ?? selectedOption.label) - ); - }, - [onFieldChange] - ); - - const onIncidentBlur = useCallback(() => { - if (!incidentTypes) { - onFieldChange('incidentTypes', []); - } - }, [incidentTypes, onFieldChange]); - - // Set field at initialization - useEffect(() => { - if (init.current) { - init.current = false; - onChange({ incidentTypes, severityCode }); - } - }, [incidentTypes, onChange, severityCode]); - - return isEdit ? ( - - - - - - - onFieldChange('severityCode', e.target.value)} - options={severitySelectOptions} - value={severityCode ?? undefined} - /> - - - - ) : ( - - ); -}; - -// eslint-disable-next-line import/no-default-export -export { ResilientFieldsComponent as default }; diff --git a/x-pack/plugins/cases/public/components/connectors/resilient/index.ts b/x-pack/plugins/cases/public/components/connectors/resilient/index.ts deleted file mode 100644 index c8e7ad9a063cb..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/resilient/index.ts +++ /dev/null @@ -1,26 +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 { lazy } from 'react'; - -import { CaseConnector } from '../types'; -import { ResilientFieldsType } from '../../../../common'; -import * as i18n from './translations'; - -export * from './types'; - -export const getCaseConnector = (): CaseConnector => { - return { - id: '.resilient', - fieldsComponent: lazy(() => import('./case_fields')), - }; -}; - -export const fieldLabels = { - incidentTypes: i18n.INCIDENT_TYPES_LABEL, - severityCode: i18n.SEVERITY_LABEL, -}; diff --git a/x-pack/plugins/cases/public/components/connectors/resilient/translations.ts b/x-pack/plugins/cases/public/components/connectors/resilient/translations.ts deleted file mode 100644 index 1b63a5098e92a..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/resilient/translations.ts +++ /dev/null @@ -1,40 +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 { i18n } from '@kbn/i18n'; - -export const INCIDENT_TYPES_API_ERROR = i18n.translate( - 'xpack.cases.connectors.resilient.unableToGetIncidentTypesMessage', - { - defaultMessage: 'Unable to get incident types', - } -); - -export const SEVERITY_API_ERROR = i18n.translate( - 'xpack.cases.connectors.resilient.unableToGetSeverityMessage', - { - defaultMessage: 'Unable to get severity', - } -); - -export const INCIDENT_TYPES_PLACEHOLDER = i18n.translate( - 'xpack.cases.connectors.resilient.incidentTypesPlaceholder', - { - defaultMessage: 'Choose types', - } -); - -export const INCIDENT_TYPES_LABEL = i18n.translate( - 'xpack.cases.connectors.resilient.incidentTypesLabel', - { - defaultMessage: 'Incident Types', - } -); - -export const SEVERITY_LABEL = i18n.translate('xpack.cases.connectors.resilient.severityLabel', { - defaultMessage: 'Severity', -}); diff --git a/x-pack/plugins/cases/public/components/connectors/resilient/types.ts b/x-pack/plugins/cases/public/components/connectors/resilient/types.ts deleted file mode 100644 index 06506d2c0d2f9..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/resilient/types.ts +++ /dev/null @@ -1,9 +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. - */ - -export type ResilientIncidentTypes = Array<{ id: number; name: string }>; -export type ResilientSeverity = ResilientIncidentTypes; diff --git a/x-pack/plugins/cases/public/components/connectors/resilient/use_get_incident_types.test.tsx b/x-pack/plugins/cases/public/components/connectors/resilient/use_get_incident_types.test.tsx deleted file mode 100644 index 59c1f8e9b40d0..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/resilient/use_get_incident_types.test.tsx +++ /dev/null @@ -1,71 +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 { renderHook, act } from '@testing-library/react-hooks'; - -import { useKibana } from '../../../common/lib/kibana'; -import { connector } from '../mock'; -import { useGetIncidentTypes, UseGetIncidentTypes } from './use_get_incident_types'; -import * as api from './api'; - -jest.mock('../../../common/lib/kibana'); -jest.mock('./api'); - -const useKibanaMock = useKibana as jest.Mocked; - -describe('useGetIncidentTypes', () => { - const { http, notifications } = useKibanaMock().services; - - test('init', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useGetIncidentTypes({ http, toastNotifications: notifications.toasts }) - ); - await waitForNextUpdate(); - expect(result.current).toEqual({ isLoading: true, incidentTypes: [] }); - }); - }); - - test('fetch incident types', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useGetIncidentTypes({ - http, - toastNotifications: notifications.toasts, - connector, - }) - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(result.current).toEqual({ - isLoading: false, - incidentTypes: [ - { id: 17, name: 'Communication error (fax; email)' }, - { id: 1001, name: 'Custom type' }, - ], - }); - }); - }); - - test('unhappy path', async () => { - const spyOnGetCaseConfigure = jest.spyOn(api, 'getIncidentTypes'); - spyOnGetCaseConfigure.mockImplementation(() => { - throw new Error('Something went wrong'); - }); - - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useGetIncidentTypes({ http, toastNotifications: notifications.toasts, connector }) - ); - - await waitForNextUpdate(); - await waitForNextUpdate(); - - expect(result.current).toEqual({ isLoading: false, incidentTypes: [] }); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/connectors/resilient/use_get_incident_types.tsx b/x-pack/plugins/cases/public/components/connectors/resilient/use_get_incident_types.tsx deleted file mode 100644 index 34cbb0a69b0f4..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/resilient/use_get_incident_types.tsx +++ /dev/null @@ -1,94 +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 { useState, useEffect, useRef } from 'react'; -import { HttpSetup, ToastsApi } from 'kibana/public'; -import { ActionConnector } from '../../../containers/types'; -import { getIncidentTypes } from './api'; -import * as i18n from './translations'; - -type IncidentTypes = Array<{ id: number; name: string }>; - -interface Props { - http: HttpSetup; - toastNotifications: Pick< - ToastsApi, - 'get$' | 'add' | 'remove' | 'addSuccess' | 'addWarning' | 'addDanger' | 'addError' - >; - connector?: ActionConnector; -} - -export interface UseGetIncidentTypes { - incidentTypes: IncidentTypes; - isLoading: boolean; -} - -export const useGetIncidentTypes = ({ - http, - toastNotifications, - connector, -}: Props): UseGetIncidentTypes => { - const [isLoading, setIsLoading] = useState(true); - const [incidentTypes, setIncidentTypes] = useState([]); - const didCancel = useRef(false); - const abortCtrl = useRef(new AbortController()); - - useEffect(() => { - const fetchData = async () => { - if (!connector) { - setIsLoading(false); - return; - } - - try { - abortCtrl.current = new AbortController(); - setIsLoading(true); - - const res = await getIncidentTypes({ - http, - signal: abortCtrl.current.signal, - connectorId: connector.id, - }); - - if (!didCancel.current) { - setIsLoading(false); - setIncidentTypes(res.data ?? []); - if (res.status && res.status === 'error') { - toastNotifications.addDanger({ - title: i18n.INCIDENT_TYPES_API_ERROR, - text: `${res.serviceMessage ?? res.message}`, - }); - } - } - } catch (error) { - if (!didCancel.current) { - setIsLoading(false); - if (error.name !== 'AbortError') { - toastNotifications.addDanger({ - title: i18n.INCIDENT_TYPES_API_ERROR, - text: error.message, - }); - } - } - } - }; - - didCancel.current = false; - abortCtrl.current.abort(); - fetchData(); - - return () => { - didCancel.current = true; - abortCtrl.current.abort(); - }; - }, [http, connector, toastNotifications]); - - return { - incidentTypes, - isLoading, - }; -}; diff --git a/x-pack/plugins/cases/public/components/connectors/resilient/use_get_severity.test.tsx b/x-pack/plugins/cases/public/components/connectors/resilient/use_get_severity.test.tsx deleted file mode 100644 index f646dd7e8f7c2..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/resilient/use_get_severity.test.tsx +++ /dev/null @@ -1,77 +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 { renderHook, act } from '@testing-library/react-hooks'; - -import { useKibana } from '../../../common/lib/kibana'; -import { connector } from '../mock'; -import { useGetSeverity, UseGetSeverity } from './use_get_severity'; -import * as api from './api'; - -jest.mock('../../../common/lib/kibana'); -jest.mock('./api'); - -const useKibanaMock = useKibana as jest.Mocked; - -describe('useGetSeverity', () => { - const { http, notifications } = useKibanaMock().services; - - test('init', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useGetSeverity({ http, toastNotifications: notifications.toasts }) - ); - await waitForNextUpdate(); - expect(result.current).toEqual({ isLoading: true, severity: [] }); - }); - }); - - test('fetch severity', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useGetSeverity({ http, toastNotifications: notifications.toasts, connector }) - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(result.current).toEqual({ - isLoading: false, - severity: [ - { - id: 4, - name: 'Low', - }, - { - id: 5, - name: 'Medium', - }, - { - id: 6, - name: 'High', - }, - ], - }); - }); - }); - - test('unhappy path', async () => { - const spyOnGetCaseConfigure = jest.spyOn(api, 'getSeverity'); - spyOnGetCaseConfigure.mockImplementation(() => { - throw new Error('Something went wrong'); - }); - - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useGetSeverity({ http, toastNotifications: notifications.toasts, connector }) - ); - - await waitForNextUpdate(); - await waitForNextUpdate(); - - expect(result.current).toEqual({ isLoading: false, severity: [] }); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/connectors/resilient/use_get_severity.tsx b/x-pack/plugins/cases/public/components/connectors/resilient/use_get_severity.tsx deleted file mode 100644 index 5b44c6b4a32b2..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/resilient/use_get_severity.tsx +++ /dev/null @@ -1,91 +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 { useState, useEffect, useRef } from 'react'; -import { HttpSetup, ToastsApi } from 'kibana/public'; -import { ActionConnector } from '../../../containers/types'; -import { getSeverity } from './api'; -import * as i18n from './translations'; - -type Severity = Array<{ id: number; name: string }>; - -interface Props { - http: HttpSetup; - toastNotifications: Pick< - ToastsApi, - 'get$' | 'add' | 'remove' | 'addSuccess' | 'addWarning' | 'addDanger' | 'addError' - >; - connector?: ActionConnector; -} - -export interface UseGetSeverity { - severity: Severity; - isLoading: boolean; -} - -export const useGetSeverity = ({ http, toastNotifications, connector }: Props): UseGetSeverity => { - const [isLoading, setIsLoading] = useState(true); - const [severity, setSeverity] = useState([]); - const abortCtrl = useRef(new AbortController()); - const didCancel = useRef(false); - - useEffect(() => { - const fetchData = async () => { - if (!connector) { - setIsLoading(false); - return; - } - - try { - abortCtrl.current = new AbortController(); - setIsLoading(true); - - const res = await getSeverity({ - http, - signal: abortCtrl.current.signal, - connectorId: connector.id, - }); - - if (!didCancel.current) { - setIsLoading(false); - setSeverity(res.data ?? []); - - if (res.status && res.status === 'error') { - toastNotifications.addDanger({ - title: i18n.SEVERITY_API_ERROR, - text: `${res.serviceMessage ?? res.message}`, - }); - } - } - } catch (error) { - if (!didCancel.current) { - setIsLoading(false); - if (error.name !== 'AbortError') { - toastNotifications.addDanger({ - title: i18n.SEVERITY_API_ERROR, - text: error.message, - }); - } - } - } - }; - - didCancel.current = false; - abortCtrl.current.abort(); - fetchData(); - - return () => { - didCancel.current = true; - abortCtrl.current.abort(); - }; - }, [http, connector, toastNotifications]); - - return { - severity, - isLoading, - }; -}; diff --git a/x-pack/plugins/cases/public/components/connectors/servicenow/__mocks__/api.ts b/x-pack/plugins/cases/public/components/connectors/servicenow/__mocks__/api.ts deleted file mode 100644 index 215e3d6f92e6d..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/servicenow/__mocks__/api.ts +++ /dev/null @@ -1,19 +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 { choices } from '../../mock'; -import { GetChoicesProps } from '../api'; -import { Choice } from '../types'; - -export const choicesResponse = { - status: 'ok', - data: choices, -}; - -export const getChoices = async ( - props: GetChoicesProps -): Promise<{ status: string; data: Choice[] }> => Promise.resolve(choicesResponse); diff --git a/x-pack/plugins/cases/public/components/connectors/servicenow/api.test.ts b/x-pack/plugins/cases/public/components/connectors/servicenow/api.test.ts deleted file mode 100644 index 461823036ed21..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/servicenow/api.test.ts +++ /dev/null @@ -1,40 +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 { httpServiceMock } from '../../../../../../../src/core/public/mocks'; -import { getChoices } from './api'; -import { choices } from '../mock'; - -const choicesResponse = { - status: 'ok', - data: choices, -}; - -describe('ServiceNow API', () => { - const http = httpServiceMock.createStartContract(); - - beforeEach(() => jest.resetAllMocks()); - - describe('getChoices', () => { - test('should call get choices API', async () => { - const abortCtrl = new AbortController(); - http.post.mockResolvedValueOnce(choicesResponse); - const res = await getChoices({ - http, - signal: abortCtrl.signal, - connectorId: 'test', - fields: ['priority'], - }); - - expect(res).toEqual(choicesResponse); - expect(http.post).toHaveBeenCalledWith('/api/actions/action/test/_execute', { - body: '{"params":{"subAction":"getChoices","subActionParams":{"fields":["priority"]}}}', - signal: abortCtrl.signal, - }); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/connectors/servicenow/api.ts b/x-pack/plugins/cases/public/components/connectors/servicenow/api.ts deleted file mode 100644 index e68eb18860ae3..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/servicenow/api.ts +++ /dev/null @@ -1,31 +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 { HttpSetup } from 'kibana/public'; -import { ActionTypeExecutorResult } from '../../../../../actions/common'; -import { Choice } from './types'; - -export const BASE_ACTION_API_PATH = '/api/actions'; - -export interface GetChoicesProps { - http: HttpSetup; - signal: AbortSignal; - connectorId: string; - fields: string[]; -} - -export async function getChoices({ http, signal, connectorId, fields }: GetChoicesProps) { - return http.post>( - `${BASE_ACTION_API_PATH}/action/${connectorId}/_execute`, - { - body: JSON.stringify({ - params: { subAction: 'getChoices', subActionParams: { fields } }, - }), - signal, - } - ); -} diff --git a/x-pack/plugins/cases/public/components/connectors/servicenow/helpers.ts b/x-pack/plugins/cases/public/components/connectors/servicenow/helpers.ts deleted file mode 100644 index 314d224491128..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/servicenow/helpers.ts +++ /dev/null @@ -1,12 +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 { EuiSelectOption } from '@elastic/eui'; -import { Choice } from './types'; - -export const choicesToEuiOptions = (choices: Choice[]): EuiSelectOption[] => - choices.map((choice) => ({ value: choice.value, text: choice.label })); diff --git a/x-pack/plugins/cases/public/components/connectors/servicenow/index.ts b/x-pack/plugins/cases/public/components/connectors/servicenow/index.ts deleted file mode 100644 index a6f0795fe4d8f..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/servicenow/index.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { lazy } from 'react'; - -import { CaseConnector } from '../types'; -import { ServiceNowITSMFieldsType, ServiceNowSIRFieldsType } from '../../../../common'; -import * as i18n from './translations'; - -export const getServiceNowITSMCaseConnector = (): CaseConnector => { - return { - id: '.servicenow', - fieldsComponent: lazy(() => import('./servicenow_itsm_case_fields')), - }; -}; - -export const getServiceNowSIRCaseConnector = (): CaseConnector => { - return { - id: '.servicenow-sir', - fieldsComponent: lazy(() => import('./servicenow_sir_case_fields')), - }; -}; - -export const serviceNowITSMFieldLabels = { - impact: i18n.IMPACT, - severity: i18n.SEVERITY, - urgency: i18n.URGENCY, -}; diff --git a/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_itsm_case_fields.test.tsx b/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_itsm_case_fields.test.tsx deleted file mode 100644 index 9688ca191d672..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_itsm_case_fields.test.tsx +++ /dev/null @@ -1,164 +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 from 'react'; -import { waitFor, act } from '@testing-library/react'; -import { EuiSelect } from '@elastic/eui'; -import { mount } from 'enzyme'; - -import { connector, choices as mockChoices } from '../mock'; -import { Choice } from './types'; -import Fields from './servicenow_itsm_case_fields'; - -let onChoicesSuccess = (c: Choice[]) => {}; - -jest.mock('../../../common/lib/kibana'); -jest.mock('./use_get_choices', () => ({ - useGetChoices: (args: { onSuccess: () => void }) => { - onChoicesSuccess = args.onSuccess; - return { isLoading: false, choices: mockChoices }; - }, -})); - -describe('ServiceNowITSM Fields', () => { - const fields = { - severity: '1', - urgency: '2', - impact: '3', - category: 'software', - subcategory: 'os', - }; - const onChange = jest.fn(); - - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('all params fields are rendered - isEdit: true', () => { - const wrapper = mount(); - expect(wrapper.find('[data-test-subj="severitySelect"]').exists()).toBeTruthy(); - expect(wrapper.find('[data-test-subj="urgencySelect"]').exists()).toBeTruthy(); - expect(wrapper.find('[data-test-subj="impactSelect"]').exists()).toBeTruthy(); - expect(wrapper.find('[data-test-subj="categorySelect"]').exists()).toBeTruthy(); - expect(wrapper.find('[data-test-subj="subcategorySelect"]').exists()).toBeTruthy(); - }); - - it('all params fields are rendered - isEdit: false', () => { - const wrapper = mount( - - ); - act(() => { - onChoicesSuccess(mockChoices); - }); - - expect(wrapper.find('[data-test-subj="card-list-item"]').at(0).text()).toEqual( - 'Urgency: 2 - High' - ); - expect(wrapper.find('[data-test-subj="card-list-item"]').at(1).text()).toEqual( - 'Severity: 1 - Critical' - ); - expect(wrapper.find('[data-test-subj="card-list-item"]').at(2).text()).toEqual( - 'Impact: 3 - Moderate' - ); - }); - - test('it transforms the categories to options correctly', async () => { - const wrapper = mount(); - act(() => { - onChoicesSuccess(mockChoices); - }); - - wrapper.update(); - expect(wrapper.find('[data-test-subj="categorySelect"]').first().prop('options')).toEqual([ - { value: 'Priviledge Escalation', text: 'Priviledge Escalation' }, - { - value: 'Criminal activity/investigation', - text: 'Criminal activity/investigation', - }, - { value: 'Denial of Service', text: 'Denial of Service' }, - { - value: 'software', - text: 'Software', - }, - ]); - }); - - test('it transforms the subcategories to options correctly', async () => { - const wrapper = mount(); - act(() => { - onChoicesSuccess(mockChoices); - }); - - wrapper.update(); - expect(wrapper.find('[data-test-subj="subcategorySelect"]').first().prop('options')).toEqual([ - { - text: 'Operation System', - value: 'os', - }, - ]); - }); - - it('it transforms the options correctly', async () => { - const wrapper = mount(); - act(() => { - onChoicesSuccess(mockChoices); - }); - - wrapper.update(); - const testers = ['severity', 'urgency', 'impact']; - testers.forEach((subj) => - expect(wrapper.find(`[data-test-subj="${subj}Select"]`).first().prop('options')).toEqual([ - { value: '1', text: '1 - Critical' }, - { value: '2', text: '2 - High' }, - { value: '3', text: '3 - Moderate' }, - { value: '4', text: '4 - Low' }, - ]) - ); - }); - - describe('onChange calls', () => { - const wrapper = mount(); - - expect(onChange).toHaveBeenCalledWith(fields); - - const testers = ['severity', 'urgency', 'impact', 'subcategory']; - testers.forEach((subj) => - test(`${subj.toUpperCase()}`, async () => { - await waitFor(() => { - const select = wrapper.find(EuiSelect).filter(`[data-test-subj="${subj}Select"]`)!; - select.prop('onChange')!({ - target: { - value: '9', - }, - } as React.ChangeEvent); - }); - wrapper.update(); - expect(onChange).toHaveBeenCalledWith({ - ...fields, - [subj]: '9', - }); - }) - ); - - test('it should set subcategory to null when changing category', async () => { - await waitFor(() => { - const select = wrapper.find(EuiSelect).filter(`[data-test-subj="categorySelect"]`)!; - select.prop('onChange')!({ - target: { - value: 'network', - }, - } as React.ChangeEvent); - }); - wrapper.update(); - expect(onChange).toHaveBeenCalledWith({ - ...fields, - subcategory: null, - category: 'network', - }); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_itsm_case_fields.tsx b/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_itsm_case_fields.tsx deleted file mode 100644 index 710e230958354..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_itsm_case_fields.tsx +++ /dev/null @@ -1,235 +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, useEffect, useMemo, useState, useRef } from 'react'; -import { EuiFormRow, EuiSelect, EuiSpacer, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import * as i18n from './translations'; - -import { ConnectorFieldsProps } from '../types'; -import { ConnectorTypes, ServiceNowITSMFieldsType } from '../../../../common'; -import { useKibana } from '../../../common/lib/kibana'; -import { ConnectorCard } from '../card'; -import { useGetChoices } from './use_get_choices'; -import { Fields, Choice } from './types'; -import { choicesToEuiOptions } from './helpers'; - -const useGetChoicesFields = ['urgency', 'severity', 'impact', 'category', 'subcategory']; -const defaultFields: Fields = { - urgency: [], - severity: [], - impact: [], - category: [], - subcategory: [], -}; - -const ServiceNowITSMFieldsComponent: React.FunctionComponent< - ConnectorFieldsProps -> = ({ isEdit = true, fields, connector, onChange }) => { - const init = useRef(true); - const { severity = null, urgency = null, impact = null, category = null, subcategory = null } = - fields ?? {}; - const { http, notifications } = useKibana().services; - const [choices, setChoices] = useState(defaultFields); - - const categoryOptions = useMemo(() => choicesToEuiOptions(choices.category), [choices.category]); - const urgencyOptions = useMemo(() => choicesToEuiOptions(choices.urgency), [choices.urgency]); - const severityOptions = useMemo(() => choicesToEuiOptions(choices.severity), [choices.severity]); - const impactOptions = useMemo(() => choicesToEuiOptions(choices.impact), [choices.impact]); - - const subcategoryOptions = useMemo( - () => - choicesToEuiOptions( - choices.subcategory.filter((choice) => choice.dependent_value === category) - ), - [choices.subcategory, category] - ); - - const listItems = useMemo( - () => [ - ...(urgency != null && urgency.length > 0 - ? [ - { - title: i18n.URGENCY, - description: urgencyOptions.find((option) => `${option.value}` === urgency)?.text, - }, - ] - : []), - ...(severity != null && severity.length > 0 - ? [ - { - title: i18n.SEVERITY, - description: severityOptions.find((option) => `${option.value}` === severity)?.text, - }, - ] - : []), - ...(impact != null && impact.length > 0 - ? [ - { - title: i18n.IMPACT, - description: impactOptions.find((option) => `${option.value}` === impact)?.text, - }, - ] - : []), - ...(category != null && category.length > 0 - ? [ - { - title: i18n.CATEGORY, - description: categoryOptions.find((option) => `${option.value}` === category)?.text, - }, - ] - : []), - ...(subcategory != null && subcategory.length > 0 - ? [ - { - title: i18n.SUBCATEGORY, - description: subcategoryOptions.find((option) => `${option.value}` === subcategory) - ?.text, - }, - ] - : []), - ], - [ - category, - categoryOptions, - impact, - impactOptions, - severity, - severityOptions, - subcategory, - subcategoryOptions, - urgency, - urgencyOptions, - ] - ); - - const onChoicesSuccess = (values: Choice[]) => { - setChoices( - values.reduce( - (acc, value) => ({ - ...acc, - [value.element]: [...(acc[value.element] != null ? acc[value.element] : []), value], - }), - defaultFields - ) - ); - }; - - const { isLoading: isLoadingChoices } = useGetChoices({ - http, - toastNotifications: notifications.toasts, - connector, - fields: useGetChoicesFields, - onSuccess: onChoicesSuccess, - }); - - const onChangeCb = useCallback( - ( - key: keyof ServiceNowITSMFieldsType, - value: ServiceNowITSMFieldsType[keyof ServiceNowITSMFieldsType] - ) => { - onChange({ ...fields, [key]: value }); - }, - [fields, onChange] - ); - - // Set field at initialization - useEffect(() => { - if (init.current) { - init.current = false; - onChange({ urgency, severity, impact, category, subcategory }); - } - }, [category, impact, onChange, severity, subcategory, urgency]); - - return isEdit ? ( -
- - onChangeCb('urgency', e.target.value)} - /> - - - - - - onChangeCb('severity', e.target.value)} - /> - - - - - onChangeCb('impact', e.target.value)} - /> - - - - - - - onChange({ ...fields, category: e.target.value, subcategory: null })} - /> - - - - - onChangeCb('subcategory', e.target.value)} - /> - - - -
- ) : ( - - ); -}; - -// eslint-disable-next-line import/no-default-export -export { ServiceNowITSMFieldsComponent as default }; diff --git a/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_sir_case_fields.test.tsx b/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_sir_case_fields.test.tsx deleted file mode 100644 index 4a5b34cd3c3cb..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_sir_case_fields.test.tsx +++ /dev/null @@ -1,221 +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 from 'react'; -import { mount } from 'enzyme'; -import { waitFor, act } from '@testing-library/react'; -import { EuiSelect } from '@elastic/eui'; - -import { connector, choices as mockChoices } from '../mock'; -import { Choice } from './types'; -import Fields from './servicenow_sir_case_fields'; - -let onChoicesSuccess = (c: Choice[]) => {}; - -jest.mock('../../../common/lib/kibana'); -jest.mock('./use_get_choices', () => ({ - useGetChoices: (args: { onSuccess: () => void }) => { - onChoicesSuccess = args.onSuccess; - return { isLoading: false, mockChoices }; - }, -})); - -describe('ServiceNowSIR Fields', () => { - const fields = { - destIp: true, - sourceIp: true, - malwareHash: true, - malwareUrl: true, - priority: '1', - category: 'Denial of Service', - subcategory: '26', - }; - const onChange = jest.fn(); - - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('all params fields are rendered - isEdit: true', () => { - const wrapper = mount(); - expect(wrapper.find('[data-test-subj="destIpCheckbox"]').exists()).toBeTruthy(); - expect(wrapper.find('[data-test-subj="sourceIpCheckbox"]').exists()).toBeTruthy(); - expect(wrapper.find('[data-test-subj="malwareUrlCheckbox"]').exists()).toBeTruthy(); - expect(wrapper.find('[data-test-subj="malwareHashCheckbox"]').exists()).toBeTruthy(); - expect(wrapper.find('[data-test-subj="prioritySelect"]').exists()).toBeTruthy(); - expect(wrapper.find('[data-test-subj="categorySelect"]').exists()).toBeTruthy(); - expect(wrapper.find('[data-test-subj="subcategorySelect"]').exists()).toBeTruthy(); - }); - - test('all params fields are rendered - isEdit: false', () => { - const wrapper = mount( - - ); - act(() => { - onChoicesSuccess(mockChoices); - }); - wrapper.update(); - - expect(wrapper.find('[data-test-subj="card-list-item"]').at(0).text()).toEqual( - 'Destination IP: Yes' - ); - expect(wrapper.find('[data-test-subj="card-list-item"]').at(1).text()).toEqual( - 'Source IP: Yes' - ); - expect(wrapper.find('[data-test-subj="card-list-item"]').at(2).text()).toEqual( - 'Malware URL: Yes' - ); - expect(wrapper.find('[data-test-subj="card-list-item"]').at(3).text()).toEqual( - 'Malware Hash: Yes' - ); - expect(wrapper.find('[data-test-subj="card-list-item"]').at(4).text()).toEqual( - 'Priority: 1 - Critical' - ); - expect(wrapper.find('[data-test-subj="card-list-item"]').at(5).text()).toEqual( - 'Category: Denial of Service' - ); - expect(wrapper.find('[data-test-subj="card-list-item"]').at(6).text()).toEqual( - 'Subcategory: Single or distributed (DoS or DDoS)' - ); - }); - - test('it transforms the categories to options correctly', async () => { - const wrapper = mount(); - act(() => { - onChoicesSuccess(mockChoices); - }); - - wrapper.update(); - expect(wrapper.find('[data-test-subj="categorySelect"]').first().prop('options')).toEqual([ - { value: 'Priviledge Escalation', text: 'Priviledge Escalation' }, - { - value: 'Criminal activity/investigation', - text: 'Criminal activity/investigation', - }, - { value: 'Denial of Service', text: 'Denial of Service' }, - { - text: 'Software', - value: 'software', - }, - ]); - }); - - test('it transforms the subcategories to options correctly', async () => { - const wrapper = mount(); - act(() => { - onChoicesSuccess(mockChoices); - }); - - wrapper.update(); - expect(wrapper.find('[data-test-subj="subcategorySelect"]').first().prop('options')).toEqual([ - { - text: 'Inbound or outbound', - value: '12', - }, - { - text: 'Single or distributed (DoS or DDoS)', - value: '26', - }, - { - text: 'Inbound DDos', - value: 'inbound_ddos', - }, - ]); - }); - - test('it transforms the priorities to options correctly', async () => { - const wrapper = mount(); - act(() => { - onChoicesSuccess(mockChoices); - }); - - wrapper.update(); - expect(wrapper.find('[data-test-subj="prioritySelect"]').first().prop('options')).toEqual([ - { - text: '1 - Critical', - value: '1', - }, - { - text: '2 - High', - value: '2', - }, - { - text: '3 - Moderate', - value: '3', - }, - { - text: '4 - Low', - value: '4', - }, - ]); - }); - - describe('onChange calls', () => { - const wrapper = mount(); - - act(() => { - onChoicesSuccess(mockChoices); - }); - wrapper.update(); - - expect(onChange).toHaveBeenCalledWith(fields); - - const checkbox = ['destIp', 'sourceIp', 'malwareHash', 'malwareUrl']; - checkbox.forEach((subj) => - test(`${subj.toUpperCase()}`, async () => { - await waitFor(() => { - wrapper - .find(`[data-test-subj="${subj}Checkbox"] input`) - .first() - .simulate('change', { target: { checked: false } }); - expect(onChange).toHaveBeenCalledWith({ - ...fields, - [subj]: false, - }); - }); - }) - ); - - const testers = ['priority', 'subcategory']; - testers.forEach((subj) => - test(`${subj.toUpperCase()}`, async () => { - await waitFor(() => { - const select = wrapper.find(EuiSelect).filter(`[data-test-subj="${subj}Select"]`)!; - select.prop('onChange')!({ - target: { - value: '9', - }, - } as React.ChangeEvent); - }); - wrapper.update(); - expect(onChange).toHaveBeenCalledWith({ - ...fields, - [subj]: '9', - }); - }) - ); - - test('it should set subcategory to null when changing category', async () => { - const select = wrapper.find(EuiSelect).filter(`[data-test-subj="categorySelect"]`)!; - select.prop('onChange')!({ - target: { - value: 'network', - }, - } as React.ChangeEvent); - - wrapper.update(); - - await waitFor(() => { - expect(onChange).toHaveBeenCalledWith({ - ...fields, - subcategory: null, - category: 'network', - }); - }); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_sir_case_fields.tsx b/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_sir_case_fields.tsx deleted file mode 100644 index 1f9a7cf7acd64..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/servicenow/servicenow_sir_case_fields.tsx +++ /dev/null @@ -1,282 +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, useEffect, useMemo, useState, useRef } from 'react'; -import { EuiFormRow, EuiSelect, EuiFlexGroup, EuiFlexItem, EuiCheckbox } from '@elastic/eui'; - -import { ConnectorTypes, ServiceNowSIRFieldsType } from '../../../../common'; -import { useKibana } from '../../../common/lib/kibana'; -import { ConnectorFieldsProps } from '../types'; -import { ConnectorCard } from '../card'; -import { useGetChoices } from './use_get_choices'; -import { Choice, Fields } from './types'; -import { choicesToEuiOptions } from './helpers'; - -import * as i18n from './translations'; - -const useGetChoicesFields = ['category', 'subcategory', 'priority']; -const defaultFields: Fields = { - category: [], - subcategory: [], - priority: [], -}; - -const ServiceNowSIRFieldsComponent: React.FunctionComponent< - ConnectorFieldsProps -> = ({ isEdit = true, fields, connector, onChange }) => { - const init = useRef(true); - const { - category = null, - destIp = true, - malwareHash = true, - malwareUrl = true, - priority = null, - sourceIp = true, - subcategory = null, - } = fields ?? {}; - - const { http, notifications } = useKibana().services; - - const [choices, setChoices] = useState(defaultFields); - - const onChangeCb = useCallback( - ( - key: keyof ServiceNowSIRFieldsType, - value: ServiceNowSIRFieldsType[keyof ServiceNowSIRFieldsType] - ) => { - onChange({ ...fields, [key]: value }); - }, - [fields, onChange] - ); - - const onChoicesSuccess = (values: Choice[]) => { - setChoices( - values.reduce( - (acc, value) => ({ - ...acc, - [value.element]: [...(acc[value.element] != null ? acc[value.element] : []), value], - }), - defaultFields - ) - ); - }; - - const { isLoading: isLoadingChoices } = useGetChoices({ - http, - toastNotifications: notifications.toasts, - connector, - fields: useGetChoicesFields, - onSuccess: onChoicesSuccess, - }); - - const categoryOptions = useMemo(() => choicesToEuiOptions(choices.category), [choices.category]); - const priorityOptions = useMemo(() => choicesToEuiOptions(choices.priority), [choices.priority]); - - const subcategoryOptions = useMemo( - () => - choicesToEuiOptions( - choices.subcategory.filter((choice) => choice.dependent_value === category) - ), - [choices.subcategory, category] - ); - - const listItems = useMemo( - () => [ - ...(destIp != null && destIp - ? [ - { - title: i18n.DEST_IP, - description: i18n.ALERT_FIELD_ENABLED_TEXT, - }, - ] - : []), - ...(sourceIp != null && sourceIp - ? [ - { - title: i18n.SOURCE_IP, - description: i18n.ALERT_FIELD_ENABLED_TEXT, - }, - ] - : []), - ...(malwareUrl != null && malwareUrl - ? [ - { - title: i18n.MALWARE_URL, - description: i18n.ALERT_FIELD_ENABLED_TEXT, - }, - ] - : []), - ...(malwareHash != null && malwareHash - ? [ - { - title: i18n.MALWARE_HASH, - description: i18n.ALERT_FIELD_ENABLED_TEXT, - }, - ] - : []), - ...(priority != null && priority.length > 0 - ? [ - { - title: i18n.PRIORITY, - description: priorityOptions.find((option) => `${option.value}` === priority)?.text, - }, - ] - : []), - ...(category != null && category.length > 0 - ? [ - { - title: i18n.CATEGORY, - description: categoryOptions.find((option) => `${option.value}` === category)?.text, - }, - ] - : []), - ...(subcategory != null && subcategory.length > 0 - ? [ - { - title: i18n.SUBCATEGORY, - description: subcategoryOptions.find((option) => `${option.value}` === subcategory) - ?.text, - }, - ] - : []), - ], - [ - category, - categoryOptions, - destIp, - malwareHash, - malwareUrl, - priority, - priorityOptions, - sourceIp, - subcategory, - subcategoryOptions, - ] - ); - - // Set field at initialization - useEffect(() => { - if (init.current) { - init.current = false; - onChange({ category, destIp, malwareHash, malwareUrl, priority, sourceIp, subcategory }); - } - }, [category, destIp, malwareHash, malwareUrl, onChange, priority, sourceIp, subcategory]); - - return isEdit ? ( -
- - - - <> - - - onChangeCb('destIp', e.target.checked)} - /> - - - onChangeCb('sourceIp', e.target.checked)} - /> - - - - - onChangeCb('malwareUrl', e.target.checked)} - /> - - - onChangeCb('malwareHash', e.target.checked)} - /> - - - - - - - - - - onChangeCb('priority', e.target.value)} - /> - - - - - - - onChange({ ...fields, category: e.target.value, subcategory: null })} - /> - - - - - onChangeCb('subcategory', e.target.value)} - /> - - - -
- ) : ( - - ); -}; - -// eslint-disable-next-line import/no-default-export -export { ServiceNowSIRFieldsComponent as default }; diff --git a/x-pack/plugins/cases/public/components/connectors/servicenow/translations.ts b/x-pack/plugins/cases/public/components/connectors/servicenow/translations.ts deleted file mode 100644 index fc48ecf17f2c6..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/servicenow/translations.ts +++ /dev/null @@ -1,75 +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 { i18n } from '@kbn/i18n'; - -export const URGENCY = i18n.translate('xpack.cases.connectors.serviceNow.urgencySelectFieldLabel', { - defaultMessage: 'Urgency', -}); - -export const SEVERITY = i18n.translate( - 'xpack.cases.connectors.serviceNow.severitySelectFieldLabel', - { - defaultMessage: 'Severity', - } -); - -export const IMPACT = i18n.translate('xpack.cases.connectors.serviceNow.impactSelectFieldLabel', { - defaultMessage: 'Impact', -}); - -export const CHOICES_API_ERROR = i18n.translate( - 'xpack.cases.connectors.serviceNow.unableToGetChoicesMessage', - { - defaultMessage: 'Unable to get choices', - } -); - -export const MALWARE_URL = i18n.translate('xpack.cases.connectors.serviceNow.malwareURLTitle', { - defaultMessage: 'Malware URL', -}); - -export const MALWARE_HASH = i18n.translate('xpack.cases.connectors.serviceNow.malwareHashTitle', { - defaultMessage: 'Malware Hash', -}); - -export const CATEGORY = i18n.translate('xpack.cases.connectors.serviceNow.categoryTitle', { - defaultMessage: 'Category', -}); - -export const SUBCATEGORY = i18n.translate('xpack.cases.connectors.serviceNow.subcategoryTitle', { - defaultMessage: 'Subcategory', -}); - -export const SOURCE_IP = i18n.translate('xpack.cases.connectors.serviceNow.sourceIPTitle', { - defaultMessage: 'Source IP', -}); - -export const DEST_IP = i18n.translate('xpack.cases.connectors.serviceNow.destinationIPTitle', { - defaultMessage: 'Destination IP', -}); - -export const PRIORITY = i18n.translate( - 'xpack.cases.connectors.serviceNow.prioritySelectFieldTitle', - { - defaultMessage: 'Priority', - } -); - -export const ALERT_FIELDS_LABEL = i18n.translate( - 'xpack.cases.connectors.serviceNow.alertFieldsTitle', - { - defaultMessage: 'Select Observables to push', - } -); - -export const ALERT_FIELD_ENABLED_TEXT = i18n.translate( - 'xpack.cases.connectors.serviceNow.alertFieldEnabledText', - { - defaultMessage: 'Yes', - } -); diff --git a/x-pack/plugins/cases/public/components/connectors/servicenow/types.ts b/x-pack/plugins/cases/public/components/connectors/servicenow/types.ts deleted file mode 100644 index fd1af62f7bb2a..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/servicenow/types.ts +++ /dev/null @@ -1,15 +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. - */ - -export interface Choice { - value: string; - label: string; - dependent_value: string; - element: string; -} - -export type Fields = Record; diff --git a/x-pack/plugins/cases/public/components/connectors/servicenow/use_get_choices.test.tsx b/x-pack/plugins/cases/public/components/connectors/servicenow/use_get_choices.test.tsx deleted file mode 100644 index ed4577dd0114b..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/servicenow/use_get_choices.test.tsx +++ /dev/null @@ -1,144 +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 { renderHook } from '@testing-library/react-hooks'; - -import { useKibana } from '../../../common/lib/kibana'; -import { ActionConnector } from '../../../containers/types'; -import { choices } from '../mock'; -import { useGetChoices, UseGetChoices, UseGetChoicesProps } from './use_get_choices'; -import * as api from './api'; - -jest.mock('./api'); -jest.mock('../../../common/lib/kibana'); - -const useKibanaMock = useKibana as jest.Mocked; -const onSuccess = jest.fn(); -const fields = ['priority']; - -const connector = { - secrets: { - username: 'user', - password: 'pass', - }, - id: 'test', - actionTypeId: '.servicenow', - name: 'ServiceNow', - isPreconfigured: false, - config: { - apiUrl: 'https://dev94428.service-now.com/', - }, -} as ActionConnector; - -describe('useGetChoices', () => { - const { services } = useKibanaMock(); - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('init', async () => { - const { result, waitForNextUpdate } = renderHook(() => - useGetChoices({ - http: services.http, - connector, - toastNotifications: services.notifications.toasts, - fields, - onSuccess, - }) - ); - - await waitForNextUpdate(); - - expect(result.current).toEqual({ - isLoading: false, - choices, - }); - }); - - it('returns an empty array when connector is not presented', async () => { - const { result } = renderHook(() => - useGetChoices({ - http: services.http, - connector: undefined, - toastNotifications: services.notifications.toasts, - fields, - onSuccess, - }) - ); - - expect(result.current).toEqual({ - isLoading: false, - choices: [], - }); - }); - - it('it calls onSuccess', async () => { - const { waitForNextUpdate } = renderHook(() => - useGetChoices({ - http: services.http, - connector, - toastNotifications: services.notifications.toasts, - fields, - onSuccess, - }) - ); - - await waitForNextUpdate(); - - expect(onSuccess).toHaveBeenCalledWith(choices); - }); - - it('it displays an error when service fails', async () => { - const spyOnGetChoices = jest.spyOn(api, 'getChoices'); - spyOnGetChoices.mockResolvedValue( - Promise.resolve({ - actionId: 'test', - status: 'error', - serviceMessage: 'An error occurred', - }) - ); - - const { waitForNextUpdate } = renderHook(() => - useGetChoices({ - http: services.http, - connector, - toastNotifications: services.notifications.toasts, - fields, - onSuccess, - }) - ); - - await waitForNextUpdate(); - - expect(services.notifications.toasts.addDanger).toHaveBeenCalledWith({ - text: 'An error occurred', - title: 'Unable to get choices', - }); - }); - - it('it displays an error when http throws an error', async () => { - const spyOnGetChoices = jest.spyOn(api, 'getChoices'); - spyOnGetChoices.mockImplementation(() => { - throw new Error('An error occurred'); - }); - - renderHook(() => - useGetChoices({ - http: services.http, - connector, - toastNotifications: services.notifications.toasts, - fields, - onSuccess, - }) - ); - - expect(services.notifications.toasts.addDanger).toHaveBeenCalledWith({ - text: 'An error occurred', - title: 'Unable to get choices', - }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/connectors/servicenow/use_get_choices.tsx b/x-pack/plugins/cases/public/components/connectors/servicenow/use_get_choices.tsx deleted file mode 100644 index a979f96d84ab2..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/servicenow/use_get_choices.tsx +++ /dev/null @@ -1,101 +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 { useState, useEffect, useRef } from 'react'; -import { HttpSetup, ToastsApi } from 'kibana/public'; -import { ActionConnector } from '../../../containers/types'; -import { getChoices } from './api'; -import { Choice } from './types'; -import * as i18n from './translations'; - -export interface UseGetChoicesProps { - http: HttpSetup; - toastNotifications: Pick< - ToastsApi, - 'get$' | 'add' | 'remove' | 'addSuccess' | 'addWarning' | 'addDanger' | 'addError' - >; - connector?: ActionConnector; - fields: string[]; - onSuccess?: (choices: Choice[]) => void; -} - -export interface UseGetChoices { - choices: Choice[]; - isLoading: boolean; -} - -export const useGetChoices = ({ - http, - connector, - toastNotifications, - fields, - onSuccess, -}: UseGetChoicesProps): UseGetChoices => { - const [isLoading, setIsLoading] = useState(false); - const [choices, setChoices] = useState([]); - const didCancel = useRef(false); - const abortCtrl = useRef(new AbortController()); - - useEffect(() => { - const fetchData = async () => { - if (!connector) { - setIsLoading(false); - return; - } - - try { - abortCtrl.current = new AbortController(); - setIsLoading(true); - - const res = await getChoices({ - http, - signal: abortCtrl.current.signal, - connectorId: connector.id, - fields, - }); - - if (!didCancel.current) { - setIsLoading(false); - setChoices(res.data ?? []); - if (res.status && res.status === 'error') { - toastNotifications.addDanger({ - title: i18n.CHOICES_API_ERROR, - text: `${res.serviceMessage ?? res.message}`, - }); - } else if (onSuccess) { - onSuccess(res.data ?? []); - } - } - } catch (error) { - if (!didCancel.current) { - setIsLoading(false); - if (error.name !== 'AbortError') { - toastNotifications.addDanger({ - title: i18n.CHOICES_API_ERROR, - text: error.message, - }); - } - } - } - }; - - didCancel.current = false; - abortCtrl.current.abort(); - fetchData(); - - return () => { - didCancel.current = true; - abortCtrl.current.abort(); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [http, connector, toastNotifications, fields]); - - return { - choices, - isLoading, - }; -}; diff --git a/x-pack/plugins/cases/public/components/connectors/types.ts b/x-pack/plugins/cases/public/components/connectors/types.ts deleted file mode 100644 index fc2f66d331700..0000000000000 --- a/x-pack/plugins/cases/public/components/connectors/types.ts +++ /dev/null @@ -1,53 +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 from 'react'; - -import { - ActionType as ThirdPartySupportedActions, - CaseField, - ActionConnector, - ConnectorTypeFields, -} from '../../../common'; - -export { ThirdPartyField as AllThirdPartyFields } from '../../../common'; -export type CaseActionConnector = ActionConnector; - -export interface ThirdPartyField { - label: string; - validSourceFields: CaseField[]; - defaultSourceField: CaseField; - defaultActionType: ThirdPartySupportedActions; -} - -export interface ConnectorConfiguration { - name: string; - logo: string; -} - -export interface CaseConnector { - id: string; - fieldsComponent: React.LazyExoticComponent< - React.ComponentType> - > | null; -} - -export interface CaseConnectorsRegistry { - has: (id: string) => boolean; - register: ( - connector: CaseConnector - ) => void; - get: (id: string) => CaseConnector; - list: () => CaseConnector[]; -} - -export interface ConnectorFieldsProps { - isEdit?: boolean; - connector: CaseActionConnector; - fields: TFields; - onChange: (fields: TFields) => void; -} diff --git a/x-pack/plugins/cases/public/components/create/connector.test.tsx b/x-pack/plugins/cases/public/components/create/connector.test.tsx deleted file mode 100644 index db9e5ffac1533..0000000000000 --- a/x-pack/plugins/cases/public/components/create/connector.test.tsx +++ /dev/null @@ -1,187 +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 from 'react'; -import { mount } from 'enzyme'; -import { act, waitFor } from '@testing-library/react'; -import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; - -import { useForm, Form, FormHook } from '../../common/shared_imports'; -import { connectorsMock } from '../../containers/mock'; -import { Connector } from './connector'; -import { useConnectors } from '../../containers/configure/use_connectors'; -import { useGetIncidentTypes } from '../connectors/resilient/use_get_incident_types'; -import { useGetSeverity } from '../connectors/resilient/use_get_severity'; -import { useGetChoices } from '../connectors/servicenow/use_get_choices'; -import { incidentTypes, severity, choices } from '../connectors/mock'; -import { schema, FormProps } from './schema'; - -jest.mock('../../common/lib/kibana', () => { - return { - useKibana: () => ({ - services: { - notifications: {}, - http: {}, - }, - }), - }; -}); -jest.mock('../../containers/configure/use_connectors'); -jest.mock('../connectors/resilient/use_get_incident_types'); -jest.mock('../connectors/resilient/use_get_severity'); -jest.mock('../connectors/servicenow/use_get_choices'); - -const useConnectorsMock = useConnectors as jest.Mock; -const useGetIncidentTypesMock = useGetIncidentTypes as jest.Mock; -const useGetSeverityMock = useGetSeverity as jest.Mock; -const useGetChoicesMock = useGetChoices as jest.Mock; - -const useGetIncidentTypesResponse = { - isLoading: false, - incidentTypes, -}; - -const useGetSeverityResponse = { - isLoading: false, - severity, -}; - -const useGetChoicesResponse = { - isLoading: false, - choices, -}; - -describe('Connector', () => { - let globalForm: FormHook; - - const MockHookWrapperComponent: React.FC = ({ children }) => { - const { form } = useForm({ - defaultValue: { connectorId: connectorsMock[0].id, fields: null }, - schema: { - connectorId: schema.connectorId, - fields: schema.fields, - }, - }); - - globalForm = form; - - return
{children}
; - }; - - beforeEach(() => { - jest.resetAllMocks(); - useConnectorsMock.mockReturnValue({ loading: false, connectors: connectorsMock }); - useGetIncidentTypesMock.mockReturnValue(useGetIncidentTypesResponse); - useGetSeverityMock.mockReturnValue(useGetSeverityResponse); - useGetChoicesMock.mockReturnValue(useGetChoicesResponse); - }); - - it('it renders', async () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find(`[data-test-subj="caseConnectors"]`).exists()).toBeTruthy(); - expect(wrapper.find(`[data-test-subj="connector-fields"]`).exists()).toBeTruthy(); - - await waitFor(() => { - expect(wrapper.find(`button[data-test-subj="dropdown-connectors"]`).first().text()).toBe( - 'My Connector' - ); - }); - - await waitFor(() => { - wrapper.update(); - expect(wrapper.find(`[data-test-subj="connector-fields-sn-itsm"]`).exists()).toBeTruthy(); - }); - }); - - it('it is loading when fetching connectors', async () => { - useConnectorsMock.mockReturnValue({ loading: true, connectors: connectorsMock }); - const wrapper = mount( - - - - ); - - expect( - wrapper.find('[data-test-subj="dropdown-connectors"]').first().prop('isLoading') - ).toEqual(true); - }); - - it('it is disabled when fetching connectors', async () => { - useConnectorsMock.mockReturnValue({ loading: true, connectors: connectorsMock }); - const wrapper = mount( - - - - ); - - expect(wrapper.find('[data-test-subj="dropdown-connectors"]').first().prop('disabled')).toEqual( - true - ); - }); - - it('it is disabled and loading when passing loading as true', async () => { - const wrapper = mount( - - - - ); - - expect( - wrapper.find('[data-test-subj="dropdown-connectors"]').first().prop('isLoading') - ).toEqual(true); - expect(wrapper.find('[data-test-subj="dropdown-connectors"]').first().prop('disabled')).toEqual( - true - ); - }); - - it(`it should change connector`, async () => { - const wrapper = mount( - - - - ); - - await waitFor(() => { - expect(wrapper.find(`[data-test-subj="connector-fields-resilient"]`).exists()).toBeFalsy(); - wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); - wrapper.find(`button[data-test-subj="dropdown-connector-resilient-2"]`).simulate('click'); - wrapper.update(); - }); - - await waitFor(() => { - wrapper.update(); - expect(wrapper.find(`[data-test-subj="connector-fields-resilient"]`).exists()).toBeTruthy(); - }); - - act(() => { - ((wrapper.find(EuiComboBox).props() as unknown) as { - onChange: (a: EuiComboBoxOptionOption[]) => void; - }).onChange([{ value: '19', label: 'Denial of Service' }]); - }); - - act(() => { - wrapper - .find('select[data-test-subj="severitySelect"]') - .first() - .simulate('change', { - target: { value: '4' }, - }); - }); - - await waitFor(() => { - expect(globalForm.getFormData()).toEqual({ - connectorId: 'resilient-2', - fields: { incidentTypes: ['19'], severityCode: '4' }, - }); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/create/connector.tsx b/x-pack/plugins/cases/public/components/create/connector.tsx deleted file mode 100644 index 9b6063a7bf9b9..0000000000000 --- a/x-pack/plugins/cases/public/components/create/connector.tsx +++ /dev/null @@ -1,103 +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, { memo, useCallback } from 'react'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; - -import { ConnectorTypes } from '../../../common'; -import { UseField, useFormData, FieldHook, useFormContext } from '../../common/shared_imports'; -import { useConnectors } from '../../containers/configure/use_connectors'; -import { ConnectorSelector } from '../connector_selector/form'; -import { ConnectorFieldsForm } from '../connectors/fields_form'; -import { ActionConnector } from '../../containers/types'; -import { getConnectorById } from '../configure_cases/utils'; -import { FormProps } from './schema'; - -interface Props { - isLoading: boolean; - hideConnectorServiceNowSir?: boolean; -} - -interface ConnectorsFieldProps { - connectors: ActionConnector[]; - field: FieldHook; - isEdit: boolean; - hideConnectorServiceNowSir?: boolean; -} - -const ConnectorFields = ({ - connectors, - isEdit, - field, - hideConnectorServiceNowSir = false, -}: ConnectorsFieldProps) => { - const [{ connectorId }] = useFormData({ watch: ['connectorId'] }); - const { setValue } = field; - let connector = getConnectorById(connectorId, connectors) ?? null; - if ( - connector && - hideConnectorServiceNowSir && - connector.actionTypeId === ConnectorTypes.serviceNowSIR - ) { - connector = null; - } - return ( - - ); -}; - -const ConnectorComponent: React.FC = ({ hideConnectorServiceNowSir = false, isLoading }) => { - const { getFields } = useFormContext(); - const { loading: isLoadingConnectors, connectors } = useConnectors(); - const handleConnectorChange = useCallback( - (newConnector) => { - const { fields } = getFields(); - fields.setValue(null); - }, - [getFields] - ); - - return ( - - - - - - - - - ); -}; - -ConnectorComponent.displayName = 'ConnectorComponent'; - -export const Connector = memo(ConnectorComponent); diff --git a/x-pack/plugins/cases/public/components/create/description.test.tsx b/x-pack/plugins/cases/public/components/create/description.test.tsx deleted file mode 100644 index fcd1f82d64a53..0000000000000 --- a/x-pack/plugins/cases/public/components/create/description.test.tsx +++ /dev/null @@ -1,62 +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 from 'react'; -import { mount } from 'enzyme'; -import { act } from '@testing-library/react'; - -import { useForm, Form, FormHook } from '../../common/shared_imports'; -import { Description } from './description'; -import { schema, FormProps } from './schema'; - -describe('Description', () => { - let globalForm: FormHook; - - const MockHookWrapperComponent: React.FC = ({ children }) => { - const { form } = useForm({ - defaultValue: { description: 'My description' }, - schema: { - description: schema.description, - }, - }); - - globalForm = form; - - return
{children}
; - }; - - beforeEach(() => { - jest.resetAllMocks(); - }); - - it('it renders', async () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find(`[data-test-subj="caseDescription"]`).exists()).toBeTruthy(); - }); - - it('it changes the description', async () => { - const wrapper = mount( - - - - ); - - await act(async () => { - wrapper - .find(`[data-test-subj="caseDescription"] textarea`) - .first() - .simulate('change', { target: { value: 'My new description' } }); - }); - - expect(globalForm.getFormData()).toEqual({ description: 'My new description' }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/create/description.tsx b/x-pack/plugins/cases/public/components/create/description.tsx deleted file mode 100644 index 0a7102cff1ad5..0000000000000 --- a/x-pack/plugins/cases/public/components/create/description.tsx +++ /dev/null @@ -1,31 +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, { memo } from 'react'; -import { MarkdownEditorForm } from '../markdown_editor'; -import { UseField } from '../../common/shared_imports'; -interface Props { - isLoading: boolean; -} - -export const fieldName = 'description'; - -const DescriptionComponent: React.FC = ({ isLoading }) => ( - -); - -DescriptionComponent.displayName = 'DescriptionComponent'; - -export const Description = memo(DescriptionComponent); diff --git a/x-pack/plugins/cases/public/components/create/flyout.test.tsx b/x-pack/plugins/cases/public/components/create/flyout.test.tsx deleted file mode 100644 index 5187029ab60c7..0000000000000 --- a/x-pack/plugins/cases/public/components/create/flyout.test.tsx +++ /dev/null @@ -1,115 +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, { ReactNode } from 'react'; -import { mount } from 'enzyme'; - -import { CreateCaseFlyout } from './flyout'; -import { TestProviders } from '../../common/mock'; - -jest.mock('../create/form_context', () => { - return { - FormContext: ({ - children, - onSuccess, - }: { - children: ReactNode; - onSuccess: ({ id }: { id: string }) => Promise; - }) => { - return ( - <> - - {children} - - ); - }, - }; -}); - -jest.mock('../create/form', () => { - return { - CreateCaseForm: () => { - return <>{'form'}; - }, - }; -}); - -jest.mock('../create/submit_button', () => { - return { - SubmitCaseButton: () => { - return <>{'Submit'}; - }, - }; -}); - -const onCloseFlyout = jest.fn(); -const onSuccess = jest.fn(); -const defaultProps = { - onCloseFlyout, - onSuccess, -}; - -describe('CreateCaseFlyout', () => { - beforeEach(() => { - jest.resetAllMocks(); - }); - - it('renders', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find(`[data-test-subj='create-case-flyout']`).exists()).toBeTruthy(); - }); - - it('Closing modal calls onCloseCaseModal', () => { - const wrapper = mount( - - - - ); - - wrapper.find('.euiFlyout__closeButton').first().simulate('click'); - expect(onCloseFlyout).toBeCalled(); - }); - - it('pass the correct props to FormContext component', () => { - const wrapper = mount( - - - - ); - - const props = wrapper.find('FormContext').props(); - expect(props).toEqual( - expect.objectContaining({ - onSuccess, - }) - ); - }); - - it('onSuccess called when creating a case', () => { - const wrapper = mount( - - - - ); - - wrapper.find(`[data-test-subj='form-context-on-success']`).first().simulate('click'); - expect(onSuccess).toHaveBeenCalledWith({ id: 'case-id' }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/create/flyout.tsx b/x-pack/plugins/cases/public/components/create/flyout.tsx deleted file mode 100644 index 8ed09865e9eab..0000000000000 --- a/x-pack/plugins/cases/public/components/create/flyout.tsx +++ /dev/null @@ -1,71 +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, { memo } from 'react'; -import styled from 'styled-components'; -import { EuiFlyout, EuiFlyoutHeader, EuiTitle, EuiFlyoutBody } from '@elastic/eui'; - -import { FormContext } from '../create/form_context'; -import { CreateCaseForm } from '../create/form'; -import { SubmitCaseButton } from '../create/submit_button'; -import { Case } from '../../containers/types'; -import * as i18n from '../../common/translations'; - -export interface CreateCaseModalProps { - onCloseFlyout: () => void; - onSuccess: (theCase: Case) => Promise; - afterCaseCreated?: (theCase: Case) => Promise; -} - -const Container = styled.div` - ${({ theme }) => ` - margin-top: ${theme.eui.euiSize}; - text-align: right; - `} -`; - -const StyledFlyout = styled(EuiFlyout)` - ${({ theme }) => ` - z-index: ${theme.eui.euiZModal}; - `} -`; - -// Adding bottom padding because timeline's -// bottom bar gonna hide the submit button. -const FormWrapper = styled.div` - padding-bottom: 50px; -`; - -const CreateCaseFlyoutComponent: React.FC = ({ - onSuccess, - afterCaseCreated, - onCloseFlyout, -}) => { - return ( - - - -

{i18n.CREATE_TITLE}

-
-
- - - - - - - - - - -
- ); -}; - -export const CreateCaseFlyout = memo(CreateCaseFlyoutComponent); - -CreateCaseFlyout.displayName = 'CreateCaseFlyout'; diff --git a/x-pack/plugins/cases/public/components/create/form.test.tsx b/x-pack/plugins/cases/public/components/create/form.test.tsx deleted file mode 100644 index 9e59924bdf483..0000000000000 --- a/x-pack/plugins/cases/public/components/create/form.test.tsx +++ /dev/null @@ -1,109 +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 from 'react'; -import { mount } from 'enzyme'; -import { act, waitFor } from '@testing-library/react'; - -import { useForm, Form, FormHook } from '../../common/shared_imports'; -import { useGetTags } from '../../containers/use_get_tags'; -import { useConnectors } from '../../containers/configure/use_connectors'; -import { connectorsMock } from '../../containers/mock'; -import { schema, FormProps } from './schema'; -import { CreateCaseForm } from './form'; - -jest.mock('../../containers/use_get_tags'); -jest.mock('../../containers/configure/use_connectors'); -const useGetTagsMock = useGetTags as jest.Mock; -const useConnectorsMock = useConnectors as jest.Mock; - -const initialCaseValue: FormProps = { - description: '', - tags: [], - title: '', - connectorId: 'none', - fields: null, - syncAlerts: true, -}; - -describe('CreateCaseForm', () => { - let globalForm: FormHook; - const MockHookWrapperComponent: React.FC = ({ children }) => { - const { form } = useForm({ - defaultValue: initialCaseValue, - options: { stripEmptyFields: false }, - schema, - }); - - globalForm = form; - - return
{children}
; - }; - - beforeEach(() => { - jest.resetAllMocks(); - useGetTagsMock.mockReturnValue({ tags: ['test'] }); - useConnectorsMock.mockReturnValue({ loading: false, connectors: connectorsMock }); - }); - - it('it renders with steps', async () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find(`[data-test-subj="case-creation-form-steps"]`).exists()).toBeTruthy(); - }); - - it('it renders without steps', async () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find(`[data-test-subj="case-creation-form-steps"]`).exists()).toBeFalsy(); - }); - - it('it renders all form fields', async () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find(`[data-test-subj="caseTitle"]`).exists()).toBeTruthy(); - expect(wrapper.find(`[data-test-subj="caseTags"]`).exists()).toBeTruthy(); - expect(wrapper.find(`[data-test-subj="caseDescription"]`).exists()).toBeTruthy(); - expect(wrapper.find(`[data-test-subj="caseSyncAlerts"]`).exists()).toBeTruthy(); - expect(wrapper.find(`[data-test-subj="caseConnectors"]`).exists()).toBeTruthy(); - }); - - it('should render spinner when loading', async () => { - const wrapper = mount( - - - - ); - - await act(async () => { - globalForm.setFieldValue('title', 'title'); - globalForm.setFieldValue('description', 'description'); - globalForm.submit(); - // For some weird reason this is needed to pass the test. - // It does not do anything useful - await wrapper.find(`[data-test-subj="caseTitle"]`); - await wrapper.update(); - await waitFor(() => { - expect( - wrapper.find(`[data-test-subj="create-case-loading-spinner"]`).exists() - ).toBeTruthy(); - }); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/create/form.tsx b/x-pack/plugins/cases/public/components/create/form.tsx deleted file mode 100644 index a81ecf32576a9..0000000000000 --- a/x-pack/plugins/cases/public/components/create/form.tsx +++ /dev/null @@ -1,119 +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, { useMemo } from 'react'; -import { EuiLoadingSpinner, EuiSteps } from '@elastic/eui'; -import styled, { css } from 'styled-components'; - -import { useFormContext } from '../../common/shared_imports'; - -import { Title } from './title'; -import { Description } from './description'; -import { Tags } from './tags'; -import { Connector } from './connector'; -import * as i18n from './translations'; -import { SyncAlertsToggle } from './sync_alerts_toggle'; - -interface ContainerProps { - big?: boolean; -} - -const Container = styled.div.attrs((props) => props)` - ${({ big, theme }) => css` - margin-top: ${big ? theme.eui?.euiSizeXL ?? '32px' : theme.eui?.euiSize ?? '16px'}; - `} -`; - -const MySpinner = styled(EuiLoadingSpinner)` - position: absolute; - top: 50%; - left: 50%; - z-index: 99; -`; - -interface Props { - hideConnectorServiceNowSir?: boolean; - withSteps?: boolean; -} - -export const CreateCaseForm: React.FC = React.memo( - ({ hideConnectorServiceNowSir = false, withSteps = true }) => { - const { isSubmitting } = useFormContext(); - - const firstStep = useMemo( - () => ({ - title: i18n.STEP_ONE_TITLE, - children: ( - <> - - <Container> - <Tags isLoading={isSubmitting} /> - </Container> - <Container big> - <Description isLoading={isSubmitting} /> - </Container> - </> - ), - }), - [isSubmitting] - ); - - const secondStep = useMemo( - () => ({ - title: i18n.STEP_TWO_TITLE, - children: ( - <Container> - <SyncAlertsToggle isLoading={isSubmitting} /> - </Container> - ), - }), - [isSubmitting] - ); - - const thirdStep = useMemo( - () => ({ - title: i18n.STEP_THREE_TITLE, - children: ( - <Container> - <Connector - hideConnectorServiceNowSir={hideConnectorServiceNowSir} - isLoading={isSubmitting} - /> - </Container> - ), - }), - [hideConnectorServiceNowSir, isSubmitting] - ); - - const allSteps = useMemo(() => [firstStep, secondStep, thirdStep], [ - firstStep, - secondStep, - thirdStep, - ]); - - return ( - <> - {isSubmitting && <MySpinner data-test-subj="create-case-loading-spinner" size="xl" />} - {withSteps ? ( - <EuiSteps - headingElement="h2" - steps={allSteps} - data-test-subj={'case-creation-form-steps'} - /> - ) : ( - <> - {firstStep.children} - {secondStep.children} - {thirdStep.children} - </> - )} - </> - ); - } -); - -CreateCaseForm.displayName = 'CreateCaseForm'; diff --git a/x-pack/plugins/cases/public/components/create/form_context.test.tsx b/x-pack/plugins/cases/public/components/create/form_context.test.tsx deleted file mode 100644 index 207ff6207e09d..0000000000000 --- a/x-pack/plugins/cases/public/components/create/form_context.test.tsx +++ /dev/null @@ -1,682 +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 from 'react'; -import { mount, ReactWrapper } from 'enzyme'; -import { act, waitFor } from '@testing-library/react'; -import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; - -import { ConnectorTypes } from '../../../common'; -import { TestProviders } from '../../common/mock'; -import { usePostCase } from '../../containers/use_post_case'; -import { useGetTags } from '../../containers/use_get_tags'; -import { useConnectors } from '../../containers/configure/use_connectors'; -import { useCaseConfigure } from '../../containers/configure/use_configure'; -import { connectorsMock } from '../../containers/configure/mock'; -import { useGetIncidentTypes } from '../connectors/resilient/use_get_incident_types'; -import { useGetSeverity } from '../connectors/resilient/use_get_severity'; -import { useGetIssueTypes } from '../connectors/jira/use_get_issue_types'; -import { useGetChoices } from '../connectors/servicenow/use_get_choices'; -import { useGetFieldsByIssueType } from '../connectors/jira/use_get_fields_by_issue_type'; -import { useCaseConfigureResponse } from '../configure_cases/__mock__'; -import { - sampleConnectorData, - sampleData, - sampleTags, - useGetIncidentTypesResponse, - useGetSeverityResponse, - useGetIssueTypesResponse, - useGetFieldsByIssueTypeResponse, - useGetChoicesResponse, -} from './mock'; -import { FormContext } from './form_context'; -import { CreateCaseForm } from './form'; -import { SubmitCaseButton } from './submit_button'; -import { usePostPushToService } from '../../containers/use_post_push_to_service'; - -const sampleId = 'case-id'; - -jest.mock('../../containers/use_post_case'); -jest.mock('../../containers/use_post_push_to_service'); -jest.mock('../../containers/use_get_tags'); -jest.mock('../../containers/configure/use_connectors'); -jest.mock('../../containers/configure/use_configure'); -jest.mock('../connectors/resilient/use_get_incident_types'); -jest.mock('../connectors/resilient/use_get_severity'); -jest.mock('../connectors/jira/use_get_issue_types'); -jest.mock('../connectors/jira/use_get_fields_by_issue_type'); -jest.mock('../connectors/jira/use_get_single_issue'); -jest.mock('../connectors/jira/use_get_issues'); -jest.mock('../connectors/servicenow/use_get_choices'); - -const useConnectorsMock = useConnectors as jest.Mock; -const useCaseConfigureMock = useCaseConfigure as jest.Mock; -const usePostCaseMock = usePostCase as jest.Mock; -const usePostPushToServiceMock = usePostPushToService as jest.Mock; -const useGetIncidentTypesMock = useGetIncidentTypes as jest.Mock; -const useGetSeverityMock = useGetSeverity as jest.Mock; -const useGetIssueTypesMock = useGetIssueTypes as jest.Mock; -const useGetFieldsByIssueTypeMock = useGetFieldsByIssueType as jest.Mock; -const useGetChoicesMock = useGetChoices as jest.Mock; -const postCase = jest.fn(); -const pushCaseToExternalService = jest.fn(); - -const defaultPostCase = { - isLoading: false, - isError: false, - postCase, -}; - -const defaultPostPushToService = { - isLoading: false, - isError: false, - pushCaseToExternalService, -}; - -const fillForm = (wrapper: ReactWrapper) => { - wrapper - .find(`[data-test-subj="caseTitle"] input`) - .first() - .simulate('change', { target: { value: sampleData.title } }); - - wrapper - .find(`[data-test-subj="caseDescription"] textarea`) - .first() - .simulate('change', { target: { value: sampleData.description } }); - - act(() => { - ((wrapper.find(EuiComboBox).props() as unknown) as { - onChange: (a: EuiComboBoxOptionOption[]) => void; - }).onChange(sampleTags.map((tag) => ({ label: tag }))); - }); -}; - -describe('Create case', () => { - const fetchTags = jest.fn(); - const onFormSubmitSuccess = jest.fn(); - const afterCaseCreated = jest.fn(); - - beforeEach(() => { - jest.resetAllMocks(); - postCase.mockResolvedValue({ - id: sampleId, - ...sampleData, - }); - usePostCaseMock.mockImplementation(() => defaultPostCase); - usePostPushToServiceMock.mockImplementation(() => defaultPostPushToService); - useConnectorsMock.mockReturnValue(sampleConnectorData); - useCaseConfigureMock.mockImplementation(() => useCaseConfigureResponse); - useGetIncidentTypesMock.mockReturnValue(useGetIncidentTypesResponse); - useGetSeverityMock.mockReturnValue(useGetSeverityResponse); - useGetIssueTypesMock.mockReturnValue(useGetIssueTypesResponse); - useGetFieldsByIssueTypeMock.mockReturnValue(useGetFieldsByIssueTypeResponse); - useGetChoicesMock.mockReturnValue(useGetChoicesResponse); - - (useGetTags as jest.Mock).mockImplementation(() => ({ - tags: sampleTags, - fetchTags, - })); - }); - - describe('Step 1 - Case Fields', () => { - it('it renders', async () => { - const wrapper = mount( - <TestProviders> - <FormContext onSuccess={onFormSubmitSuccess}> - <CreateCaseForm /> - <SubmitCaseButton /> - </FormContext> - </TestProviders> - ); - - expect(wrapper.find(`[data-test-subj="caseTitle"]`).first().exists()).toBeTruthy(); - expect(wrapper.find(`[data-test-subj="caseDescription"]`).first().exists()).toBeTruthy(); - expect(wrapper.find(`[data-test-subj="caseTags"]`).first().exists()).toBeTruthy(); - expect(wrapper.find(`[data-test-subj="caseConnectors"]`).first().exists()).toBeTruthy(); - expect( - wrapper.find(`[data-test-subj="case-creation-form-steps"]`).first().exists() - ).toBeTruthy(); - }); - - it('should post case on submit click', async () => { - useConnectorsMock.mockReturnValue({ - ...sampleConnectorData, - connectors: connectorsMock, - }); - - const wrapper = mount( - <TestProviders> - <FormContext onSuccess={onFormSubmitSuccess}> - <CreateCaseForm /> - <SubmitCaseButton /> - </FormContext> - </TestProviders> - ); - - fillForm(wrapper); - wrapper.find(`[data-test-subj="create-case-submit"]`).first().simulate('click'); - await waitFor(() => expect(postCase).toBeCalledWith(sampleData)); - }); - - it('should toggle sync settings', async () => { - useConnectorsMock.mockReturnValue({ - ...sampleConnectorData, - connectors: connectorsMock, - }); - - const wrapper = mount( - <TestProviders> - <FormContext onSuccess={onFormSubmitSuccess}> - <CreateCaseForm /> - <SubmitCaseButton /> - </FormContext> - </TestProviders> - ); - - fillForm(wrapper); - wrapper.find('[data-test-subj="caseSyncAlerts"] button').first().simulate('click'); - wrapper.find(`[data-test-subj="create-case-submit"]`).first().simulate('click'); - - await waitFor(() => - expect(postCase).toBeCalledWith({ ...sampleData, settings: { syncAlerts: false } }) - ); - }); - - it('it should select the default connector set in the configuration', async () => { - useCaseConfigureMock.mockImplementation(() => ({ - ...useCaseConfigureResponse, - connector: { - id: 'servicenow-1', - name: 'SN', - type: ConnectorTypes.serviceNowITSM, - fields: null, - }, - persistLoading: false, - })); - - useConnectorsMock.mockReturnValue({ - ...sampleConnectorData, - connectors: connectorsMock, - }); - - const wrapper = mount( - <TestProviders> - <FormContext onSuccess={onFormSubmitSuccess}> - <CreateCaseForm /> - <SubmitCaseButton /> - </FormContext> - </TestProviders> - ); - - fillForm(wrapper); - await act(async () => { - wrapper.find(`[data-test-subj="create-case-submit"]`).first().simulate('click'); - }); - - await waitFor(() => - expect(postCase).toBeCalledWith({ - ...sampleData, - connector: { - fields: { - impact: null, - severity: null, - urgency: null, - category: null, - subcategory: null, - }, - id: 'servicenow-1', - name: 'My Connector', - type: '.servicenow', - }, - }) - ); - }); - - it('it should default to none if the default connector does not exist in connectors', async () => { - useCaseConfigureMock.mockImplementation(() => ({ - ...useCaseConfigureResponse, - connector: { - id: 'not-exist', - name: 'SN', - type: ConnectorTypes.serviceNowITSM, - fields: null, - }, - persistLoading: false, - })); - - useConnectorsMock.mockReturnValue({ - ...sampleConnectorData, - connectors: connectorsMock, - }); - - const wrapper = mount( - <TestProviders> - <FormContext onSuccess={onFormSubmitSuccess}> - <CreateCaseForm /> - <SubmitCaseButton /> - </FormContext> - </TestProviders> - ); - - fillForm(wrapper); - wrapper.find(`[data-test-subj="create-case-submit"]`).first().simulate('click'); - await waitFor(() => { - expect(postCase).toBeCalledWith(sampleData); - expect(pushCaseToExternalService).not.toHaveBeenCalled(); - }); - }); - }); - - describe('Step 2 - Connector Fields', () => { - it(`it should submit and push to Jira connector`, async () => { - useConnectorsMock.mockReturnValue({ - ...sampleConnectorData, - connectors: connectorsMock, - }); - - const wrapper = mount( - <TestProviders> - <FormContext onSuccess={onFormSubmitSuccess}> - <CreateCaseForm /> - <SubmitCaseButton /> - </FormContext> - </TestProviders> - ); - - fillForm(wrapper); - expect(wrapper.find(`[data-test-subj="connector-fields-jira"]`).exists()).toBeFalsy(); - wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); - wrapper.find(`button[data-test-subj="dropdown-connector-jira-1"]`).simulate('click'); - - await waitFor(() => { - wrapper.update(); - expect(wrapper.find(`[data-test-subj="connector-fields-jira"]`).exists()).toBeTruthy(); - }); - - wrapper - .find('select[data-test-subj="issueTypeSelect"]') - .first() - .simulate('change', { - target: { value: '10007' }, - }); - - wrapper - .find('select[data-test-subj="prioritySelect"]') - .first() - .simulate('change', { - target: { value: '2' }, - }); - - wrapper.find(`[data-test-subj="create-case-submit"]`).first().simulate('click'); - - await waitFor(() => { - expect(postCase).toBeCalledWith({ - ...sampleData, - connector: { - id: 'jira-1', - name: 'Jira', - type: '.jira', - fields: { issueType: '10007', parent: null, priority: '2' }, - }, - }); - expect(pushCaseToExternalService).toHaveBeenCalledWith({ - caseId: sampleId, - connector: { - id: 'jira-1', - name: 'Jira', - type: '.jira', - fields: { issueType: '10007', parent: null, priority: '2' }, - }, - }); - expect(onFormSubmitSuccess).toHaveBeenCalledWith({ - id: sampleId, - ...sampleData, - }); - }); - }); - - it(`it should submit and push to resilient connector`, async () => { - useConnectorsMock.mockReturnValue({ - ...sampleConnectorData, - connectors: connectorsMock, - }); - - const wrapper = mount( - <TestProviders> - <FormContext onSuccess={onFormSubmitSuccess}> - <CreateCaseForm /> - <SubmitCaseButton /> - </FormContext> - </TestProviders> - ); - - fillForm(wrapper); - expect(wrapper.find(`[data-test-subj="connector-fields-resilient"]`).exists()).toBeFalsy(); - wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); - wrapper.find(`button[data-test-subj="dropdown-connector-resilient-2"]`).simulate('click'); - - await waitFor(() => { - wrapper.update(); - expect(wrapper.find(`[data-test-subj="connector-fields-resilient"]`).exists()).toBeTruthy(); - }); - - act(() => { - ((wrapper.find(EuiComboBox).at(1).props() as unknown) as { - onChange: (a: EuiComboBoxOptionOption[]) => void; - }).onChange([{ value: '19', label: 'Denial of Service' }]); - }); - - wrapper - .find('select[data-test-subj="severitySelect"]') - .first() - .simulate('change', { - target: { value: '4' }, - }); - - wrapper.find(`[data-test-subj="create-case-submit"]`).first().simulate('click'); - - await waitFor(() => { - expect(postCase).toBeCalledWith({ - ...sampleData, - connector: { - id: 'resilient-2', - name: 'My Connector 2', - type: '.resilient', - fields: { incidentTypes: ['19'], severityCode: '4' }, - }, - }); - - expect(pushCaseToExternalService).toHaveBeenCalledWith({ - caseId: sampleId, - connector: { - id: 'resilient-2', - name: 'My Connector 2', - type: '.resilient', - fields: { incidentTypes: ['19'], severityCode: '4' }, - }, - }); - - expect(onFormSubmitSuccess).toHaveBeenCalledWith({ - id: sampleId, - ...sampleData, - }); - }); - }); - - it(`it should submit and push to servicenow itsm connector`, async () => { - useConnectorsMock.mockReturnValue({ - ...sampleConnectorData, - connectors: connectorsMock, - }); - - const wrapper = mount( - <TestProviders> - <FormContext onSuccess={onFormSubmitSuccess}> - <CreateCaseForm /> - <SubmitCaseButton /> - </FormContext> - </TestProviders> - ); - - fillForm(wrapper); - expect(wrapper.find(`[data-test-subj="connector-fields-sn-itsm"]`).exists()).toBeFalsy(); - wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); - wrapper.find(`button[data-test-subj="dropdown-connector-servicenow-1"]`).simulate('click'); - - await waitFor(() => { - wrapper.update(); - expect(wrapper.find(`[data-test-subj="connector-fields-sn-itsm"]`).exists()).toBeTruthy(); - }); - - ['severitySelect', 'urgencySelect', 'impactSelect'].forEach((subj) => { - wrapper - .find(`select[data-test-subj="${subj}"]`) - .first() - .simulate('change', { - target: { value: '2' }, - }); - }); - - wrapper - .find('select[data-test-subj="categorySelect"]') - .first() - .simulate('change', { - target: { value: 'software' }, - }); - - wrapper - .find('select[data-test-subj="subcategorySelect"]') - .first() - .simulate('change', { - target: { value: 'os' }, - }); - - wrapper.find(`[data-test-subj="create-case-submit"]`).first().simulate('click'); - - await waitFor(() => { - expect(postCase).toBeCalledWith({ - ...sampleData, - connector: { - id: 'servicenow-1', - name: 'My Connector', - type: '.servicenow', - fields: { - impact: '2', - severity: '2', - urgency: '2', - category: 'software', - subcategory: 'os', - }, - }, - }); - - expect(pushCaseToExternalService).toHaveBeenCalledWith({ - caseId: sampleId, - connector: { - id: 'servicenow-1', - name: 'My Connector', - type: '.servicenow', - fields: { - impact: '2', - severity: '2', - urgency: '2', - category: 'software', - subcategory: 'os', - }, - }, - }); - - expect(onFormSubmitSuccess).toHaveBeenCalledWith({ - id: sampleId, - ...sampleData, - }); - }); - }); - - it(`it should submit and push to servicenow sir connector`, async () => { - useConnectorsMock.mockReturnValue({ - ...sampleConnectorData, - connectors: connectorsMock, - }); - - const wrapper = mount( - <TestProviders> - <FormContext onSuccess={onFormSubmitSuccess}> - <CreateCaseForm /> - <SubmitCaseButton /> - </FormContext> - </TestProviders> - ); - - fillForm(wrapper); - expect(wrapper.find(`[data-test-subj="connector-fields-sn-sir"]`).exists()).toBeFalsy(); - wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); - wrapper.find(`button[data-test-subj="dropdown-connector-servicenow-sir"]`).simulate('click'); - - await waitFor(() => { - wrapper.update(); - expect(wrapper.find(`[data-test-subj="connector-fields-sn-sir"]`).exists()).toBeTruthy(); - }); - - wrapper - .find('[data-test-subj="destIpCheckbox"] input') - .first() - .simulate('change', { target: { checked: false } }); - - wrapper - .find('select[data-test-subj="prioritySelect"]') - .first() - .simulate('change', { - target: { value: '1' }, - }); - - wrapper - .find('select[data-test-subj="categorySelect"]') - .first() - .simulate('change', { - target: { value: 'Denial of Service' }, - }); - - wrapper - .find('select[data-test-subj="subcategorySelect"]') - .first() - .simulate('change', { - target: { value: '26' }, - }); - - wrapper.find(`[data-test-subj="create-case-submit"]`).first().simulate('click'); - - await waitFor(() => { - expect(postCase).toBeCalledWith({ - ...sampleData, - connector: { - id: 'servicenow-sir', - name: 'My Connector SIR', - type: '.servicenow-sir', - fields: { - destIp: false, - sourceIp: true, - malwareHash: true, - malwareUrl: true, - priority: '1', - category: 'Denial of Service', - subcategory: '26', - }, - }, - }); - - expect(pushCaseToExternalService).toHaveBeenCalledWith({ - caseId: sampleId, - connector: { - id: 'servicenow-sir', - name: 'My Connector SIR', - type: '.servicenow-sir', - fields: { - destIp: false, - sourceIp: true, - malwareHash: true, - malwareUrl: true, - priority: '1', - category: 'Denial of Service', - subcategory: '26', - }, - }, - }); - - expect(onFormSubmitSuccess).toHaveBeenCalledWith({ - id: sampleId, - ...sampleData, - }); - }); - }); - }); - - it(`it should call afterCaseCreated`, async () => { - useConnectorsMock.mockReturnValue({ - ...sampleConnectorData, - connectors: connectorsMock, - }); - - const wrapper = mount( - <TestProviders> - <FormContext onSuccess={onFormSubmitSuccess} afterCaseCreated={afterCaseCreated}> - <CreateCaseForm /> - <SubmitCaseButton /> - </FormContext> - </TestProviders> - ); - - fillForm(wrapper); - expect(wrapper.find(`[data-test-subj="connector-fields-jira"]`).exists()).toBeFalsy(); - wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); - wrapper.find(`button[data-test-subj="dropdown-connector-jira-1"]`).simulate('click'); - - await waitFor(() => { - wrapper.update(); - expect(wrapper.find(`[data-test-subj="connector-fields-jira"]`).exists()).toBeTruthy(); - }); - - wrapper.find(`[data-test-subj="create-case-submit"]`).first().simulate('click'); - await waitFor(() => { - expect(afterCaseCreated).toHaveBeenCalledWith({ - id: sampleId, - ...sampleData, - }); - }); - }); - - it(`it should call callbacks in correct order`, async () => { - useConnectorsMock.mockReturnValue({ - ...sampleConnectorData, - connectors: connectorsMock, - }); - - const wrapper = mount( - <TestProviders> - <FormContext onSuccess={onFormSubmitSuccess} afterCaseCreated={afterCaseCreated}> - <CreateCaseForm /> - <SubmitCaseButton /> - </FormContext> - </TestProviders> - ); - - fillForm(wrapper); - expect(wrapper.find(`[data-test-subj="connector-fields-jira"]`).exists()).toBeFalsy(); - wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); - wrapper.find(`button[data-test-subj="dropdown-connector-jira-1"]`).simulate('click'); - - await waitFor(() => { - wrapper.update(); - expect(wrapper.find(`[data-test-subj="connector-fields-jira"]`).exists()).toBeTruthy(); - }); - - wrapper.find(`[data-test-subj="create-case-submit"]`).first().simulate('click'); - await waitFor(() => { - expect(postCase).toHaveBeenCalled(); - expect(afterCaseCreated).toHaveBeenCalled(); - expect(pushCaseToExternalService).toHaveBeenCalled(); - expect(onFormSubmitSuccess).toHaveBeenCalled(); - }); - - const postCaseOrder = postCase.mock.invocationCallOrder[0]; - const afterCaseOrder = afterCaseCreated.mock.invocationCallOrder[0]; - const pushCaseToExternalServiceOrder = pushCaseToExternalService.mock.invocationCallOrder[0]; - const onFormSubmitSuccessOrder = onFormSubmitSuccess.mock.invocationCallOrder[0]; - - expect( - postCaseOrder < afterCaseOrder && - postCaseOrder < pushCaseToExternalServiceOrder && - postCaseOrder < onFormSubmitSuccessOrder - ).toBe(true); - - expect( - afterCaseOrder < pushCaseToExternalServiceOrder && afterCaseOrder < onFormSubmitSuccessOrder - ).toBe(true); - - expect(pushCaseToExternalServiceOrder < onFormSubmitSuccessOrder).toBe(true); - }); -}); diff --git a/x-pack/plugins/cases/public/components/create/form_context.tsx b/x-pack/plugins/cases/public/components/create/form_context.tsx deleted file mode 100644 index e84f451ab4215..0000000000000 --- a/x-pack/plugins/cases/public/components/create/form_context.tsx +++ /dev/null @@ -1,120 +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, useEffect, useMemo } from 'react'; -import { schema, FormProps } from './schema'; -import { Form, useForm } from '../../common/shared_imports'; -import { - getConnectorById, - getNoneConnector, - normalizeActionConnector, -} from '../configure_cases/utils'; -import { usePostCase } from '../../containers/use_post_case'; -import { usePostPushToService } from '../../containers/use_post_push_to_service'; - -import { useConnectors } from '../../containers/configure/use_connectors'; -import { useCaseConfigure } from '../../containers/configure/use_configure'; -import { Case } from '../../containers/types'; -import { CaseType, ConnectorTypes } from '../../../common'; - -const initialCaseValue: FormProps = { - description: '', - tags: [], - title: '', - connectorId: 'none', - fields: null, - syncAlerts: true, -}; - -interface Props { - afterCaseCreated?: (theCase: Case) => Promise<void>; - caseType?: CaseType; - hideConnectorServiceNowSir?: boolean; - onSuccess?: (theCase: Case) => Promise<void>; -} - -export const FormContext: React.FC<Props> = ({ - afterCaseCreated, - caseType = CaseType.individual, - children, - hideConnectorServiceNowSir, - onSuccess, -}) => { - const { connectors } = useConnectors(); - const { connector: configurationConnector } = useCaseConfigure(); - const { postCase } = usePostCase(); - const { pushCaseToExternalService } = usePostPushToService(); - - const connectorId = useMemo(() => { - if ( - hideConnectorServiceNowSir && - configurationConnector.type === ConnectorTypes.serviceNowSIR - ) { - return 'none'; - } - return connectors.some((connector) => connector.id === configurationConnector.id) - ? configurationConnector.id - : 'none'; - }, [ - configurationConnector.id, - configurationConnector.type, - connectors, - hideConnectorServiceNowSir, - ]); - - const submitCase = useCallback( - async ( - { connectorId: dataConnectorId, fields, syncAlerts, ...dataWithoutConnectorId }, - isValid - ) => { - if (isValid) { - const caseConnector = getConnectorById(dataConnectorId, connectors); - - const connectorToUpdate = caseConnector - ? normalizeActionConnector(caseConnector, fields) - : getNoneConnector(); - - const updatedCase = await postCase({ - ...dataWithoutConnectorId, - type: caseType, - connector: connectorToUpdate, - settings: { syncAlerts }, - }); - - if (afterCaseCreated && updatedCase) { - await afterCaseCreated(updatedCase); - } - - if (updatedCase?.id && dataConnectorId !== 'none') { - await pushCaseToExternalService({ - caseId: updatedCase.id, - connector: connectorToUpdate, - }); - } - - if (onSuccess && updatedCase) { - await onSuccess(updatedCase); - } - } - }, - [caseType, connectors, postCase, onSuccess, pushCaseToExternalService, afterCaseCreated] - ); - - const { form } = useForm<FormProps>({ - defaultValue: initialCaseValue, - options: { stripEmptyFields: false }, - schema, - onSubmit: submitCase, - }); - const { setFieldValue } = form; - // Set the selected connector to the configuration connector - useEffect(() => setFieldValue('connectorId', connectorId), [connectorId, setFieldValue]); - - return <Form form={form}>{children}</Form>; -}; - -FormContext.displayName = 'FormContext'; diff --git a/x-pack/plugins/cases/public/components/create/index.test.tsx b/x-pack/plugins/cases/public/components/create/index.test.tsx deleted file mode 100644 index e82af8edc6337..0000000000000 --- a/x-pack/plugins/cases/public/components/create/index.test.tsx +++ /dev/null @@ -1,126 +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 from 'react'; -import { mount, ReactWrapper } from 'enzyme'; -import { act, waitFor } from '@testing-library/react'; -import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; - -import { TestProviders } from '../../common/mock'; -import { useGetTags } from '../../containers/use_get_tags'; -import { useConnectors } from '../../containers/configure/use_connectors'; -import { useCaseConfigure } from '../../containers/configure/use_configure'; -import { useGetIncidentTypes } from '../connectors/resilient/use_get_incident_types'; -import { useGetSeverity } from '../connectors/resilient/use_get_severity'; -import { useGetIssueTypes } from '../connectors/jira/use_get_issue_types'; -import { useGetFieldsByIssueType } from '../connectors/jira/use_get_fields_by_issue_type'; -import { useCaseConfigureResponse } from '../configure_cases/__mock__'; -import { - sampleConnectorData, - sampleData, - sampleTags, - useGetIncidentTypesResponse, - useGetSeverityResponse, - useGetIssueTypesResponse, - useGetFieldsByIssueTypeResponse, -} from './mock'; -import { CreateCase } from '.'; - -jest.mock('../../containers/api'); -jest.mock('../../containers/use_get_tags'); -jest.mock('../../containers/configure/use_connectors'); -jest.mock('../../containers/configure/use_configure'); -jest.mock('../connectors/resilient/use_get_incident_types'); -jest.mock('../connectors/resilient/use_get_severity'); -jest.mock('../connectors/jira/use_get_issue_types'); -jest.mock('../connectors/jira/use_get_fields_by_issue_type'); -jest.mock('../connectors/jira/use_get_single_issue'); -jest.mock('../connectors/jira/use_get_issues'); - -const useConnectorsMock = useConnectors as jest.Mock; -const useCaseConfigureMock = useCaseConfigure as jest.Mock; -const useGetTagsMock = useGetTags as jest.Mock; -const useGetIncidentTypesMock = useGetIncidentTypes as jest.Mock; -const useGetSeverityMock = useGetSeverity as jest.Mock; -const useGetIssueTypesMock = useGetIssueTypes as jest.Mock; -const useGetFieldsByIssueTypeMock = useGetFieldsByIssueType as jest.Mock; -const fetchTags = jest.fn(); - -const fillForm = (wrapper: ReactWrapper) => { - wrapper - .find(`[data-test-subj="caseTitle"] input`) - .first() - .simulate('change', { target: { value: sampleData.title } }); - - wrapper - .find(`[data-test-subj="caseDescription"] textarea`) - .first() - .simulate('change', { target: { value: sampleData.description } }); - - act(() => { - ((wrapper.find(EuiComboBox).props() as unknown) as { - onChange: (a: EuiComboBoxOptionOption[]) => void; - }).onChange(sampleTags.map((tag) => ({ label: tag }))); - }); -}; - -const defaultProps = { - onCancel: jest.fn(), - onSuccess: jest.fn(), -}; - -describe('CreateCase case', () => { - beforeEach(() => { - jest.resetAllMocks(); - useConnectorsMock.mockReturnValue(sampleConnectorData); - useCaseConfigureMock.mockImplementation(() => useCaseConfigureResponse); - useGetIncidentTypesMock.mockReturnValue(useGetIncidentTypesResponse); - useGetSeverityMock.mockReturnValue(useGetSeverityResponse); - useGetIssueTypesMock.mockReturnValue(useGetIssueTypesResponse); - useGetFieldsByIssueTypeMock.mockReturnValue(useGetFieldsByIssueTypeResponse); - useGetTagsMock.mockImplementation(() => ({ - tags: sampleTags, - fetchTags, - })); - }); - - it('it renders', async () => { - const wrapper = mount( - <TestProviders> - <CreateCase {...defaultProps} /> - </TestProviders> - ); - - expect(wrapper.find(`[data-test-subj="create-case-submit"]`).exists()).toBeTruthy(); - expect(wrapper.find(`[data-test-subj="create-case-cancel"]`).exists()).toBeTruthy(); - }); - - it('should call cancel on cancel click', async () => { - const wrapper = mount( - <TestProviders> - <CreateCase {...defaultProps} /> - </TestProviders> - ); - - wrapper.find(`[data-test-subj="create-case-cancel"]`).first().simulate('click'); - expect(defaultProps.onCancel).toHaveBeenCalled(); - }); - - it('should redirect to new case when posting the case', async () => { - const wrapper = mount( - <TestProviders> - <CreateCase {...defaultProps} /> - </TestProviders> - ); - - fillForm(wrapper); - wrapper.find(`[data-test-subj="create-case-submit"]`).first().simulate('click'); - await waitFor(() => { - expect(defaultProps.onSuccess).toHaveBeenCalled(); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/create/index.tsx b/x-pack/plugins/cases/public/components/create/index.tsx deleted file mode 100644 index 192effb6adb24..0000000000000 --- a/x-pack/plugins/cases/public/components/create/index.tsx +++ /dev/null @@ -1,57 +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 from 'react'; -import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import styled from 'styled-components'; - -import { Field, getUseField } from '../../common/shared_imports'; -import * as i18n from './translations'; -import { CreateCaseForm } from './form'; -import { FormContext } from './form_context'; -import { SubmitCaseButton } from './submit_button'; -import { Case } from '../../containers/types'; - -export const CommonUseField = getUseField({ component: Field }); - -const Container = styled.div` - ${({ theme }) => ` - margin-top: ${theme.eui.euiSize}; - `} -`; - -export interface CreateCaseProps { - afterCaseCreated?: (theCase: Case) => Promise<void>; - onCancel: () => void; - onSuccess: (theCase: Case) => Promise<void>; -} - -export const CreateCase = ({ afterCaseCreated, onCancel, onSuccess }: CreateCaseProps) => ( - <FormContext afterCaseCreated={afterCaseCreated} onSuccess={onSuccess}> - <CreateCaseForm /> - <Container> - <EuiFlexGroup alignItems="center" justifyContent="flexEnd" gutterSize="xs" responsive={false}> - <EuiFlexItem grow={false}> - <EuiButtonEmpty - data-test-subj="create-case-cancel" - size="s" - onClick={onCancel} - iconType="cross" - > - {i18n.CANCEL} - </EuiButtonEmpty> - </EuiFlexItem> - <EuiFlexItem grow={false}> - <SubmitCaseButton /> - </EuiFlexItem> - </EuiFlexGroup> - </Container> - </FormContext> -); - -// eslint-disable-next-line import/no-default-export -export { CreateCase as default }; diff --git a/x-pack/plugins/cases/public/components/create/mock.ts b/x-pack/plugins/cases/public/components/create/mock.ts deleted file mode 100644 index eb40fa097d3cc..0000000000000 --- a/x-pack/plugins/cases/public/components/create/mock.ts +++ /dev/null @@ -1,101 +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 { CasePostRequest, CaseType, ConnectorTypes } from '../../../common'; -import { choices } from '../connectors/mock'; - -export const sampleTags = ['coke', 'pepsi']; -export const sampleData: CasePostRequest = { - description: 'what a great description', - tags: sampleTags, - title: 'what a cool title', - type: CaseType.individual, - connector: { - fields: null, - id: 'none', - name: 'none', - type: ConnectorTypes.none, - }, - settings: { - syncAlerts: true, - }, -}; - -export const sampleConnectorData = { loading: false, connectors: [] }; - -export const useGetIncidentTypesResponse = { - isLoading: false, - incidentTypes: [ - { - id: 19, - name: 'Malware', - }, - { - id: 21, - name: 'Denial of Service', - }, - ], -}; - -export const useGetSeverityResponse = { - isLoading: false, - severity: [ - { - id: 4, - name: 'Low', - }, - { - id: 5, - name: 'Medium', - }, - { - id: 6, - name: 'High', - }, - ], -}; - -export const useGetIssueTypesResponse = { - isLoading: false, - issueTypes: [ - { - id: '10006', - name: 'Task', - }, - { - id: '10007', - name: 'Bug', - }, - ], -}; - -export const useGetFieldsByIssueTypeResponse = { - isLoading: false, - fields: { - summary: { allowedValues: [], defaultValue: {} }, - labels: { allowedValues: [], defaultValue: {} }, - description: { allowedValues: [], defaultValue: {} }, - priority: { - allowedValues: [ - { - name: 'Medium', - id: '3', - }, - { - name: 'Low', - id: '2', - }, - ], - defaultValue: { name: 'Medium', id: '3' }, - }, - }, -}; - -export const useGetChoicesResponse = { - isLoading: false, - choices, -}; diff --git a/x-pack/plugins/cases/public/components/create/optional_field_label/index.test.tsx b/x-pack/plugins/cases/public/components/create/optional_field_label/index.test.tsx deleted file mode 100644 index 4b6d5f90513ef..0000000000000 --- a/x-pack/plugins/cases/public/components/create/optional_field_label/index.test.tsx +++ /dev/null @@ -1,19 +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 { mount } from 'enzyme'; - -import { OptionalFieldLabel } from '.'; - -describe('OptionalFieldLabel', () => { - it('it renders correctly', async () => { - const wrapper = mount(OptionalFieldLabel); - expect(wrapper.find('[data-test-subj="form-optional-field-label"]').first().text()).toBe( - 'Optional' - ); - }); -}); diff --git a/x-pack/plugins/cases/public/components/create/optional_field_label/index.tsx b/x-pack/plugins/cases/public/components/create/optional_field_label/index.tsx deleted file mode 100644 index ea994b2219961..0000000000000 --- a/x-pack/plugins/cases/public/components/create/optional_field_label/index.tsx +++ /dev/null @@ -1,17 +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 { EuiText } from '@elastic/eui'; -import React from 'react'; - -import * as i18n from '../../../common/translations'; - -export const OptionalFieldLabel = ( - <EuiText color="subdued" size="xs" data-test-subj="form-optional-field-label"> - {i18n.OPTIONAL} - </EuiText> -); diff --git a/x-pack/plugins/cases/public/components/create/schema.tsx b/x-pack/plugins/cases/public/components/create/schema.tsx deleted file mode 100644 index 7ca1e2e061545..0000000000000 --- a/x-pack/plugins/cases/public/components/create/schema.tsx +++ /dev/null @@ -1,58 +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 { CasePostRequest, ConnectorTypeFields } from '../../../common'; -import { FIELD_TYPES, fieldValidators, FormSchema } from '../../common/shared_imports'; -import * as i18n from './translations'; - -import { OptionalFieldLabel } from './optional_field_label'; -const { emptyField } = fieldValidators; - -export const schemaTags = { - type: FIELD_TYPES.COMBO_BOX, - label: i18n.TAGS, - helpText: i18n.TAGS_HELP, - labelAppend: OptionalFieldLabel, -}; - -export type FormProps = Omit<CasePostRequest, 'connector' | 'settings'> & { - connectorId: string; - fields: ConnectorTypeFields['fields']; - syncAlerts: boolean; -}; - -export const schema: FormSchema<FormProps> = { - title: { - type: FIELD_TYPES.TEXT, - label: i18n.NAME, - validations: [ - { - validator: emptyField(i18n.TITLE_REQUIRED), - }, - ], - }, - description: { - label: i18n.DESCRIPTION, - validations: [ - { - validator: emptyField(i18n.DESCRIPTION_REQUIRED), - }, - ], - }, - tags: schemaTags, - connectorId: { - type: FIELD_TYPES.SUPER_SELECT, - label: i18n.CONNECTORS, - defaultValue: 'none', - }, - fields: {}, - syncAlerts: { - helpText: i18n.SYNC_ALERTS_HELP, - type: FIELD_TYPES.TOGGLE, - defaultValue: true, - }, -}; diff --git a/x-pack/plugins/cases/public/components/create/submit_button.test.tsx b/x-pack/plugins/cases/public/components/create/submit_button.test.tsx deleted file mode 100644 index dd67c8170dc3f..0000000000000 --- a/x-pack/plugins/cases/public/components/create/submit_button.test.tsx +++ /dev/null @@ -1,88 +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 from 'react'; -import { mount } from 'enzyme'; -import { act, waitFor } from '@testing-library/react'; - -import { useForm, Form } from '../../common/shared_imports'; -import { SubmitCaseButton } from './submit_button'; -import { schema, FormProps } from './schema'; - -describe('SubmitCaseButton', () => { - const onSubmit = jest.fn(); - - const MockHookWrapperComponent: React.FC = ({ children }) => { - const { form } = useForm<FormProps>({ - defaultValue: { title: 'My title' }, - schema: { - title: schema.title, - }, - onSubmit, - }); - - return <Form form={form}>{children}</Form>; - }; - - beforeEach(() => { - jest.resetAllMocks(); - }); - - it('it renders', async () => { - const wrapper = mount( - <MockHookWrapperComponent> - <SubmitCaseButton /> - </MockHookWrapperComponent> - ); - - expect(wrapper.find(`[data-test-subj="create-case-submit"]`).exists()).toBeTruthy(); - }); - - it('it submits', async () => { - const wrapper = mount( - <MockHookWrapperComponent> - <SubmitCaseButton /> - </MockHookWrapperComponent> - ); - - await act(async () => { - wrapper.find(`[data-test-subj="create-case-submit"]`).first().simulate('click'); - }); - - await waitFor(() => expect(onSubmit).toBeCalled()); - }); - - it('it disables when submitting', async () => { - const wrapper = mount( - <MockHookWrapperComponent> - <SubmitCaseButton /> - </MockHookWrapperComponent> - ); - - await waitFor(() => { - wrapper.find(`[data-test-subj="create-case-submit"]`).first().simulate('click'); - expect( - wrapper.find(`[data-test-subj="create-case-submit"]`).first().prop('isDisabled') - ).toBeTruthy(); - }); - }); - - it('it is loading when submitting', async () => { - const wrapper = mount( - <MockHookWrapperComponent> - <SubmitCaseButton /> - </MockHookWrapperComponent> - ); - - await waitFor(() => { - wrapper.find(`[data-test-subj="create-case-submit"]`).first().simulate('click'); - expect( - wrapper.find(`[data-test-subj="create-case-submit"]`).first().prop('isLoading') - ).toBeTruthy(); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/create/submit_button.tsx b/x-pack/plugins/cases/public/components/create/submit_button.tsx deleted file mode 100644 index b5e58517e6ec1..0000000000000 --- a/x-pack/plugins/cases/public/components/create/submit_button.tsx +++ /dev/null @@ -1,31 +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, { memo } from 'react'; -import { EuiButton } from '@elastic/eui'; - -import { useFormContext } from '../../common/shared_imports'; -import * as i18n from './translations'; - -const SubmitCaseButtonComponent: React.FC = () => { - const { submit, isSubmitting } = useFormContext(); - - return ( - <EuiButton - data-test-subj="create-case-submit" - fill - iconType="plusInCircle" - isDisabled={isSubmitting} - isLoading={isSubmitting} - onClick={submit} - > - {i18n.CREATE_CASE} - </EuiButton> - ); -}; - -export const SubmitCaseButton = memo(SubmitCaseButtonComponent); diff --git a/x-pack/plugins/cases/public/components/create/sync_alerts_toggle.test.tsx b/x-pack/plugins/cases/public/components/create/sync_alerts_toggle.test.tsx deleted file mode 100644 index b4a37f0abb518..0000000000000 --- a/x-pack/plugins/cases/public/components/create/sync_alerts_toggle.test.tsx +++ /dev/null @@ -1,79 +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 from 'react'; -import { mount } from 'enzyme'; -import { waitFor } from '@testing-library/react'; - -import { useForm, Form, FormHook } from '../../common/shared_imports'; -import { SyncAlertsToggle } from './sync_alerts_toggle'; -import { schema, FormProps } from './schema'; - -describe('SyncAlertsToggle', () => { - let globalForm: FormHook; - - const MockHookWrapperComponent: React.FC = ({ children }) => { - const { form } = useForm<FormProps>({ - defaultValue: { syncAlerts: true }, - schema: { - syncAlerts: schema.syncAlerts, - }, - }); - - globalForm = form; - - return <Form form={form}>{children}</Form>; - }; - - beforeEach(() => { - jest.resetAllMocks(); - }); - - it('it renders', async () => { - const wrapper = mount( - <MockHookWrapperComponent> - <SyncAlertsToggle isLoading={false} /> - </MockHookWrapperComponent> - ); - - expect(wrapper.find(`[data-test-subj="caseSyncAlerts"]`).exists()).toBeTruthy(); - }); - - it('it toggles the switch', async () => { - const wrapper = mount( - <MockHookWrapperComponent> - <SyncAlertsToggle isLoading={false} /> - </MockHookWrapperComponent> - ); - - wrapper.find('[data-test-subj="caseSyncAlerts"] button').first().simulate('click'); - - await waitFor(() => { - expect(globalForm.getFormData()).toEqual({ syncAlerts: false }); - }); - }); - - it('it shows the correct labels', async () => { - const wrapper = mount( - <MockHookWrapperComponent> - <SyncAlertsToggle isLoading={false} /> - </MockHookWrapperComponent> - ); - - expect(wrapper.find(`[data-test-subj="caseSyncAlerts"] .euiSwitch__label`).first().text()).toBe( - 'On' - ); - - wrapper.find('[data-test-subj="caseSyncAlerts"] button').first().simulate('click'); - - await waitFor(() => { - expect( - wrapper.find(`[data-test-subj="caseSyncAlerts"] .euiSwitch__label`).first().text() - ).toBe('Off'); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/create/sync_alerts_toggle.tsx b/x-pack/plugins/cases/public/components/create/sync_alerts_toggle.tsx deleted file mode 100644 index bed8e6d18f5e3..0000000000000 --- a/x-pack/plugins/cases/public/components/create/sync_alerts_toggle.tsx +++ /dev/null @@ -1,38 +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, { memo } from 'react'; -import { Field, getUseField, useFormData } from '../../common/shared_imports'; -import * as i18n from './translations'; - -const CommonUseField = getUseField({ component: Field }); - -interface Props { - isLoading: boolean; -} - -const SyncAlertsToggleComponent: React.FC<Props> = ({ isLoading }) => { - const [{ syncAlerts }] = useFormData({ watch: ['syncAlerts'] }); - return ( - <CommonUseField - path="syncAlerts" - componentProps={{ - idAria: 'caseSyncAlerts', - 'data-test-subj': 'caseSyncAlerts', - label: i18n.SYNC_ALERTS_LABEL, - euiFieldProps: { - disabled: isLoading, - label: syncAlerts ? i18n.SYNC_ALERTS_SWITCH_LABEL_ON : i18n.SYNC_ALERTS_SWITCH_LABEL_OFF, - }, - }} - /> - ); -}; - -SyncAlertsToggleComponent.displayName = 'SyncAlertsToggleComponent'; - -export const SyncAlertsToggle = memo(SyncAlertsToggleComponent); diff --git a/x-pack/plugins/cases/public/components/create/tags.test.tsx b/x-pack/plugins/cases/public/components/create/tags.test.tsx deleted file mode 100644 index 2eddb83dcac29..0000000000000 --- a/x-pack/plugins/cases/public/components/create/tags.test.tsx +++ /dev/null @@ -1,79 +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 from 'react'; -import { mount } from 'enzyme'; -import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; -import { waitFor } from '@testing-library/react'; - -import { useForm, Form, FormHook } from '../../common/shared_imports'; -import { useGetTags } from '../../containers/use_get_tags'; -import { Tags } from './tags'; -import { schema, FormProps } from './schema'; - -jest.mock('../../containers/use_get_tags'); -const useGetTagsMock = useGetTags as jest.Mock; - -describe('Tags', () => { - let globalForm: FormHook; - - const MockHookWrapperComponent: React.FC = ({ children }) => { - const { form } = useForm<FormProps>({ - defaultValue: { tags: [] }, - schema: { - tags: schema.tags, - }, - }); - - globalForm = form; - - return <Form form={form}>{children}</Form>; - }; - - beforeEach(() => { - jest.resetAllMocks(); - useGetTagsMock.mockReturnValue({ tags: ['test'] }); - }); - - it('it renders', async () => { - const wrapper = mount( - <MockHookWrapperComponent> - <Tags isLoading={false} /> - </MockHookWrapperComponent> - ); - - await waitFor(() => { - expect(wrapper.find(`[data-test-subj="caseTags"]`).exists()).toBeTruthy(); - }); - }); - - it('it disables the input when loading', async () => { - const wrapper = mount( - <MockHookWrapperComponent> - <Tags isLoading={true} /> - </MockHookWrapperComponent> - ); - - expect(wrapper.find(EuiComboBox).prop('disabled')).toBeTruthy(); - }); - - it('it changes the tags', async () => { - const wrapper = mount( - <MockHookWrapperComponent> - <Tags isLoading={false} /> - </MockHookWrapperComponent> - ); - - await waitFor(() => { - ((wrapper.find(EuiComboBox).props() as unknown) as { - onChange: (a: EuiComboBoxOptionOption[]) => void; - }).onChange(['test', 'case'].map((tag) => ({ label: tag }))); - }); - - expect(globalForm.getFormData()).toEqual({ tags: ['test', 'case'] }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/create/tags.tsx b/x-pack/plugins/cases/public/components/create/tags.tsx deleted file mode 100644 index ac0b67529e15a..0000000000000 --- a/x-pack/plugins/cases/public/components/create/tags.tsx +++ /dev/null @@ -1,49 +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, { memo, useMemo } from 'react'; - -import { Field, getUseField } from '../../common/shared_imports'; -import { useGetTags } from '../../containers/use_get_tags'; - -const CommonUseField = getUseField({ component: Field }); - -interface Props { - isLoading: boolean; -} - -const TagsComponent: React.FC<Props> = ({ isLoading }) => { - const { tags: tagOptions, isLoading: isLoadingTags } = useGetTags(); - const options = useMemo( - () => - tagOptions.map((label) => ({ - label, - })), - [tagOptions] - ); - - return ( - <CommonUseField - path="tags" - componentProps={{ - idAria: 'caseTags', - 'data-test-subj': 'caseTags', - euiFieldProps: { - fullWidth: true, - placeholder: '', - disabled: isLoading || isLoadingTags, - options, - noSuggestions: false, - }, - }} - /> - ); -}; - -TagsComponent.displayName = 'TagsComponent'; - -export const Tags = memo(TagsComponent); diff --git a/x-pack/plugins/cases/public/components/create/title.test.tsx b/x-pack/plugins/cases/public/components/create/title.test.tsx deleted file mode 100644 index a41d5afbb4038..0000000000000 --- a/x-pack/plugins/cases/public/components/create/title.test.tsx +++ /dev/null @@ -1,72 +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 from 'react'; -import { mount } from 'enzyme'; -import { act } from '@testing-library/react'; - -import { useForm, Form, FormHook } from '../../common/shared_imports'; -import { Title } from './title'; -import { schema, FormProps } from './schema'; - -describe('Title', () => { - let globalForm: FormHook; - - const MockHookWrapperComponent: React.FC = ({ children }) => { - const { form } = useForm<FormProps>({ - defaultValue: { title: 'My title' }, - schema: { - title: schema.title, - }, - }); - - globalForm = form; - - return <Form form={form}>{children}</Form>; - }; - - beforeEach(() => { - jest.resetAllMocks(); - }); - - it('it renders', async () => { - const wrapper = mount( - <MockHookWrapperComponent> - <Title isLoading={false} /> - </MockHookWrapperComponent> - ); - - expect(wrapper.find(`[data-test-subj="caseTitle"]`).exists()).toBeTruthy(); - }); - - it('it disables the input when loading', async () => { - const wrapper = mount( - <MockHookWrapperComponent> - <Title isLoading={true} /> - </MockHookWrapperComponent> - ); - - expect(wrapper.find(`[data-test-subj="caseTitle"] input`).prop('disabled')).toBeTruthy(); - }); - - it('it changes the title', async () => { - const wrapper = mount( - <MockHookWrapperComponent> - <Title isLoading={false} /> - </MockHookWrapperComponent> - ); - - await act(async () => { - wrapper - .find(`[data-test-subj="caseTitle"] input`) - .first() - .simulate('change', { target: { value: 'My new title' } }); - }); - - expect(globalForm.getFormData()).toEqual({ title: 'My new title' }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/create/title.tsx b/x-pack/plugins/cases/public/components/create/title.tsx deleted file mode 100644 index cc51a805b5c38..0000000000000 --- a/x-pack/plugins/cases/public/components/create/title.tsx +++ /dev/null @@ -1,33 +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, { memo } from 'react'; -import { Field, getUseField } from '../../common/shared_imports'; - -const CommonUseField = getUseField({ component: Field }); - -interface Props { - isLoading: boolean; -} - -const TitleComponent: React.FC<Props> = ({ isLoading }) => ( - <CommonUseField - path="title" - componentProps={{ - idAria: 'caseTitle', - 'data-test-subj': 'caseTitle', - euiFieldProps: { - fullWidth: true, - disabled: isLoading, - }, - }} - /> -); - -TitleComponent.displayName = 'TitleComponent'; - -export const Title = memo(TitleComponent); diff --git a/x-pack/plugins/cases/public/components/create/translations.ts b/x-pack/plugins/cases/public/components/create/translations.ts deleted file mode 100644 index 7e0f7e5a6b9d5..0000000000000 --- a/x-pack/plugins/cases/public/components/create/translations.ts +++ /dev/null @@ -1,26 +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 { i18n } from '@kbn/i18n'; - -export * from '../../common/translations'; - -export const STEP_ONE_TITLE = i18n.translate('xpack.cases.create.stepOneTitle', { - defaultMessage: 'Case fields', -}); - -export const STEP_TWO_TITLE = i18n.translate('xpack.cases.create.stepTwoTitle', { - defaultMessage: 'Case settings', -}); - -export const STEP_THREE_TITLE = i18n.translate('xpack.cases.create.stepThreeTitle', { - defaultMessage: 'External Connector Fields', -}); - -export const SYNC_ALERTS_LABEL = i18n.translate('xpack.cases.create.syncAlertsLabel', { - defaultMessage: 'Sync alert status with case status', -}); diff --git a/x-pack/plugins/cases/public/components/markdown_editor/editor.tsx b/x-pack/plugins/cases/public/components/markdown_editor/editor.tsx deleted file mode 100644 index a7d37fdda3085..0000000000000 --- a/x-pack/plugins/cases/public/components/markdown_editor/editor.tsx +++ /dev/null @@ -1,66 +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, { memo, useEffect, useState, useCallback } from 'react'; -import { - EuiMarkdownEditor, - getDefaultEuiMarkdownParsingPlugins, - getDefaultEuiMarkdownProcessingPlugins, - getDefaultEuiMarkdownUiPlugins, -} from '@elastic/eui'; - -interface MarkdownEditorProps { - onChange: (content: string) => void; - value: string; - ariaLabel: string; - editorId?: string; - dataTestSubj?: string; - height?: number; -} - -// create plugin stuff here -export const { uiPlugins, parsingPlugins, processingPlugins } = { - uiPlugins: getDefaultEuiMarkdownUiPlugins(), - parsingPlugins: getDefaultEuiMarkdownParsingPlugins(), - processingPlugins: getDefaultEuiMarkdownProcessingPlugins(), -}; -const MarkdownEditorComponent: React.FC<MarkdownEditorProps> = ({ - onChange, - value, - ariaLabel, - editorId, - dataTestSubj, - height, -}) => { - const [markdownErrorMessages, setMarkdownErrorMessages] = useState([]); - const onParse = useCallback((err, { messages }) => { - setMarkdownErrorMessages(err ? [err] : messages); - }, []); - - useEffect( - () => document.querySelector<HTMLElement>('textarea.euiMarkdownEditorTextArea')?.focus(), - [] - ); - - return ( - <EuiMarkdownEditor - aria-label={ariaLabel} - editorId={editorId} - onChange={onChange} - value={value} - uiPlugins={uiPlugins} - parsingPluginList={parsingPlugins} - processingPluginList={processingPlugins} - onParse={onParse} - errors={markdownErrorMessages} - data-test-subj={dataTestSubj} - height={height} - /> - ); -}; - -export const MarkdownEditor = memo(MarkdownEditorComponent); diff --git a/x-pack/plugins/cases/public/components/markdown_editor/eui_form.tsx b/x-pack/plugins/cases/public/components/markdown_editor/eui_form.tsx deleted file mode 100644 index 858e79ff65baf..0000000000000 --- a/x-pack/plugins/cases/public/components/markdown_editor/eui_form.tsx +++ /dev/null @@ -1,66 +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 from 'react'; -import styled from 'styled-components'; -import { EuiMarkdownEditorProps, EuiFormRow, EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; -import { FieldHook, getFieldValidityAndErrorMessage } from '../../common/shared_imports'; - -import { MarkdownEditor } from './editor'; - -type MarkdownEditorFormProps = EuiMarkdownEditorProps & { - id: string; - field: FieldHook; - dataTestSubj: string; - idAria: string; - isDisabled?: boolean; - bottomRightContent?: React.ReactNode; -}; - -const BottomContentWrapper = styled(EuiFlexGroup)` - ${({ theme }) => ` - padding: ${theme.eui.ruleMargins.marginSmall} 0; - `} -`; - -export const MarkdownEditorForm: React.FC<MarkdownEditorFormProps> = ({ - id, - field, - dataTestSubj, - idAria, - bottomRightContent, -}) => { - const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(field); - - return ( - <EuiFormRow - data-test-subj={dataTestSubj} - describedByIds={idAria ? [idAria] : undefined} - error={errorMessage} - fullWidth - helpText={field.helpText} - isInvalid={isInvalid} - label={field.label} - labelAppend={field.labelAppend} - > - <> - <MarkdownEditor - ariaLabel={idAria} - editorId={id} - onChange={field.setValue} - value={field.value as string} - data-test-subj={`${dataTestSubj}-markdown-editor`} - /> - {bottomRightContent && ( - <BottomContentWrapper justifyContent={'flexEnd'}> - <EuiFlexItem grow={false}>{bottomRightContent}</EuiFlexItem> - </BottomContentWrapper> - )} - </> - </EuiFormRow> - ); -}; diff --git a/x-pack/plugins/cases/public/components/markdown_editor/index.tsx b/x-pack/plugins/cases/public/components/markdown_editor/index.tsx deleted file mode 100644 index e77a36d48f7d9..0000000000000 --- a/x-pack/plugins/cases/public/components/markdown_editor/index.tsx +++ /dev/null @@ -1,11 +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. - */ - -export * from './types'; -export * from './renderer'; -export * from './editor'; -export * from './eui_form'; diff --git a/x-pack/plugins/cases/public/components/markdown_editor/markdown_link.tsx b/x-pack/plugins/cases/public/components/markdown_editor/markdown_link.tsx deleted file mode 100644 index 7cc8a07c8c04e..0000000000000 --- a/x-pack/plugins/cases/public/components/markdown_editor/markdown_link.tsx +++ /dev/null @@ -1,35 +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, { memo } from 'react'; -import { EuiLink, EuiLinkAnchorProps, EuiToolTip } from '@elastic/eui'; - -type MarkdownLinkProps = { disableLinks?: boolean } & EuiLinkAnchorProps; - -/** prevents search engine manipulation by noting the linked document is not trusted or endorsed by us */ -const REL_NOFOLLOW = 'nofollow'; - -const MarkdownLinkComponent: React.FC<MarkdownLinkProps> = ({ - disableLinks, - href, - target, - children, - ...props -}) => ( - <EuiToolTip content={href}> - <EuiLink - href={disableLinks ? undefined : href} - data-test-subj="markdown-link" - rel={`${REL_NOFOLLOW}`} - target="_blank" - > - {children} - </EuiLink> - </EuiToolTip> -); - -export const MarkdownLink = memo(MarkdownLinkComponent); diff --git a/x-pack/plugins/cases/public/components/markdown_editor/renderer.test.tsx b/x-pack/plugins/cases/public/components/markdown_editor/renderer.test.tsx deleted file mode 100644 index 5d299529561ba..0000000000000 --- a/x-pack/plugins/cases/public/components/markdown_editor/renderer.test.tsx +++ /dev/null @@ -1,63 +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 from 'react'; -import { mount } from 'enzyme'; - -import { removeExternalLinkText } from '../../common/test_utils'; -import { MarkdownRenderer } from './renderer'; - -describe('Markdown', () => { - describe('markdown links', () => { - const markdownWithLink = 'A link to an external site [External Site](https://google.com)'; - - test('it renders the expected link text', () => { - const wrapper = mount(<MarkdownRenderer>{markdownWithLink}</MarkdownRenderer>); - - expect( - removeExternalLinkText(wrapper.find('[data-test-subj="markdown-link"]').first().text()) - ).toEqual('External Site'); - }); - - test('it renders the expected href', () => { - const wrapper = mount(<MarkdownRenderer>{markdownWithLink}</MarkdownRenderer>); - - expect(wrapper.find('[data-test-subj="markdown-link"]').first().getDOMNode()).toHaveProperty( - 'href', - 'https://google.com/' - ); - }); - - test('it does NOT render the href if links are disabled', () => { - const wrapper = mount( - <MarkdownRenderer disableLinks={true}>{markdownWithLink}</MarkdownRenderer> - ); - - expect( - wrapper.find('[data-test-subj="markdown-link"]').first().getDOMNode() - ).not.toHaveProperty('href'); - }); - - test('it opens links in a new tab via target="_blank"', () => { - const wrapper = mount(<MarkdownRenderer>{markdownWithLink}</MarkdownRenderer>); - - expect(wrapper.find('[data-test-subj="markdown-link"]').first().getDOMNode()).toHaveProperty( - 'target', - '_blank' - ); - }); - - test('it sets the link `rel` attribute to `noopener` to prevent the new page from accessing `window.opener`, `nofollow` to note the link is not endorsed by us, and noreferrer to prevent the browser from sending the current address', () => { - const wrapper = mount(<MarkdownRenderer>{markdownWithLink}</MarkdownRenderer>); - - expect(wrapper.find('[data-test-subj="markdown-link"]').first().getDOMNode()).toHaveProperty( - 'rel', - 'nofollow noopener noreferrer' - ); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/markdown_editor/renderer.tsx b/x-pack/plugins/cases/public/components/markdown_editor/renderer.tsx deleted file mode 100644 index c321c794c1e77..0000000000000 --- a/x-pack/plugins/cases/public/components/markdown_editor/renderer.tsx +++ /dev/null @@ -1,41 +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, { memo, useMemo } from 'react'; -import { cloneDeep } from 'lodash/fp'; -import { EuiMarkdownFormat, EuiLinkAnchorProps } from '@elastic/eui'; - -import { parsingPlugins, processingPlugins } from './'; -import { MarkdownLink } from './markdown_link'; - -interface Props { - children: string; - disableLinks?: boolean; -} - -const MarkdownRendererComponent: React.FC<Props> = ({ children, disableLinks }) => { - const MarkdownLinkProcessingComponent: React.FC<EuiLinkAnchorProps> = useMemo( - () => (props) => <MarkdownLink {...props} disableLinks={disableLinks} />, - [disableLinks] - ); - - // Deep clone of the processing plugins to prevent affecting the markdown editor. - const processingPluginList = cloneDeep(processingPlugins); - // This line of code is TS-compatible and it will break if [1][1] change in the future. - processingPluginList[1][1].components.a = MarkdownLinkProcessingComponent; - - return ( - <EuiMarkdownFormat - parsingPluginList={parsingPlugins} - processingPluginList={processingPluginList} - > - {children} - </EuiMarkdownFormat> - ); -}; - -export const MarkdownRenderer = memo(MarkdownRendererComponent); diff --git a/x-pack/plugins/cases/public/components/markdown_editor/translations.ts b/x-pack/plugins/cases/public/components/markdown_editor/translations.ts deleted file mode 100644 index 365738f53ef8a..0000000000000 --- a/x-pack/plugins/cases/public/components/markdown_editor/translations.ts +++ /dev/null @@ -1,19 +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 { i18n } from '@kbn/i18n'; - -export const MARKDOWN_SYNTAX_HELP = i18n.translate('xpack.cases.markdownEditor.markdownInputHelp', { - defaultMessage: 'Markdown syntax help', -}); - -export const MARKDOWN = i18n.translate('xpack.cases.markdownEditor.markdown', { - defaultMessage: 'Markdown', -}); -export const PREVIEW = i18n.translate('xpack.cases.markdownEditor.preview', { - defaultMessage: 'Preview', -}); diff --git a/x-pack/plugins/cases/public/components/markdown_editor/types.ts b/x-pack/plugins/cases/public/components/markdown_editor/types.ts deleted file mode 100644 index 8a30a4a143f54..0000000000000 --- a/x-pack/plugins/cases/public/components/markdown_editor/types.ts +++ /dev/null @@ -1,11 +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. - */ - -export interface CursorPosition { - start: number; - end: number; -} diff --git a/x-pack/plugins/cases/public/components/status/button.test.tsx b/x-pack/plugins/cases/public/components/status/button.test.tsx deleted file mode 100644 index a4d4a53ff4a62..0000000000000 --- a/x-pack/plugins/cases/public/components/status/button.test.tsx +++ /dev/null @@ -1,90 +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 from 'react'; -import { mount } from 'enzyme'; - -import { CaseStatuses } from '../../../common'; -import { StatusActionButton } from './button'; - -describe('StatusActionButton', () => { - const onStatusChanged = jest.fn(); - const defaultProps = { - status: CaseStatuses.open, - disabled: false, - isLoading: false, - onStatusChanged, - }; - - it('it renders', async () => { - const wrapper = mount(<StatusActionButton {...defaultProps} />); - - expect(wrapper.find(`[data-test-subj="case-view-status-action-button"]`).exists()).toBeTruthy(); - }); - - describe('Button icons', () => { - it('it renders the correct button icon: status open', () => { - const wrapper = mount(<StatusActionButton {...defaultProps} />); - - expect( - wrapper.find(`[data-test-subj="case-view-status-action-button"]`).first().prop('iconType') - ).toBe('folderExclamation'); - }); - - it('it renders the correct button icon: status in-progress', () => { - const wrapper = mount( - <StatusActionButton {...defaultProps} status={CaseStatuses['in-progress']} /> - ); - - expect( - wrapper.find(`[data-test-subj="case-view-status-action-button"]`).first().prop('iconType') - ).toBe('folderCheck'); - }); - - it('it renders the correct button icon: status closed', () => { - const wrapper = mount(<StatusActionButton {...defaultProps} status={CaseStatuses.closed} />); - - expect( - wrapper.find(`[data-test-subj="case-view-status-action-button"]`).first().prop('iconType') - ).toBe('folderOpen'); - }); - }); - - describe('Status rotation', () => { - it('rotates correctly to in-progress when status is open', () => { - const wrapper = mount(<StatusActionButton {...defaultProps} />); - - wrapper - .find(`button[data-test-subj="case-view-status-action-button"]`) - .first() - .simulate('click'); - expect(onStatusChanged).toHaveBeenCalledWith('in-progress'); - }); - - it('rotates correctly to closed when status is in-progress', () => { - const wrapper = mount( - <StatusActionButton {...defaultProps} status={CaseStatuses['in-progress']} /> - ); - - wrapper - .find(`button[data-test-subj="case-view-status-action-button"]`) - .first() - .simulate('click'); - expect(onStatusChanged).toHaveBeenCalledWith('closed'); - }); - - it('rotates correctly to open when status is closed', () => { - const wrapper = mount(<StatusActionButton {...defaultProps} status={CaseStatuses.closed} />); - - wrapper - .find(`button[data-test-subj="case-view-status-action-button"]`) - .first() - .simulate('click'); - expect(onStatusChanged).toHaveBeenCalledWith('open'); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/status/button.tsx b/x-pack/plugins/cases/public/components/status/button.tsx deleted file mode 100644 index 623afeb43c596..0000000000000 --- a/x-pack/plugins/cases/public/components/status/button.tsx +++ /dev/null @@ -1,52 +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, { memo, useCallback, useMemo } from 'react'; -import { EuiButton } from '@elastic/eui'; - -import { CaseStatuses, caseStatuses } from '../../../common'; -import { statuses } from './config'; - -interface Props { - status: CaseStatuses; - disabled: boolean; - isLoading: boolean; - onStatusChanged: (status: CaseStatuses) => void; -} - -// Rotate over the statuses. open -> in-progress -> closes -> open... -const getNextItem = (item: number) => (item + 1) % caseStatuses.length; - -const StatusActionButtonComponent: React.FC<Props> = ({ - status, - onStatusChanged, - disabled, - isLoading, -}) => { - const indexOfCurrentStatus = useMemo( - () => caseStatuses.findIndex((caseStatus) => caseStatus === status), - [status] - ); - const nextStatusIndex = useMemo(() => getNextItem(indexOfCurrentStatus), [indexOfCurrentStatus]); - - const onClick = useCallback(() => { - onStatusChanged(caseStatuses[nextStatusIndex]); - }, [nextStatusIndex, onStatusChanged]); - - return ( - <EuiButton - data-test-subj="case-view-status-action-button" - iconType={statuses[caseStatuses[nextStatusIndex]].icon} - isDisabled={disabled} - isLoading={isLoading} - onClick={onClick} - > - {statuses[caseStatuses[nextStatusIndex]].button.label} - </EuiButton> - ); -}; -export const StatusActionButton = memo(StatusActionButtonComponent); diff --git a/x-pack/plugins/cases/public/components/status/config.ts b/x-pack/plugins/cases/public/components/status/config.ts deleted file mode 100644 index e85d429067724..0000000000000 --- a/x-pack/plugins/cases/public/components/status/config.ts +++ /dev/null @@ -1,82 +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 { CaseStatuses } from '../../../common'; -import * as i18n from './translations'; -import { AllCaseStatus, Statuses, StatusAll } from './types'; - -export const allCaseStatus: AllCaseStatus = { - [StatusAll]: { color: 'hollow', label: i18n.ALL }, -}; - -export const statuses: Statuses = { - [CaseStatuses.open]: { - color: 'primary', - label: i18n.OPEN, - icon: 'folderOpen' as const, - actions: { - bulk: { - title: i18n.BULK_ACTION_OPEN_SELECTED, - }, - single: { - title: i18n.OPEN_CASE, - }, - }, - actionBar: { - title: i18n.CASE_OPENED, - }, - button: { - label: i18n.REOPEN_CASE, - }, - stats: { - title: i18n.OPEN_CASES, - }, - }, - [CaseStatuses['in-progress']]: { - color: 'warning', - label: i18n.IN_PROGRESS, - icon: 'folderExclamation' as const, - actions: { - bulk: { - title: i18n.BULK_ACTION_MARK_IN_PROGRESS, - }, - single: { - title: i18n.MARK_CASE_IN_PROGRESS, - }, - }, - actionBar: { - title: i18n.CASE_IN_PROGRESS, - }, - button: { - label: i18n.MARK_CASE_IN_PROGRESS, - }, - stats: { - title: i18n.IN_PROGRESS_CASES, - }, - }, - [CaseStatuses.closed]: { - color: 'default', - label: i18n.CLOSED, - icon: 'folderCheck' as const, - actions: { - bulk: { - title: i18n.BULK_ACTION_CLOSE_SELECTED, - }, - single: { - title: i18n.CLOSE_CASE, - }, - }, - actionBar: { - title: i18n.CASE_CLOSED, - }, - button: { - label: i18n.CLOSE_CASE, - }, - stats: { - title: i18n.CLOSED_CASES, - }, - }, -}; diff --git a/x-pack/plugins/cases/public/components/status/index.ts b/x-pack/plugins/cases/public/components/status/index.ts deleted file mode 100644 index 94d7cb6a31830..0000000000000 --- a/x-pack/plugins/cases/public/components/status/index.ts +++ /dev/null @@ -1,11 +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. - */ - -export * from './status'; -export * from './config'; -export * from './stats'; -export * from './types'; diff --git a/x-pack/plugins/cases/public/components/status/stats.test.tsx b/x-pack/plugins/cases/public/components/status/stats.test.tsx deleted file mode 100644 index b2da828da77b0..0000000000000 --- a/x-pack/plugins/cases/public/components/status/stats.test.tsx +++ /dev/null @@ -1,66 +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 from 'react'; -import { mount } from 'enzyme'; - -import { CaseStatuses } from '../../../common'; -import { Stats } from './stats'; - -describe('Stats', () => { - const defaultProps = { - caseStatus: CaseStatuses.open, - caseCount: 2, - isLoading: false, - dataTestSubj: 'test-stats', - }; - it('it renders', async () => { - const wrapper = mount(<Stats {...defaultProps} />); - - expect(wrapper.find(`[data-test-subj="test-stats"]`).exists()).toBeTruthy(); - }); - - it('shows the count', async () => { - const wrapper = mount(<Stats {...defaultProps} />); - - expect( - wrapper.find(`[data-test-subj="test-stats"] .euiDescriptionList__description`).first().text() - ).toBe('2'); - }); - - it('shows the loading spinner', async () => { - const wrapper = mount(<Stats {...defaultProps} isLoading={true} />); - - expect(wrapper.find(`[data-test-subj="test-stats-loading-spinner"]`).exists()).toBeTruthy(); - }); - - describe('Status title', () => { - it('shows the correct title for status open', async () => { - const wrapper = mount(<Stats {...defaultProps} />); - - expect( - wrapper.find(`[data-test-subj="test-stats"] .euiDescriptionList__title`).first().text() - ).toBe('Open cases'); - }); - - it('shows the correct title for status in-progress', async () => { - const wrapper = mount(<Stats {...defaultProps} caseStatus={CaseStatuses['in-progress']} />); - - expect( - wrapper.find(`[data-test-subj="test-stats"] .euiDescriptionList__title`).first().text() - ).toBe('In progress cases'); - }); - - it('shows the correct title for status closed', async () => { - const wrapper = mount(<Stats {...defaultProps} caseStatus={CaseStatuses.closed} />); - - expect( - wrapper.find(`[data-test-subj="test-stats"] .euiDescriptionList__title`).first().text() - ).toBe('Closed cases'); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/status/stats.tsx b/x-pack/plugins/cases/public/components/status/stats.tsx deleted file mode 100644 index 071ea43746fdc..0000000000000 --- a/x-pack/plugins/cases/public/components/status/stats.tsx +++ /dev/null @@ -1,40 +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, { memo, useMemo } from 'react'; -import { EuiDescriptionList, EuiLoadingSpinner } from '@elastic/eui'; -import { CaseStatuses } from '../../../common'; -import { statuses } from './config'; - -export interface Props { - caseCount: number | null; - caseStatus: CaseStatuses; - isLoading: boolean; - dataTestSubj?: string; -} - -const StatsComponent: React.FC<Props> = ({ caseCount, caseStatus, isLoading, dataTestSubj }) => { - const statusStats = useMemo( - () => [ - { - title: statuses[caseStatus].stats.title, - description: isLoading ? ( - <EuiLoadingSpinner data-test-subj={`${dataTestSubj}-loading-spinner`} /> - ) : ( - caseCount ?? 'N/A' - ), - }, - ], - [caseCount, caseStatus, dataTestSubj, isLoading] - ); - return ( - <EuiDescriptionList data-test-subj={dataTestSubj} textStyle="reverse" listItems={statusStats} /> - ); -}; - -StatsComponent.displayName = 'StatsComponent'; -export const Stats = memo(StatsComponent); diff --git a/x-pack/plugins/cases/public/components/status/status.test.tsx b/x-pack/plugins/cases/public/components/status/status.test.tsx deleted file mode 100644 index 7cddbf5ca4a1d..0000000000000 --- a/x-pack/plugins/cases/public/components/status/status.test.tsx +++ /dev/null @@ -1,72 +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 from 'react'; -import { mount } from 'enzyme'; - -import { CaseStatuses } from '../../../common'; -import { Status } from './status'; - -describe('Stats', () => { - const onClick = jest.fn(); - - it('it renders', async () => { - const wrapper = mount(<Status type={CaseStatuses.open} withArrow={false} onClick={onClick} />); - - expect(wrapper.find(`[data-test-subj="status-badge-open"]`).exists()).toBeTruthy(); - expect( - wrapper.find(`[data-test-subj="status-badge-open"] .euiBadge__iconButton`).exists() - ).toBeFalsy(); - }); - - it('it renders with arrow', async () => { - const wrapper = mount(<Status type={CaseStatuses.open} withArrow={true} onClick={onClick} />); - - expect( - wrapper.find(`[data-test-subj="status-badge-open"] .euiBadge__iconButton`).exists() - ).toBeTruthy(); - }); - - it('it calls onClick when pressing the badge', async () => { - const wrapper = mount(<Status type={CaseStatuses.open} withArrow={true} onClick={onClick} />); - - wrapper.find(`[data-test-subj="status-badge-open"] .euiBadge__iconButton`).simulate('click'); - expect(onClick).toHaveBeenCalled(); - }); - - describe('Colors', () => { - it('shows the correct color when status is open', async () => { - const wrapper = mount( - <Status type={CaseStatuses.open} withArrow={false} onClick={onClick} /> - ); - - expect(wrapper.find(`[data-test-subj="status-badge-open"]`).first().prop('color')).toBe( - 'primary' - ); - }); - - it('shows the correct color when status is in-progress', async () => { - const wrapper = mount( - <Status type={CaseStatuses['in-progress']} withArrow={false} onClick={onClick} /> - ); - - expect( - wrapper.find(`[data-test-subj="status-badge-in-progress"]`).first().prop('color') - ).toBe('warning'); - }); - - it('shows the correct color when status is closed', async () => { - const wrapper = mount( - <Status type={CaseStatuses.closed} withArrow={false} onClick={onClick} /> - ); - - expect(wrapper.find(`[data-test-subj="status-badge-closed"]`).first().prop('color')).toBe( - 'default' - ); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/status/status.tsx b/x-pack/plugins/cases/public/components/status/status.tsx deleted file mode 100644 index de4c979daf4c1..0000000000000 --- a/x-pack/plugins/cases/public/components/status/status.tsx +++ /dev/null @@ -1,43 +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, { memo, useMemo } from 'react'; -import { noop } from 'lodash/fp'; -import { EuiBadge } from '@elastic/eui'; - -import { allCaseStatus, statuses } from './config'; -import { CaseStatusWithAllStatus, StatusAll } from './types'; -import * as i18n from './translations'; - -interface Props { - type: CaseStatusWithAllStatus; - withArrow?: boolean; - onClick?: () => void; -} - -const StatusComponent: React.FC<Props> = ({ type, withArrow = false, onClick = noop }) => { - const props = useMemo( - () => ({ - color: type === StatusAll ? allCaseStatus[StatusAll].color : statuses[type].color, - ...(withArrow ? { iconType: 'arrowDown', iconSide: 'right' as const } : {}), - }), - [withArrow, type] - ); - - return ( - <EuiBadge - {...props} - iconOnClick={onClick} - iconOnClickAriaLabel={i18n.STATUS_ICON_ARIA} - data-test-subj={`status-badge-${type}`} - > - {type === StatusAll ? allCaseStatus[StatusAll].label : statuses[type].label} - </EuiBadge> - ); -}; - -export const Status = memo(StatusComponent); diff --git a/x-pack/plugins/cases/public/components/status/translations.ts b/x-pack/plugins/cases/public/components/status/translations.ts deleted file mode 100644 index b3eadfd681ba5..0000000000000 --- a/x-pack/plugins/cases/public/components/status/translations.ts +++ /dev/null @@ -1,69 +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 { i18n } from '@kbn/i18n'; -export * from '../../common/translations'; - -export const ALL = i18n.translate('xpack.cases.status.all', { - defaultMessage: 'All', -}); - -export const OPEN = i18n.translate('xpack.cases.status.open', { - defaultMessage: 'Open', -}); - -export const IN_PROGRESS = i18n.translate('xpack.cases.status.inProgress', { - defaultMessage: 'In progress', -}); - -export const CLOSED = i18n.translate('xpack.cases.status.closed', { - defaultMessage: 'Closed', -}); - -export const STATUS_ICON_ARIA = i18n.translate('xpack.cases.status.iconAria', { - defaultMessage: 'Change status', -}); - -export const CASE_OPENED = i18n.translate('xpack.cases.caseView.caseOpened', { - defaultMessage: 'Case opened', -}); - -export const CASE_IN_PROGRESS = i18n.translate('xpack.cases.caseView.caseInProgress', { - defaultMessage: 'Case in progress', -}); - -export const CASE_CLOSED = i18n.translate('xpack.cases.caseView.caseClosed', { - defaultMessage: 'Case closed', -}); - -export const BULK_ACTION_CLOSE_SELECTED = i18n.translate( - 'xpack.cases.caseTable.bulkActions.closeSelectedTitle', - { - defaultMessage: 'Close selected', - } -); - -export const BULK_ACTION_OPEN_SELECTED = i18n.translate( - 'xpack.cases.caseTable.bulkActions.openSelectedTitle', - { - defaultMessage: 'Open selected', - } -); - -export const BULK_ACTION_DELETE_SELECTED = i18n.translate( - 'xpack.cases.caseTable.bulkActions.deleteSelectedTitle', - { - defaultMessage: 'Delete selected', - } -); - -export const BULK_ACTION_MARK_IN_PROGRESS = i18n.translate( - 'xpack.cases.caseTable.bulkActions.markInProgressTitle', - { - defaultMessage: 'Mark in progress', - } -); diff --git a/x-pack/plugins/cases/public/components/status/types.ts b/x-pack/plugins/cases/public/components/status/types.ts deleted file mode 100644 index 674838067b0ac..0000000000000 --- a/x-pack/plugins/cases/public/components/status/types.ts +++ /dev/null @@ -1,43 +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 { EuiIconType } from '@elastic/eui/src/components/icon/icon'; -import { CaseStatuses } from '../../../common'; - -export const StatusAll = 'all' as const; -type StatusAllType = typeof StatusAll; - -export type CaseStatusWithAllStatus = CaseStatuses | StatusAllType; - -export type AllCaseStatus = Record<StatusAllType, { color: string; label: string }>; - -export type Statuses = Record< - CaseStatuses, - { - color: string; - label: string; - icon: EuiIconType; - actions: { - bulk: { - title: string; - }; - single: { - title: string; - description?: string; - }; - }; - actionBar: { - title: string; - }; - button: { - label: string; - }; - stats: { - title: string; - }; - } ->; diff --git a/x-pack/plugins/cases/public/components/toasters/__snapshots__/modal_all_errors.test.tsx.snap b/x-pack/plugins/cases/public/components/toasters/__snapshots__/modal_all_errors.test.tsx.snap deleted file mode 100644 index 5e008e28073de..0000000000000 --- a/x-pack/plugins/cases/public/components/toasters/__snapshots__/modal_all_errors.test.tsx.snap +++ /dev/null @@ -1,48 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Modal all errors rendering it renders the default all errors modal when isShowing is positive 1`] = ` -<EuiModal - onClose={[Function]} -> - <EuiModalHeader> - <EuiModalHeaderTitle> - Your visualization has error(s) - </EuiModalHeaderTitle> - </EuiModalHeader> - <EuiModalBody> - <EuiCallOut - color="danger" - iconType="alert" - size="s" - title="Test & Test" - /> - <EuiSpacer - size="s" - /> - <EuiAccordion - arrowDisplay="left" - buttonContent="Error 1, Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt u ..." - data-test-subj="modal-all-errors-accordion" - id="accordion1" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - key="id-super-id-0" - paddingSize="none" - > - <MyEuiCodeBlock> - Error 1, Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. - </MyEuiCodeBlock> - </EuiAccordion> - </EuiModalBody> - <EuiModalFooter> - <EuiButton - data-test-subj="modal-all-errors-close" - fill={true} - onClick={[Function]} - > - Close - </EuiButton> - </EuiModalFooter> -</EuiModal> -`; diff --git a/x-pack/plugins/cases/public/components/toasters/errors.ts b/x-pack/plugins/cases/public/components/toasters/errors.ts deleted file mode 100644 index 0a672aeee8b7c..0000000000000 --- a/x-pack/plugins/cases/public/components/toasters/errors.ts +++ /dev/null @@ -1,19 +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. - */ - -export class ToasterError extends Error { - public readonly messages: string[]; - - constructor(messages: string[]) { - super(messages[0]); - this.name = 'ToasterError'; - this.messages = messages; - } -} - -export const isToasterError = (error: unknown): error is ToasterError => - error instanceof ToasterError; diff --git a/x-pack/plugins/cases/public/components/toasters/index.test.tsx b/x-pack/plugins/cases/public/components/toasters/index.test.tsx deleted file mode 100644 index 1d78570e18a59..0000000000000 --- a/x-pack/plugins/cases/public/components/toasters/index.test.tsx +++ /dev/null @@ -1,307 +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 { set } from '@elastic/safer-lodash-set/fp'; -import { cloneDeep } from 'lodash/fp'; -import { mount } from 'enzyme'; -import React, { useEffect } from 'react'; - -import { - AppToast, - useStateToaster, - ManageGlobalToaster, - GlobalToaster, - displayErrorToast, -} from '.'; - -jest.mock('uuid', () => { - return { - v1: jest.fn(() => '27261ae0-0bbb-11ea-b0ea-db767b07ea47'), - v4: jest.fn(() => '9e1f72a9-7c73-4b7f-a562-09940f7daf4a'), - }; -}); - -const mockToast: AppToast = { - color: 'danger', - id: 'id-super-id', - iconType: 'alert', - title: 'Test & Test', - toastLifeTimeMs: 100, - text: - 'Error 1, Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', -}; - -describe('Toaster', () => { - describe('Manage Global Toaster Reducer', () => { - test('we can add a toast in the reducer', () => { - const AddToaster = () => { - const [{ toasts }, dispatch] = useStateToaster(); - return ( - <> - <button - data-test-subj="add-toast" - type="button" - onClick={() => dispatch({ type: 'addToaster', toast: mockToast })} - /> - {toasts.map((toast) => ( - <span - data-test-subj={`add-toaster-${toast.id}`} - key={`add-toaster-${toast.id}`} - >{`${toast.title} ${toast.text}`}</span> - ))} - </> - ); - }; - const wrapper = mount( - <ManageGlobalToaster> - <AddToaster /> - </ManageGlobalToaster> - ); - wrapper.find('[data-test-subj="add-toast"]').simulate('click'); - expect(wrapper.find('[data-test-subj="add-toaster-id-super-id"]').exists()).toBe(true); - }); - test('we can delete a toast in the reducer', () => { - const DeleteToaster = () => { - const [{ toasts }, dispatch] = useStateToaster(); - useEffect(() => { - if (toasts.length === 0) { - dispatch({ type: 'addToaster', toast: mockToast }); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - return ( - <> - <button - data-test-subj="delete-toast" - type="button" - onClick={() => dispatch({ type: 'deleteToaster', id: mockToast.id })} - /> - {toasts.map((toast) => ( - <span - data-test-subj={`delete-toaster-${toast.id}`} - key={`delete-toaster-${toast.id}`} - >{`${toast.title} ${toast.text}`}</span> - ))} - </> - ); - }; - const wrapper = mount( - <ManageGlobalToaster> - <DeleteToaster /> - </ManageGlobalToaster> - ); - - expect(wrapper.find('[data-test-subj="delete-toaster-id-super-id"]').exists()).toBe(true); - wrapper.find('[data-test-subj="delete-toast"]').simulate('click'); - expect(wrapper.find('[data-test-subj="delete-toaster-id-super-id"]').exists()).toBe(false); - }); - }); - - describe('Global Toaster', () => { - test('Render a basic toaster', () => { - const AddToaster = () => { - const [{ toasts }, dispatch] = useStateToaster(); - return ( - <> - <button - data-test-subj="add-toast" - type="button" - onClick={() => dispatch({ type: 'addToaster', toast: mockToast })} - /> - {toasts.map((toast) => ( - <span key={`add-toaster-${toast.id}`}>{`${toast.title} ${toast.text}`}</span> - ))} - </> - ); - }; - const wrapper = mount( - <ManageGlobalToaster> - <AddToaster /> - <GlobalToaster /> - </ManageGlobalToaster> - ); - wrapper.find('[data-test-subj="add-toast"]').simulate('click'); - - expect(wrapper.find('.euiGlobalToastList').exists()).toBe(true); - expect(wrapper.find('.euiToastHeader__title').text()).toBe('Test & Test'); - }); - - test('Render an error toaster', () => { - let mockErrorToast: AppToast = cloneDeep(mockToast); - mockErrorToast.title = 'Test & Test ERROR'; - mockErrorToast = set('errors', [mockErrorToast.text], mockErrorToast); - - const AddToaster = () => { - const [{ toasts }, dispatch] = useStateToaster(); - return ( - <> - <button - data-test-subj="add-toast" - type="button" - onClick={() => dispatch({ type: 'addToaster', toast: mockErrorToast })} - /> - {toasts.map((toast) => ( - <span key={`add-toaster-${toast.id}`}>{`${toast.title} ${toast.text}`}</span> - ))} - </> - ); - }; - const wrapper = mount( - <ManageGlobalToaster> - <AddToaster /> - <GlobalToaster /> - </ManageGlobalToaster> - ); - wrapper.find('[data-test-subj="add-toast"]').simulate('click'); - - expect(wrapper.find('.euiGlobalToastList').exists()).toBe(true); - expect(wrapper.find('.euiToastHeader__title').text()).toBe('Test & Test ERROR'); - expect(wrapper.find('button[data-test-subj="toaster-show-all-error-modal"]').exists()).toBe( - true - ); - }); - - test('Only show one toast at the time', () => { - const mockOneMoreToast: AppToast = cloneDeep(mockToast); - mockOneMoreToast.id = 'id-super-id-II'; - mockOneMoreToast.title = 'Test & Test II'; - - const AddToaster = () => { - const [{ toasts }, dispatch] = useStateToaster(); - return ( - <> - <button - data-test-subj="add-toast" - type="button" - onClick={() => { - dispatch({ type: 'addToaster', toast: mockToast }); - dispatch({ type: 'addToaster', toast: mockOneMoreToast }); - }} - /> - <button - data-test-subj="delete-toast" - type="button" - onClick={() => { - dispatch({ type: 'deleteToaster', id: mockToast.id }); - }} - /> - {toasts.map((toast) => ( - <span key={`add-toaster-${toast.id}`}>{`${toast.title} ${toast.text}`}</span> - ))} - </> - ); - }; - const wrapper = mount( - <ManageGlobalToaster> - <AddToaster /> - <GlobalToaster /> - </ManageGlobalToaster> - ); - wrapper.find('[data-test-subj="add-toast"]').simulate('click'); - - expect(wrapper.find('button[data-test-subj="toastCloseButton"]').length).toBe(1); - expect(wrapper.find('.euiToastHeader__title').text()).toBe('Test & Test'); - wrapper.find('button[data-test-subj="delete-toast"]').simulate('click'); - expect(wrapper.find('.euiToast').length).toBe(1); - expect(wrapper.find('.euiToastHeader__title').text()).toBe('Test & Test II'); - }); - - test('Do not show anymore toaster when modal error is open', () => { - let mockErrorToast: AppToast = cloneDeep(mockToast); - mockErrorToast.id = 'id-super-id-error'; - mockErrorToast = set('errors', [mockErrorToast.text], mockErrorToast); - - const AddToaster = () => { - const [{ toasts }, dispatch] = useStateToaster(); - return ( - <> - <button - data-test-subj="add-toast" - type="button" - onClick={() => { - dispatch({ type: 'addToaster', toast: mockErrorToast }); - dispatch({ type: 'addToaster', toast: mockToast }); - }} - /> - {toasts.map((toast) => ( - <span key={`add-toaster-${toast.id}`}>{`${toast.title} ${toast.text}`}</span> - ))} - </> - ); - }; - const wrapper = mount( - <ManageGlobalToaster> - <AddToaster /> - <GlobalToaster /> - </ManageGlobalToaster> - ); - wrapper.find('[data-test-subj="add-toast"]').simulate('click'); - wrapper.find('button[data-test-subj="toaster-show-all-error-modal"]').simulate('click'); - - expect(wrapper.find('.euiToast').length).toBe(0); - }); - - test('Show new toaster when modal error is closing', () => { - let mockErrorToast: AppToast = cloneDeep(mockToast); - mockErrorToast.title = 'Test & Test II'; - mockErrorToast.id = 'id-super-id-error'; - mockErrorToast = set('errors', [mockErrorToast.text], mockErrorToast); - - const AddToaster = () => { - const [{ toasts }, dispatch] = useStateToaster(); - return ( - <> - <button - data-test-subj="add-toast" - type="button" - onClick={() => { - dispatch({ type: 'addToaster', toast: mockErrorToast }); - dispatch({ type: 'addToaster', toast: mockToast }); - }} - /> - {toasts.map((toast) => ( - <span key={`add-toaster-${toast.id}`}>{`${toast.title} ${toast.text}`}</span> - ))} - </> - ); - }; - const wrapper = mount( - <ManageGlobalToaster> - <AddToaster /> - <GlobalToaster /> - </ManageGlobalToaster> - ); - wrapper.find('[data-test-subj="add-toast"]').simulate('click'); - expect(wrapper.find('.euiToastHeader__title').text()).toBe('Test & Test II'); - - wrapper.find('button[data-test-subj="toaster-show-all-error-modal"]').simulate('click'); - expect(wrapper.find('.euiToast').length).toBe(0); - - wrapper.find('button[data-test-subj="modal-all-errors-close"]').simulate('click'); - expect(wrapper.find('.euiToast').length).toBe(1); - expect(wrapper.find('.euiToastHeader__title').text()).toBe('Test & Test'); - }); - }); - - describe('displayErrorToast', () => { - test('dispatches toast with correct title and message', () => { - const mockErrorToast = { - toast: { - color: 'danger', - errors: ['message'], - iconType: 'alert', - id: '9e1f72a9-7c73-4b7f-a562-09940f7daf4a', - title: 'Title', - }, - type: 'addToaster', - }; - const dispatchToasterMock = jest.fn(); - displayErrorToast('Title', ['message'], dispatchToasterMock); - expect(dispatchToasterMock.mock.calls[0][0]).toEqual(mockErrorToast); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/toasters/index.tsx b/x-pack/plugins/cases/public/components/toasters/index.tsx deleted file mode 100644 index ea17b03082751..0000000000000 --- a/x-pack/plugins/cases/public/components/toasters/index.tsx +++ /dev/null @@ -1,136 +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 { EuiButton, EuiGlobalToastList, EuiGlobalToastListToast as Toast } from '@elastic/eui'; -import { noop } from 'lodash/fp'; -import React, { createContext, Dispatch, useContext, useReducer, useState } from 'react'; -import styled from 'styled-components'; - -import { ModalAllErrors } from './modal_all_errors'; -import * as i18n from './translations'; - -export * from './utils'; -export * from './errors'; - -export interface AppToast extends Toast { - errors?: string[]; -} - -interface ToastState { - toasts: AppToast[]; -} - -const initialToasterState: ToastState = { - toasts: [], -}; - -export type ActionToaster = - | { type: 'addToaster'; toast: AppToast } - | { type: 'deleteToaster'; id: string } - | { type: 'toggleWaitToShowNextToast' }; - -export const StateToasterContext = createContext<[ToastState, Dispatch<ActionToaster>]>([ - initialToasterState, - () => noop, -]); - -export const useStateToaster = () => useContext(StateToasterContext); - -interface ManageGlobalToasterProps { - children: React.ReactNode; -} - -export const ManageGlobalToaster = ({ children }: ManageGlobalToasterProps) => { - const reducerToaster = (state: ToastState, action: ActionToaster) => { - switch (action.type) { - case 'addToaster': - return { ...state, toasts: [...state.toasts, action.toast] }; - case 'deleteToaster': - return { ...state, toasts: state.toasts.filter((msg) => msg.id !== action.id) }; - default: - return state; - } - }; - - return ( - <StateToasterContext.Provider value={useReducer(reducerToaster, initialToasterState)}> - {children} - </StateToasterContext.Provider> - ); -}; - -const GlobalToasterListContainer = styled.div` - position: absolute; - right: 0; - bottom: 0; -`; - -interface GlobalToasterProps { - toastLifeTimeMs?: number; -} - -export const GlobalToaster = ({ toastLifeTimeMs = 5000 }: GlobalToasterProps) => { - const [{ toasts }, dispatch] = useStateToaster(); - const [isShowing, setIsShowing] = useState(false); - const [toastInModal, setToastInModal] = useState<AppToast | null>(null); - - const toggle = (toast: AppToast) => { - if (isShowing) { - dispatch({ type: 'deleteToaster', id: toast.id }); - setToastInModal(null); - } else { - setToastInModal(toast); - } - setIsShowing(!isShowing); - }; - - return ( - <> - {toasts.length > 0 && !isShowing && ( - <GlobalToasterListContainer> - <EuiGlobalToastList - toasts={[formatToErrorToastIfNeeded(toasts[0], toggle)]} - dismissToast={({ id }) => { - dispatch({ type: 'deleteToaster', id }); - }} - toastLifeTimeMs={toastLifeTimeMs} - /> - </GlobalToasterListContainer> - )} - {toastInModal != null && ( - <ModalAllErrors isShowing={isShowing} toast={toastInModal} toggle={toggle} /> - )} - </> - ); -}; - -const formatToErrorToastIfNeeded = ( - toast: AppToast, - toggle: (toast: AppToast) => void -): AppToast => { - if (toast != null && toast.errors != null && toast.errors.length > 0) { - toast.text = ( - <ErrorToastContainer> - <EuiButton - data-test-subj="toaster-show-all-error-modal" - size="s" - color="danger" - onClick={() => toast != null && toggle(toast)} - > - {i18n.SEE_ALL_ERRORS} - </EuiButton> - </ErrorToastContainer> - ); - } - return toast; -}; - -const ErrorToastContainer = styled.div` - text-align: right; -`; - -ErrorToastContainer.displayName = 'ErrorToastContainer'; diff --git a/x-pack/plugins/cases/public/components/toasters/modal_all_errors.test.tsx b/x-pack/plugins/cases/public/components/toasters/modal_all_errors.test.tsx deleted file mode 100644 index 7ec0553591103..0000000000000 --- a/x-pack/plugins/cases/public/components/toasters/modal_all_errors.test.tsx +++ /dev/null @@ -1,70 +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 { shallow } from 'enzyme'; - -import React from 'react'; - -import { ModalAllErrors } from './modal_all_errors'; -import { AppToast } from '.'; -import { cloneDeep } from 'lodash/fp'; - -const mockToast: AppToast = { - color: 'danger', - id: 'id-super-id', - iconType: 'alert', - title: 'Test & Test', - errors: [ - 'Error 1, Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', - ], -}; - -describe('Modal all errors', () => { - const toggle = jest.fn(); - describe('rendering', () => { - test('it renders the default all errors modal when isShowing is positive', () => { - const wrapper = shallow( - <ModalAllErrors isShowing={true} toast={mockToast} toggle={toggle} /> - ); - expect(wrapper).toMatchSnapshot(); - }); - - test('it renders null when isShowing is negative', () => { - const wrapper = shallow( - <ModalAllErrors isShowing={false} toast={mockToast} toggle={toggle} /> - ); - expect(wrapper.html()).toEqual(null); - }); - - test('it renders multiple errors in modal', () => { - const mockToastWithTwoError = cloneDeep(mockToast); - mockToastWithTwoError.errors = [ - 'Error 1, Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', - 'Error 2, Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', - 'Error 3, Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', - ]; - const wrapper = shallow( - <ModalAllErrors isShowing={true} toast={mockToastWithTwoError} toggle={toggle} /> - ); - expect(wrapper.find('[data-test-subj="modal-all-errors-accordion"]').length).toBe( - mockToastWithTwoError.errors.length - ); - }); - }); - - describe('events', () => { - test('Make sure that toggle function has been called when you click on the close button', () => { - const wrapper = shallow( - <ModalAllErrors isShowing={true} toast={mockToast} toggle={toggle} /> - ); - - wrapper.find('[data-test-subj="modal-all-errors-close"]').simulate('click'); - wrapper.update(); - expect(toggle).toHaveBeenCalledWith(mockToast); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/toasters/modal_all_errors.tsx b/x-pack/plugins/cases/public/components/toasters/modal_all_errors.tsx deleted file mode 100644 index 0a78139f5fe3a..0000000000000 --- a/x-pack/plugins/cases/public/components/toasters/modal_all_errors.tsx +++ /dev/null @@ -1,75 +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 { - EuiButton, - EuiModal, - EuiModalHeader, - EuiModalHeaderTitle, - EuiModalBody, - EuiCallOut, - EuiSpacer, - EuiCodeBlock, - EuiModalFooter, - EuiAccordion, -} from '@elastic/eui'; -import React, { useCallback } from 'react'; -import styled from 'styled-components'; - -import { AppToast } from '.'; -import * as i18n from './translations'; - -interface FullErrorProps { - isShowing: boolean; - toast: AppToast; - toggle: (toast: AppToast) => void; -} - -const ModalAllErrorsComponent: React.FC<FullErrorProps> = ({ isShowing, toast, toggle }) => { - const handleClose = useCallback(() => toggle(toast), [toggle, toast]); - - if (!isShowing || toast == null) return null; - - return ( - <EuiModal onClose={handleClose}> - <EuiModalHeader> - <EuiModalHeaderTitle>{i18n.TITLE_ERROR_MODAL}</EuiModalHeaderTitle> - </EuiModalHeader> - - <EuiModalBody> - <EuiCallOut title={toast.title} color="danger" size="s" iconType="alert" /> - <EuiSpacer size="s" /> - {toast.errors != null && - toast.errors.map((error, index) => ( - <EuiAccordion - key={`${toast.id}-${index}`} - id="accordion1" - initialIsOpen={index === 0 ? true : false} - buttonContent={error.length > 100 ? `${error.substring(0, 100)} ...` : error} - data-test-subj="modal-all-errors-accordion" - > - <MyEuiCodeBlock>{error}</MyEuiCodeBlock> - </EuiAccordion> - ))} - </EuiModalBody> - - <EuiModalFooter> - <EuiButton onClick={handleClose} fill data-test-subj="modal-all-errors-close"> - {i18n.CLOSE_ERROR_MODAL} - </EuiButton> - </EuiModalFooter> - </EuiModal> - ); -}; - -export const ModalAllErrors = React.memo(ModalAllErrorsComponent); - -const MyEuiCodeBlock = styled(EuiCodeBlock)` - margin-top: 4px; -`; - -MyEuiCodeBlock.displayName = 'MyEuiCodeBlock'; diff --git a/x-pack/plugins/cases/public/components/toasters/translations.ts b/x-pack/plugins/cases/public/components/toasters/translations.ts deleted file mode 100644 index cf7fac462a122..0000000000000 --- a/x-pack/plugins/cases/public/components/toasters/translations.ts +++ /dev/null @@ -1,20 +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 { i18n } from '@kbn/i18n'; - -export const SEE_ALL_ERRORS = i18n.translate('xpack.cases.modalAllErrors.seeAllErrors.button', { - defaultMessage: 'See the full error(s)', -}); - -export const TITLE_ERROR_MODAL = i18n.translate('xpack.cases.modalAllErrors.title', { - defaultMessage: 'Your visualization has error(s)', -}); - -export const CLOSE_ERROR_MODAL = i18n.translate('xpack.cases.modalAllErrors.close.button', { - defaultMessage: 'Close', -}); diff --git a/x-pack/plugins/cases/public/components/toasters/utils.test.ts b/x-pack/plugins/cases/public/components/toasters/utils.test.ts deleted file mode 100644 index 34871b2e68efa..0000000000000 --- a/x-pack/plugins/cases/public/components/toasters/utils.test.ts +++ /dev/null @@ -1,128 +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 { errorToToaster } from './utils'; -import { ToasterError } from './errors'; - -const ApiError = class extends Error { - public body: {} = {}; -}; - -describe('error_to_toaster', () => { - let dispatchToaster = jest.fn(); - - beforeEach(() => { - dispatchToaster = jest.fn(); - }); - - describe('#errorToToaster', () => { - test('dispatches an error toast given a ToasterError with multiple error messages', () => { - const error = new ToasterError(['some error 1', 'some error 2']); - errorToToaster({ id: 'some-made-up-id', title: 'some title', error, dispatchToaster }); - expect(dispatchToaster.mock.calls[0]).toEqual([ - { - toast: { - color: 'danger', - errors: ['some error 1', 'some error 2'], - iconType: 'alert', - id: 'some-made-up-id', - title: 'some title', - }, - type: 'addToaster', - }, - ]); - }); - - test('dispatches an error toast given a ToasterError with a single error message', () => { - const error = new ToasterError(['some error 1']); - errorToToaster({ id: 'some-made-up-id', title: 'some title', error, dispatchToaster }); - expect(dispatchToaster.mock.calls[0]).toEqual([ - { - toast: { - color: 'danger', - errors: ['some error 1'], - iconType: 'alert', - id: 'some-made-up-id', - title: 'some title', - }, - type: 'addToaster', - }, - ]); - }); - - test('dispatches an error toast given an ApiError with a message', () => { - const error = new ApiError('Internal Server Error'); - error.body = { message: 'something bad happened', status_code: 500 }; - - errorToToaster({ id: 'some-made-up-id', title: 'some title', error, dispatchToaster }); - expect(dispatchToaster.mock.calls[0]).toEqual([ - { - toast: { - color: 'danger', - errors: ['something bad happened'], - iconType: 'alert', - id: 'some-made-up-id', - title: 'some title', - }, - type: 'addToaster', - }, - ]); - }); - - test('dispatches an error toast given an ApiError with no message', () => { - const error = new ApiError('Internal Server Error'); - - errorToToaster({ id: 'some-made-up-id', title: 'some title', error, dispatchToaster }); - expect(dispatchToaster.mock.calls[0]).toEqual([ - { - toast: { - color: 'danger', - errors: ['Internal Server Error'], - iconType: 'alert', - id: 'some-made-up-id', - title: 'some title', - }, - type: 'addToaster', - }, - ]); - }); - - test('dispatches an error toast given a standard Error', () => { - const error = new Error('some error 1'); - errorToToaster({ id: 'some-made-up-id', title: 'some title', error, dispatchToaster }); - expect(dispatchToaster.mock.calls[0]).toEqual([ - { - toast: { - color: 'danger', - errors: ['some error 1'], - iconType: 'alert', - id: 'some-made-up-id', - title: 'some title', - }, - type: 'addToaster', - }, - ]); - }); - - test('adds a generic Network Error given a non Error object such as a string', () => { - const error = 'terrible string'; - errorToToaster({ id: 'some-made-up-id', title: 'some title', error, dispatchToaster }); - expect(dispatchToaster.mock.calls[0]).toEqual([ - { - toast: { - color: 'danger', - errors: ['Network Error'], - iconType: 'alert', - id: 'some-made-up-id', - title: 'some title', - }, - type: 'addToaster', - }, - ]); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/toasters/utils.ts b/x-pack/plugins/cases/public/components/toasters/utils.ts deleted file mode 100644 index 0575c40107668..0000000000000 --- a/x-pack/plugins/cases/public/components/toasters/utils.ts +++ /dev/null @@ -1,149 +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 type React from 'react'; -import uuid from 'uuid'; -import { isError } from 'lodash/fp'; - -import { AppToast, ActionToaster } from './'; -import { isToasterError } from './errors'; -import { isAppError } from '../../common/errors'; - -/** - * Displays an error toast for the provided title and message - * - * @param errorTitle Title of error to display in toaster and modal - * @param errorMessages Message to display in error modal when clicked - * @param dispatchToaster provided by useStateToaster() - */ -export const displayErrorToast = ( - errorTitle: string, - errorMessages: string[], - dispatchToaster: React.Dispatch<ActionToaster>, - id: string = uuid.v4() -): void => { - const toast: AppToast = { - id, - title: errorTitle, - color: 'danger', - iconType: 'alert', - errors: errorMessages, - }; - dispatchToaster({ - type: 'addToaster', - toast, - }); -}; - -/** - * Displays a warning toast for the provided title and message - * - * @param title warning message to display in toaster and modal - * @param dispatchToaster provided by useStateToaster() - * @param id unique ID if necessary - */ -export const displayWarningToast = ( - title: string, - dispatchToaster: React.Dispatch<ActionToaster>, - id: string = uuid.v4() -): void => { - const toast: AppToast = { - id, - title, - color: 'warning', - iconType: 'help', - }; - dispatchToaster({ - type: 'addToaster', - toast, - }); -}; - -/** - * Displays a success toast for the provided title and message - * - * @param title success message to display in toaster and modal - * @param dispatchToaster provided by useStateToaster() - */ -export const displaySuccessToast = ( - title: string, - dispatchToaster: React.Dispatch<ActionToaster>, - id: string = uuid.v4() -): void => { - const toast: AppToast = { - id, - title, - color: 'success', - iconType: 'check', - }; - dispatchToaster({ - type: 'addToaster', - toast, - }); -}; - -export type ErrorToToasterArgs = Partial<AppToast> & { - error: unknown; - dispatchToaster: React.Dispatch<ActionToaster>; -}; - -/** - * Displays an error toast with messages parsed from the error - * - * @param title error message to display in toaster and modal - * @param error the error from which messages will be parsed - * @param dispatchToaster provided by useStateToaster() - */ -export const errorToToaster = ({ - id = uuid.v4(), - title, - error, - color = 'danger', - iconType = 'alert', - dispatchToaster, -}: ErrorToToasterArgs) => { - let toast: AppToast; - - if (isToasterError(error)) { - toast = { - id, - title, - color, - iconType, - errors: error.messages, - }; - } else if (isAppError(error)) { - toast = { - id, - title, - color, - iconType, - errors: [error.body.message], - }; - } else if (isError(error)) { - toast = { - id, - title, - color, - iconType, - errors: [error.message], - }; - } else { - toast = { - id, - title, - color, - iconType, - errors: ['Network Error'], - }; - } - - dispatchToaster({ - type: 'addToaster', - toast, - }); -}; diff --git a/x-pack/plugins/cases/public/components/use_create_case_modal/create_case_modal.test.tsx b/x-pack/plugins/cases/public/components/use_create_case_modal/create_case_modal.test.tsx deleted file mode 100644 index fcdc2f8e58774..0000000000000 --- a/x-pack/plugins/cases/public/components/use_create_case_modal/create_case_modal.test.tsx +++ /dev/null @@ -1,126 +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, { ReactNode } from 'react'; -import { mount } from 'enzyme'; - -import { CreateCaseModal } from './create_case_modal'; -import { TestProviders } from '../../common/mock'; - -jest.mock('../create/form_context', () => { - return { - FormContext: ({ - children, - onSuccess, - }: { - children: ReactNode; - onSuccess: ({ id }: { id: string }) => Promise<void>; - }) => { - return ( - <> - <button - type="button" - data-test-subj="form-context-on-success" - onClick={async () => { - await onSuccess({ id: 'case-id' }); - }} - > - {'submit'} - </button> - {children} - </> - ); - }, - }; -}); - -jest.mock('../create/form', () => { - return { - CreateCaseForm: () => { - return <>{'form'}</>; - }, - }; -}); - -jest.mock('../create/submit_button', () => { - return { - SubmitCaseButton: () => { - return <>{'Submit'}</>; - }, - }; -}); - -const onCloseCaseModal = jest.fn(); -const onSuccess = jest.fn(); -const defaultProps = { - isModalOpen: true, - onCloseCaseModal, - onSuccess, -}; - -describe('CreateCaseModal', () => { - beforeEach(() => { - jest.resetAllMocks(); - }); - - it('renders', () => { - const wrapper = mount( - <TestProviders> - <CreateCaseModal {...defaultProps} /> - </TestProviders> - ); - - expect(wrapper.find(`[data-test-subj='all-cases-modal']`).exists()).toBeTruthy(); - }); - - it('it does not render the modal isModalOpen=false ', () => { - const wrapper = mount( - <TestProviders> - <CreateCaseModal {...defaultProps} isModalOpen={false} /> - </TestProviders> - ); - - expect(wrapper.find(`[data-test-subj='all-cases-modal']`).exists()).toBeFalsy(); - }); - - it('Closing modal calls onCloseCaseModal', () => { - const wrapper = mount( - <TestProviders> - <CreateCaseModal {...defaultProps} /> - </TestProviders> - ); - - wrapper.find('.euiModal__closeIcon').first().simulate('click'); - expect(onCloseCaseModal).toBeCalled(); - }); - - it('pass the correct props to FormContext component', () => { - const wrapper = mount( - <TestProviders> - <CreateCaseModal {...defaultProps} /> - </TestProviders> - ); - - const props = wrapper.find('FormContext').props(); - expect(props).toEqual( - expect.objectContaining({ - onSuccess, - }) - ); - }); - - it('onSuccess called when creating a case', () => { - const wrapper = mount( - <TestProviders> - <CreateCaseModal {...defaultProps} /> - </TestProviders> - ); - - wrapper.find(`[data-test-subj='form-context-on-success']`).first().simulate('click'); - expect(onSuccess).toHaveBeenCalledWith({ id: 'case-id' }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/use_create_case_modal/create_case_modal.tsx b/x-pack/plugins/cases/public/components/use_create_case_modal/create_case_modal.tsx deleted file mode 100644 index fc397b24e7046..0000000000000 --- a/x-pack/plugins/cases/public/components/use_create_case_modal/create_case_modal.tsx +++ /dev/null @@ -1,67 +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, { memo } from 'react'; -import styled from 'styled-components'; -import { EuiModal, EuiModalBody, EuiModalHeader, EuiModalHeaderTitle } from '@elastic/eui'; - -import { FormContext } from '../create/form_context'; -import { CreateCaseForm } from '../create/form'; -import { SubmitCaseButton } from '../create/submit_button'; -import { Case } from '../../containers/types'; -import * as i18n from '../../common/translations'; -import { CaseType } from '../../../common'; - -export interface CreateCaseModalProps { - isModalOpen: boolean; - onCloseCaseModal: () => void; - onSuccess: (theCase: Case) => Promise<void>; - caseType?: CaseType; - hideConnectorServiceNowSir?: boolean; -} - -const Container = styled.div` - ${({ theme }) => ` - margin-top: ${theme.eui.euiSize}; - text-align: right; - `} -`; - -const CreateModalComponent: React.FC<CreateCaseModalProps> = ({ - isModalOpen, - onCloseCaseModal, - onSuccess, - caseType = CaseType.individual, - hideConnectorServiceNowSir = false, -}) => { - return isModalOpen ? ( - <EuiModal onClose={onCloseCaseModal} data-test-subj="all-cases-modal"> - <EuiModalHeader> - <EuiModalHeaderTitle>{i18n.CREATE_TITLE}</EuiModalHeaderTitle> - </EuiModalHeader> - <EuiModalBody> - <FormContext - hideConnectorServiceNowSir={hideConnectorServiceNowSir} - caseType={caseType} - onSuccess={onSuccess} - > - <CreateCaseForm - withSteps={false} - hideConnectorServiceNowSir={hideConnectorServiceNowSir} - /> - <Container> - <SubmitCaseButton /> - </Container> - </FormContext> - </EuiModalBody> - </EuiModal> - ) : null; -}; - -export const CreateCaseModal = memo(CreateModalComponent); - -CreateCaseModal.displayName = 'CreateCaseModal'; diff --git a/x-pack/plugins/cases/public/components/use_create_case_modal/index.test.tsx b/x-pack/plugins/cases/public/components/use_create_case_modal/index.test.tsx deleted file mode 100644 index df9e6f0af60d9..0000000000000 --- a/x-pack/plugins/cases/public/components/use_create_case_modal/index.test.tsx +++ /dev/null @@ -1,151 +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, { ReactNode } from 'react'; -import { renderHook, act } from '@testing-library/react-hooks'; -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import { useKibana } from '../../common/lib/kibana'; -import { useCreateCaseModal, UseCreateCaseModalProps, UseCreateCaseModalReturnedValues } from '.'; -import { TestProviders } from '../../common/mock'; - -jest.mock('../../common/lib/kibana'); -jest.mock('../create/form_context', () => { - return { - FormContext: ({ - children, - onSuccess, - }: { - children: ReactNode; - onSuccess: ({ id }: { id: string }) => Promise<void>; - }) => { - return ( - <> - <button - type="button" - data-test-subj="form-context-on-success" - onClick={async () => { - await onSuccess({ id: 'case-id' }); - }} - > - {'Form submit'} - </button> - {children} - </> - ); - }, - }; -}); - -jest.mock('../create/form', () => { - return { - CreateCaseForm: () => { - return <>{'form'}</>; - }, - }; -}); - -jest.mock('../create/submit_button', () => { - return { - SubmitCaseButton: () => { - return <>{'Submit'}</>; - }, - }; -}); - -const useKibanaMock = useKibana as jest.Mocked<typeof useKibana>; -const onCaseCreated = jest.fn(); - -describe('useCreateCaseModal', () => { - let navigateToApp: jest.Mock; - - beforeEach(() => { - navigateToApp = jest.fn(); - useKibanaMock().services.application.navigateToApp = navigateToApp; - }); - - it('init', async () => { - const { result } = renderHook<UseCreateCaseModalProps, UseCreateCaseModalReturnedValues>( - () => useCreateCaseModal({ onCaseCreated }), - { - wrapper: ({ children }) => <TestProviders>{children}</TestProviders>, - } - ); - - expect(result.current.isModalOpen).toBe(false); - }); - - it('opens the modal', async () => { - const { result } = renderHook<UseCreateCaseModalProps, UseCreateCaseModalReturnedValues>( - () => useCreateCaseModal({ onCaseCreated }), - { - wrapper: ({ children }) => <TestProviders>{children}</TestProviders>, - } - ); - - act(() => { - result.current.openModal(); - }); - - expect(result.current.isModalOpen).toBe(true); - }); - - it('closes the modal', async () => { - const { result } = renderHook<UseCreateCaseModalProps, UseCreateCaseModalReturnedValues>( - () => useCreateCaseModal({ onCaseCreated }), - { - wrapper: ({ children }) => <TestProviders>{children}</TestProviders>, - } - ); - - act(() => { - result.current.openModal(); - result.current.closeModal(); - }); - - expect(result.current.isModalOpen).toBe(false); - }); - - it('returns a memoized value', async () => { - const { result, rerender } = renderHook< - UseCreateCaseModalProps, - UseCreateCaseModalReturnedValues - >(() => useCreateCaseModal({ onCaseCreated }), { - wrapper: ({ children }) => <TestProviders>{children}</TestProviders>, - }); - - const result1 = result.current; - act(() => rerender()); - const result2 = result.current; - - expect(Object.is(result1, result2)).toBe(true); - }); - - it('closes the modal when creating a case', async () => { - const { result } = renderHook<UseCreateCaseModalProps, UseCreateCaseModalReturnedValues>( - () => useCreateCaseModal({ onCaseCreated }), - { - wrapper: ({ children }) => <TestProviders>{children}</TestProviders>, - } - ); - - act(() => { - result.current.openModal(); - }); - - const modal = result.current.modal; - render(<TestProviders>{modal}</TestProviders>); - - act(() => { - userEvent.click(screen.getByText('Form submit')); - }); - - expect(result.current.isModalOpen).toBe(false); - expect(onCaseCreated).toHaveBeenCalledWith({ id: 'case-id' }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/use_create_case_modal/index.tsx b/x-pack/plugins/cases/public/components/use_create_case_modal/index.tsx deleted file mode 100644 index 7da3f49be721d..0000000000000 --- a/x-pack/plugins/cases/public/components/use_create_case_modal/index.tsx +++ /dev/null @@ -1,60 +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, { useState, useCallback, useMemo } from 'react'; -import { CaseType } from '../../../common'; -import { Case } from '../../containers/types'; -import { CreateCaseModal } from './create_case_modal'; - -export interface UseCreateCaseModalProps { - onCaseCreated: (theCase: Case) => void; - caseType?: CaseType; - hideConnectorServiceNowSir?: boolean; -} -export interface UseCreateCaseModalReturnedValues { - modal: JSX.Element; - isModalOpen: boolean; - closeModal: () => void; - openModal: () => void; -} - -export const useCreateCaseModal = ({ - caseType = CaseType.individual, - onCaseCreated, - hideConnectorServiceNowSir = false, -}: UseCreateCaseModalProps) => { - const [isModalOpen, setIsModalOpen] = useState<boolean>(false); - const closeModal = useCallback(() => setIsModalOpen(false), []); - const openModal = useCallback(() => setIsModalOpen(true), []); - const onSuccess = useCallback( - async (theCase) => { - onCaseCreated(theCase); - closeModal(); - }, - [onCaseCreated, closeModal] - ); - - const state = useMemo( - () => ({ - modal: ( - <CreateCaseModal - caseType={caseType} - hideConnectorServiceNowSir={hideConnectorServiceNowSir} - isModalOpen={isModalOpen} - onCloseCaseModal={closeModal} - onSuccess={onSuccess} - /> - ), - isModalOpen, - closeModal, - openModal, - }), - [caseType, closeModal, hideConnectorServiceNowSir, isModalOpen, onSuccess, openModal] - ); - - return state; -}; diff --git a/x-pack/plugins/cases/public/components/wrappers/index.tsx b/x-pack/plugins/cases/public/components/wrappers/index.tsx deleted file mode 100644 index 3b33e9304da83..0000000000000 --- a/x-pack/plugins/cases/public/components/wrappers/index.tsx +++ /dev/null @@ -1,26 +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 styled from 'styled-components'; - -export const WhitePageWrapper = styled.div` - background-color: ${({ theme }) => theme.eui.euiColorEmptyShade}; - border-top: ${({ theme }) => theme.eui.euiBorderThin}; - flex: 1 1 auto; -`; - -export const SectionWrapper = styled.div` - box-sizing: content-box; - margin: 0 auto; - max-width: 1175px; - width: 100%; -`; - -export const HeaderWrapper = styled.div` - padding: ${({ theme }) => - `${theme.eui.paddingSizes.l} ${theme.eui.paddingSizes.l} 0 ${theme.eui.paddingSizes.l}`}; -`; diff --git a/x-pack/plugins/cases/public/containers/__mocks__/api.ts b/x-pack/plugins/cases/public/containers/__mocks__/api.ts deleted file mode 100644 index 4dbb10da95b2d..0000000000000 --- a/x-pack/plugins/cases/public/containers/__mocks__/api.ts +++ /dev/null @@ -1,114 +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 { - ActionLicense, - AllCases, - BulkUpdateStatus, - Case, - CasesStatus, - CaseUserActions, - FetchCasesProps, - SortFieldCase, -} from '../types'; -import { - actionLicenses, - allCases, - basicCase, - basicCaseCommentPatch, - basicCasePost, - casesStatus, - caseUserActions, - pushedCase, - respReporters, - tags, -} from '../mock'; -import { - CasePatchRequest, - CasePostRequest, - CommentRequest, - User, - CaseStatuses, -} from '../../../common'; - -export const getCase = async ( - caseId: string, - includeComments: boolean = true, - signal: AbortSignal -): Promise<Case> => { - return Promise.resolve(basicCase); -}; - -export const getCasesStatus = async (signal: AbortSignal): Promise<CasesStatus> => - Promise.resolve(casesStatus); - -export const getTags = async (signal: AbortSignal): Promise<string[]> => Promise.resolve(tags); - -export const getReporters = async (signal: AbortSignal): Promise<User[]> => - Promise.resolve(respReporters); - -export const getCaseUserActions = async ( - caseId: string, - signal: AbortSignal -): Promise<CaseUserActions[]> => Promise.resolve(caseUserActions); - -export const getCases = async ({ - filterOptions = { - search: '', - reporters: [], - status: CaseStatuses.open, - tags: [], - }, - queryParams = { - page: 1, - perPage: 5, - sortField: SortFieldCase.createdAt, - sortOrder: 'desc', - }, - signal, -}: FetchCasesProps): Promise<AllCases> => Promise.resolve(allCases); - -export const postCase = async (newCase: CasePostRequest, signal: AbortSignal): Promise<Case> => - Promise.resolve(basicCasePost); - -export const patchCase = async ( - caseId: string, - updatedCase: Pick<CasePatchRequest, 'description' | 'status' | 'tags' | 'title'>, - version: string, - signal: AbortSignal -): Promise<Case[]> => Promise.resolve([basicCase]); - -export const patchCasesStatus = async ( - cases: BulkUpdateStatus[], - signal: AbortSignal -): Promise<Case[]> => Promise.resolve(allCases.cases); - -export const postComment = async ( - newComment: CommentRequest, - caseId: string, - signal: AbortSignal -): Promise<Case> => Promise.resolve(basicCase); - -export const patchComment = async ( - caseId: string, - commentId: string, - commentUpdate: string, - version: string, - signal: AbortSignal -): Promise<Case> => Promise.resolve(basicCaseCommentPatch); - -export const deleteCases = async (caseIds: string[], signal: AbortSignal): Promise<boolean> => - Promise.resolve(true); - -export const pushCase = async ( - caseId: string, - connectorId: string, - signal: AbortSignal -): Promise<Case> => Promise.resolve(pushedCase); - -export const getActionLicense = async (signal: AbortSignal): Promise<ActionLicense[]> => - Promise.resolve(actionLicenses); diff --git a/x-pack/plugins/cases/public/containers/api.test.tsx b/x-pack/plugins/cases/public/containers/api.test.tsx deleted file mode 100644 index 3e71a05df7cc1..0000000000000 --- a/x-pack/plugins/cases/public/containers/api.test.tsx +++ /dev/null @@ -1,465 +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 { KibanaServices } from '../common/lib/kibana'; - -import { ConnectorTypes, CommentType, CaseStatuses } from '../../common'; -import { CASES_URL } from '../../common'; - -import { - deleteCases, - getActionLicense, - getCase, - getCases, - getCasesStatus, - getCaseUserActions, - getReporters, - getTags, - patchCase, - patchCasesStatus, - patchComment, - postCase, - postComment, - pushCase, -} from './api'; - -import { - actionLicenses, - allCases, - basicCase, - allCasesSnake, - basicCaseSnake, - pushedCaseSnake, - casesStatus, - casesSnake, - cases, - caseUserActions, - pushedCase, - reporters, - respReporters, - tags, - caseUserActionsSnake, - casesStatusSnake, -} from './mock'; - -import { DEFAULT_FILTER_OPTIONS, DEFAULT_QUERY_PARAMS } from './use_get_cases'; - -const abortCtrl = new AbortController(); -const mockKibanaServices = KibanaServices.get as jest.Mock; -jest.mock('../common/lib/kibana'); - -const fetchMock = jest.fn(); -mockKibanaServices.mockReturnValue({ http: { fetch: fetchMock } }); - -describe('Case Configuration API', () => { - describe('deleteCases', () => { - beforeEach(() => { - fetchMock.mockClear(); - fetchMock.mockResolvedValue(''); - }); - const data = ['1', '2']; - - test('check url, method, signal', async () => { - await deleteCases(data, abortCtrl.signal); - expect(fetchMock).toHaveBeenCalledWith(`${CASES_URL}`, { - method: 'DELETE', - query: { ids: JSON.stringify(data) }, - signal: abortCtrl.signal, - }); - }); - - test('happy path', async () => { - const resp = await deleteCases(data, abortCtrl.signal); - expect(resp).toEqual(''); - }); - }); - - describe('getActionLicense', () => { - beforeEach(() => { - fetchMock.mockClear(); - fetchMock.mockResolvedValue(actionLicenses); - }); - - test('check url, method, signal', async () => { - await getActionLicense(abortCtrl.signal); - expect(fetchMock).toHaveBeenCalledWith(`/api/actions/list_action_types`, { - method: 'GET', - signal: abortCtrl.signal, - }); - }); - - test('happy path', async () => { - const resp = await getActionLicense(abortCtrl.signal); - expect(resp).toEqual(actionLicenses); - }); - }); - - describe('getCase', () => { - beforeEach(() => { - fetchMock.mockClear(); - fetchMock.mockResolvedValue(basicCaseSnake); - }); - const data = basicCase.id; - - test('check url, method, signal', async () => { - await getCase(data, true, abortCtrl.signal); - expect(fetchMock).toHaveBeenCalledWith(`${CASES_URL}/${basicCase.id}`, { - method: 'GET', - query: { includeComments: true }, - signal: abortCtrl.signal, - }); - }); - - test('happy path', async () => { - const resp = await getCase(data, true, abortCtrl.signal); - expect(resp).toEqual(basicCase); - }); - }); - - describe('getCases', () => { - beforeEach(() => { - fetchMock.mockClear(); - fetchMock.mockResolvedValue(allCasesSnake); - }); - test('check url, method, signal', async () => { - await getCases({ - filterOptions: DEFAULT_FILTER_OPTIONS, - queryParams: DEFAULT_QUERY_PARAMS, - signal: abortCtrl.signal, - }); - expect(fetchMock).toHaveBeenCalledWith(`${CASES_URL}/_find`, { - method: 'GET', - query: { - ...DEFAULT_QUERY_PARAMS, - reporters: [], - tags: [], - }, - signal: abortCtrl.signal, - }); - }); - - test('correctly applies filters', async () => { - await getCases({ - filterOptions: { - ...DEFAULT_FILTER_OPTIONS, - reporters: [...respReporters, { username: null, full_name: null, email: null }], - tags, - status: CaseStatuses.open, - search: 'hello', - }, - queryParams: DEFAULT_QUERY_PARAMS, - signal: abortCtrl.signal, - }); - expect(fetchMock).toHaveBeenCalledWith(`${CASES_URL}/_find`, { - method: 'GET', - query: { - ...DEFAULT_QUERY_PARAMS, - reporters, - tags: ['"coke"', '"pepsi"'], - search: 'hello', - status: CaseStatuses.open, - }, - signal: abortCtrl.signal, - }); - }); - - test('tags with weird chars get handled gracefully', async () => { - const weirdTags: string[] = ['(', '"double"']; - - await getCases({ - filterOptions: { - ...DEFAULT_FILTER_OPTIONS, - reporters: [...respReporters, { username: null, full_name: null, email: null }], - tags: weirdTags, - status: CaseStatuses.open, - search: 'hello', - }, - queryParams: DEFAULT_QUERY_PARAMS, - signal: abortCtrl.signal, - }); - expect(fetchMock).toHaveBeenCalledWith(`${CASES_URL}/_find`, { - method: 'GET', - query: { - ...DEFAULT_QUERY_PARAMS, - reporters, - tags: ['"("', '"\\"double\\""'], - search: 'hello', - status: CaseStatuses.open, - }, - signal: abortCtrl.signal, - }); - }); - - test('happy path', async () => { - const resp = await getCases({ - filterOptions: DEFAULT_FILTER_OPTIONS, - queryParams: DEFAULT_QUERY_PARAMS, - signal: abortCtrl.signal, - }); - expect(resp).toEqual({ ...allCases }); - }); - }); - - describe('getCasesStatus', () => { - beforeEach(() => { - fetchMock.mockClear(); - fetchMock.mockResolvedValue(casesStatusSnake); - }); - test('check url, method, signal', async () => { - await getCasesStatus(abortCtrl.signal); - expect(fetchMock).toHaveBeenCalledWith(`${CASES_URL}/status`, { - method: 'GET', - signal: abortCtrl.signal, - }); - }); - - test('happy path', async () => { - const resp = await getCasesStatus(abortCtrl.signal); - expect(resp).toEqual(casesStatus); - }); - }); - - describe('getCaseUserActions', () => { - beforeEach(() => { - fetchMock.mockClear(); - fetchMock.mockResolvedValue(caseUserActionsSnake); - }); - - test('check url, method, signal', async () => { - await getCaseUserActions(basicCase.id, abortCtrl.signal); - expect(fetchMock).toHaveBeenCalledWith(`${CASES_URL}/${basicCase.id}/user_actions`, { - method: 'GET', - signal: abortCtrl.signal, - }); - }); - - test('happy path', async () => { - const resp = await getCaseUserActions(basicCase.id, abortCtrl.signal); - expect(resp).toEqual(caseUserActions); - }); - }); - - describe('getReporters', () => { - beforeEach(() => { - fetchMock.mockClear(); - fetchMock.mockResolvedValue(respReporters); - }); - - test('check url, method, signal', async () => { - await getReporters(abortCtrl.signal); - expect(fetchMock).toHaveBeenCalledWith(`${CASES_URL}/reporters`, { - method: 'GET', - signal: abortCtrl.signal, - }); - }); - - test('happy path', async () => { - const resp = await getReporters(abortCtrl.signal); - expect(resp).toEqual(respReporters); - }); - }); - - describe('getTags', () => { - beforeEach(() => { - fetchMock.mockClear(); - fetchMock.mockResolvedValue(tags); - }); - - test('check url, method, signal', async () => { - await getTags(abortCtrl.signal); - expect(fetchMock).toHaveBeenCalledWith(`${CASES_URL}/tags`, { - method: 'GET', - signal: abortCtrl.signal, - }); - }); - - test('happy path', async () => { - const resp = await getTags(abortCtrl.signal); - expect(resp).toEqual(tags); - }); - }); - - describe('patchCase', () => { - beforeEach(() => { - fetchMock.mockClear(); - fetchMock.mockResolvedValue([basicCaseSnake]); - }); - const data = { description: 'updated description' }; - test('check url, method, signal', async () => { - await patchCase(basicCase.id, data, basicCase.version, abortCtrl.signal); - expect(fetchMock).toHaveBeenCalledWith(`${CASES_URL}`, { - method: 'PATCH', - body: JSON.stringify({ - cases: [{ ...data, id: basicCase.id, version: basicCase.version }], - }), - signal: abortCtrl.signal, - }); - }); - - test('happy path', async () => { - const resp = await patchCase( - basicCase.id, - { description: 'updated description' }, - basicCase.version, - abortCtrl.signal - ); - expect(resp).toEqual({ ...[basicCase] }); - }); - }); - - describe('patchCasesStatus', () => { - beforeEach(() => { - fetchMock.mockClear(); - fetchMock.mockResolvedValue(casesSnake); - }); - const data = [ - { - status: CaseStatuses.closed, - id: basicCase.id, - version: basicCase.version, - }, - ]; - - test('check url, method, signal', async () => { - await patchCasesStatus(data, abortCtrl.signal); - expect(fetchMock).toHaveBeenCalledWith(`${CASES_URL}`, { - method: 'PATCH', - body: JSON.stringify({ cases: data }), - signal: abortCtrl.signal, - }); - }); - - test('happy path', async () => { - const resp = await patchCasesStatus(data, abortCtrl.signal); - expect(resp).toEqual({ ...cases }); - }); - }); - - describe('patchComment', () => { - beforeEach(() => { - fetchMock.mockClear(); - fetchMock.mockResolvedValue(basicCaseSnake); - }); - - test('check url, method, signal', async () => { - await patchComment( - basicCase.id, - basicCase.comments[0].id, - 'updated comment', - basicCase.comments[0].version, - abortCtrl.signal - ); - expect(fetchMock).toHaveBeenCalledWith(`${CASES_URL}/${basicCase.id}/comments`, { - method: 'PATCH', - body: JSON.stringify({ - comment: 'updated comment', - type: CommentType.user, - id: basicCase.comments[0].id, - version: basicCase.comments[0].version, - }), - signal: abortCtrl.signal, - }); - }); - - test('happy path', async () => { - const resp = await patchComment( - basicCase.id, - basicCase.comments[0].id, - 'updated comment', - basicCase.comments[0].version, - abortCtrl.signal - ); - expect(resp).toEqual(basicCase); - }); - }); - - describe('postCase', () => { - beforeEach(() => { - fetchMock.mockClear(); - fetchMock.mockResolvedValue(basicCaseSnake); - }); - const data = { - description: 'description', - tags: ['tag'], - title: 'title', - connector: { - id: 'none', - name: 'none', - type: ConnectorTypes.none, - fields: null, - }, - settings: { - syncAlerts: true, - }, - }; - - test('check url, method, signal', async () => { - await postCase(data, abortCtrl.signal); - expect(fetchMock).toHaveBeenCalledWith(`${CASES_URL}`, { - method: 'POST', - body: JSON.stringify(data), - signal: abortCtrl.signal, - }); - }); - - test('happy path', async () => { - const resp = await postCase(data, abortCtrl.signal); - expect(resp).toEqual(basicCase); - }); - }); - - describe('postComment', () => { - beforeEach(() => { - fetchMock.mockClear(); - fetchMock.mockResolvedValue(basicCaseSnake); - }); - const data = { - comment: 'comment', - type: CommentType.user as const, - }; - - test('check url, method, signal', async () => { - await postComment(data, basicCase.id, abortCtrl.signal); - expect(fetchMock).toHaveBeenCalledWith(`${CASES_URL}/${basicCase.id}/comments`, { - method: 'POST', - body: JSON.stringify(data), - signal: abortCtrl.signal, - }); - }); - - test('happy path', async () => { - const resp = await postComment(data, basicCase.id, abortCtrl.signal); - expect(resp).toEqual(basicCase); - }); - }); - - describe('pushCase', () => { - const connectorId = 'connectorId'; - - beforeEach(() => { - fetchMock.mockClear(); - fetchMock.mockResolvedValue(pushedCaseSnake); - }); - - test('check url, method, signal', async () => { - await pushCase(basicCase.id, connectorId, abortCtrl.signal); - expect(fetchMock).toHaveBeenCalledWith( - `${CASES_URL}/${basicCase.id}/connector/${connectorId}/_push`, - { - method: 'POST', - body: JSON.stringify({}), - signal: abortCtrl.signal, - } - ); - }); - - test('happy path', async () => { - const resp = await pushCase(basicCase.id, connectorId, abortCtrl.signal); - expect(resp).toEqual(pushedCase); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/containers/api.ts b/x-pack/plugins/cases/public/containers/api.ts deleted file mode 100644 index 5827083bfdbd2..0000000000000 --- a/x-pack/plugins/cases/public/containers/api.ts +++ /dev/null @@ -1,347 +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 { assign, omit } from 'lodash'; - -import { - CasePatchRequest, - CasePostRequest, - CaseResponse, - CasesFindResponse, - CasesResponse, - CasesStatusResponse, - CaseType, - CaseUserActionsResponse, - CommentRequest, - CommentType, - SubCasePatchRequest, - SubCaseResponse, - SubCasesResponse, - User, -} from '../../common'; - -import { - ACTION_TYPES_URL, - CASE_REPORTERS_URL, - CASE_STATUS_URL, - CASE_TAGS_URL, - CASES_URL, - SUB_CASE_DETAILS_URL, - SUB_CASES_PATCH_DEL_URL, -} from '../../common'; - -import { - getCaseCommentsUrl, - getCasePushUrl, - getCaseDetailsUrl, - getCaseUserActionUrl, - getSubCaseDetailsUrl, - getSubCaseUserActionUrl, -} from '../../common'; - -import { KibanaServices } from '../common/lib/kibana'; -import { StatusAll } from '../components/status'; - -import { - ActionLicense, - AllCases, - BulkUpdateStatus, - Case, - CasesStatus, - FetchCasesProps, - SortFieldCase, - CaseUserActions, -} from './types'; - -import { - convertToCamelCase, - convertAllCasesToCamel, - convertArrayToCamelCase, - decodeCaseResponse, - decodeCasesResponse, - decodeCasesFindResponse, - decodeCasesStatusResponse, - decodeCaseUserActionsResponse, -} from './utils'; - -export const getCase = async ( - caseId: string, - includeComments: boolean = true, - signal: AbortSignal -): Promise<Case> => { - const response = await KibanaServices.get().http.fetch<CaseResponse>(getCaseDetailsUrl(caseId), { - method: 'GET', - query: { - includeComments, - }, - signal, - }); - return convertToCamelCase<CaseResponse, Case>(decodeCaseResponse(response)); -}; - -export const getSubCase = async ( - caseId: string, - subCaseId: string, - includeComments: boolean = true, - signal: AbortSignal -): Promise<Case> => { - const [caseResponse, subCaseResponse] = await Promise.all([ - KibanaServices.get().http.fetch<CaseResponse>(getCaseDetailsUrl(caseId), { - method: 'GET', - query: { - includeComments: false, - }, - signal, - }), - KibanaServices.get().http.fetch<SubCaseResponse>(getSubCaseDetailsUrl(caseId, subCaseId), { - method: 'GET', - query: { - includeComments, - }, - signal, - }), - ]); - const response = assign<CaseResponse, SubCaseResponse>(caseResponse, subCaseResponse); - const subCaseIndex = response.subCaseIds?.findIndex((scId) => scId === response.id) ?? -1; - response.title = `${response.title}${subCaseIndex >= 0 ? ` ${subCaseIndex + 1}` : ''}`; - return convertToCamelCase<CaseResponse, Case>(decodeCaseResponse(response)); -}; - -export const getCasesStatus = async (signal: AbortSignal): Promise<CasesStatus> => { - const response = await KibanaServices.get().http.fetch<CasesStatusResponse>(CASE_STATUS_URL, { - method: 'GET', - signal, - }); - return convertToCamelCase<CasesStatusResponse, CasesStatus>(decodeCasesStatusResponse(response)); -}; - -export const getTags = async (signal: AbortSignal): Promise<string[]> => { - const response = await KibanaServices.get().http.fetch<string[]>(CASE_TAGS_URL, { - method: 'GET', - signal, - }); - return response ?? []; -}; - -export const getReporters = async (signal: AbortSignal): Promise<User[]> => { - const response = await KibanaServices.get().http.fetch<User[]>(CASE_REPORTERS_URL, { - method: 'GET', - signal, - }); - return response ?? []; -}; - -export const getCaseUserActions = async ( - caseId: string, - signal: AbortSignal -): Promise<CaseUserActions[]> => { - const response = await KibanaServices.get().http.fetch<CaseUserActionsResponse>( - getCaseUserActionUrl(caseId), - { - method: 'GET', - signal, - } - ); - return convertArrayToCamelCase(decodeCaseUserActionsResponse(response)) as CaseUserActions[]; -}; - -export const getSubCaseUserActions = async ( - caseId: string, - subCaseId: string, - signal: AbortSignal -): Promise<CaseUserActions[]> => { - const response = await KibanaServices.get().http.fetch<CaseUserActionsResponse>( - getSubCaseUserActionUrl(caseId, subCaseId), - { - method: 'GET', - signal, - } - ); - return convertArrayToCamelCase(decodeCaseUserActionsResponse(response)) as CaseUserActions[]; -}; - -export const getCases = async ({ - filterOptions = { - onlyCollectionType: false, - search: '', - reporters: [], - status: StatusAll, - tags: [], - }, - queryParams = { - page: 1, - perPage: 20, - sortField: SortFieldCase.createdAt, - sortOrder: 'desc', - }, - signal, -}: FetchCasesProps): Promise<AllCases> => { - const query = { - reporters: filterOptions.reporters.map((r) => r.username ?? '').filter((r) => r !== ''), - tags: filterOptions.tags.map((t) => `"${t.replace(/"/g, '\\"')}"`), - status: filterOptions.status, - ...(filterOptions.search.length > 0 ? { search: filterOptions.search } : {}), - ...(filterOptions.onlyCollectionType ? { type: CaseType.collection } : {}), - ...queryParams, - }; - const response = await KibanaServices.get().http.fetch<CasesFindResponse>(`${CASES_URL}/_find`, { - method: 'GET', - query: query.status === StatusAll ? omit(query, ['status']) : query, - signal, - }); - return convertAllCasesToCamel(decodeCasesFindResponse(response)); -}; - -export const postCase = async (newCase: CasePostRequest, signal: AbortSignal): Promise<Case> => { - const response = await KibanaServices.get().http.fetch<CaseResponse>(CASES_URL, { - method: 'POST', - body: JSON.stringify(newCase), - signal, - }); - return convertToCamelCase<CaseResponse, Case>(decodeCaseResponse(response)); -}; - -export const patchCase = async ( - caseId: string, - updatedCase: Pick< - CasePatchRequest, - 'description' | 'status' | 'tags' | 'title' | 'settings' | 'connector' - >, - version: string, - signal: AbortSignal -): Promise<Case[]> => { - const response = await KibanaServices.get().http.fetch<CasesResponse>(CASES_URL, { - method: 'PATCH', - body: JSON.stringify({ cases: [{ ...updatedCase, id: caseId, version }] }), - signal, - }); - return convertToCamelCase<CasesResponse, Case[]>(decodeCasesResponse(response)); -}; - -export const patchSubCase = async ( - caseId: string, - subCaseId: string, - updatedSubCase: Pick<SubCasePatchRequest, 'status'>, - version: string, - signal: AbortSignal -): Promise<Case[]> => { - const subCaseResponse = await KibanaServices.get().http.fetch<SubCasesResponse>( - SUB_CASE_DETAILS_URL, - { - method: 'PATCH', - body: JSON.stringify({ cases: [{ ...updatedSubCase, id: caseId, version }] }), - signal, - } - ); - const caseResponse = await KibanaServices.get().http.fetch<CaseResponse>( - getCaseDetailsUrl(caseId), - { - method: 'GET', - query: { - includeComments: false, - }, - signal, - } - ); - const response = subCaseResponse.map((subCaseResp) => assign(caseResponse, subCaseResp)); - return convertToCamelCase<CasesResponse, Case[]>(decodeCasesResponse(response)); -}; - -export const patchCasesStatus = async ( - cases: BulkUpdateStatus[], - signal: AbortSignal -): Promise<Case[]> => { - const response = await KibanaServices.get().http.fetch<CasesResponse>(CASES_URL, { - method: 'PATCH', - body: JSON.stringify({ cases }), - signal, - }); - return convertToCamelCase<CasesResponse, Case[]>(decodeCasesResponse(response)); -}; - -export const postComment = async ( - newComment: CommentRequest, - caseId: string, - signal: AbortSignal, - subCaseId?: string -): Promise<Case> => { - const response = await KibanaServices.get().http.fetch<CaseResponse>( - `${CASES_URL}/${caseId}/comments`, - { - method: 'POST', - body: JSON.stringify(newComment), - ...(subCaseId ? { query: { subCaseId } } : {}), - signal, - } - ); - return convertToCamelCase<CaseResponse, Case>(decodeCaseResponse(response)); -}; - -export const patchComment = async ( - caseId: string, - commentId: string, - commentUpdate: string, - version: string, - signal: AbortSignal, - subCaseId?: string -): Promise<Case> => { - const response = await KibanaServices.get().http.fetch<CaseResponse>(getCaseCommentsUrl(caseId), { - method: 'PATCH', - body: JSON.stringify({ - comment: commentUpdate, - type: CommentType.user, - id: commentId, - version, - }), - ...(subCaseId ? { query: { subCaseId } } : {}), - signal, - }); - return convertToCamelCase<CaseResponse, Case>(decodeCaseResponse(response)); -}; - -export const deleteCases = async (caseIds: string[], signal: AbortSignal): Promise<string> => { - const response = await KibanaServices.get().http.fetch<string>(CASES_URL, { - method: 'DELETE', - query: { ids: JSON.stringify(caseIds) }, - signal, - }); - return response; -}; - -export const deleteSubCases = async (caseIds: string[], signal: AbortSignal): Promise<string> => { - const response = await KibanaServices.get().http.fetch<string>(SUB_CASES_PATCH_DEL_URL, { - method: 'DELETE', - query: { ids: JSON.stringify(caseIds) }, - signal, - }); - return response; -}; - -export const pushCase = async ( - caseId: string, - connectorId: string, - signal: AbortSignal -): Promise<Case> => { - const response = await KibanaServices.get().http.fetch<CaseResponse>( - getCasePushUrl(caseId, connectorId), - { - method: 'POST', - body: JSON.stringify({}), - signal, - } - ); - - return convertToCamelCase<CaseResponse, Case>(decodeCaseResponse(response)); -}; - -export const getActionLicense = async (signal: AbortSignal): Promise<ActionLicense[]> => { - const response = await KibanaServices.get().http.fetch<ActionLicense[]>(ACTION_TYPES_URL, { - method: 'GET', - signal, - }); - return response; -}; diff --git a/x-pack/plugins/cases/public/containers/configure/__mocks__/api.ts b/x-pack/plugins/cases/public/containers/configure/__mocks__/api.ts deleted file mode 100644 index ea4b92706b4d1..0000000000000 --- a/x-pack/plugins/cases/public/containers/configure/__mocks__/api.ts +++ /dev/null @@ -1,36 +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 { - CasesConfigurePatch, - CasesConfigureRequest, - ActionConnector, - ActionTypeConnector, -} from '../../../../common'; - -import { ApiProps } from '../../types'; -import { CaseConfigure } from '../types'; -import { connectorsMock, caseConfigurationCamelCaseResponseMock, actionTypesMock } from '../mock'; - -export const fetchConnectors = async ({ signal }: ApiProps): Promise<ActionConnector[]> => - Promise.resolve(connectorsMock); - -export const getCaseConfigure = async ({ signal }: ApiProps): Promise<CaseConfigure> => - Promise.resolve(caseConfigurationCamelCaseResponseMock); - -export const postCaseConfigure = async ( - caseConfiguration: CasesConfigureRequest, - signal: AbortSignal -): Promise<CaseConfigure> => Promise.resolve(caseConfigurationCamelCaseResponseMock); - -export const patchCaseConfigure = async ( - caseConfiguration: CasesConfigurePatch, - signal: AbortSignal -): Promise<CaseConfigure> => Promise.resolve(caseConfigurationCamelCaseResponseMock); - -export const fetchActionTypes = async ({ signal }: ApiProps): Promise<ActionTypeConnector[]> => - Promise.resolve(actionTypesMock); diff --git a/x-pack/plugins/cases/public/containers/configure/api.test.ts b/x-pack/plugins/cases/public/containers/configure/api.test.ts deleted file mode 100644 index ae749b4391776..0000000000000 --- a/x-pack/plugins/cases/public/containers/configure/api.test.ts +++ /dev/null @@ -1,154 +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 { - fetchConnectors, - getCaseConfigure, - postCaseConfigure, - patchCaseConfigure, - fetchActionTypes, -} from './api'; -import { - connectorsMock, - actionTypesMock, - caseConfigurationMock, - caseConfigurationResposeMock, - caseConfigurationCamelCaseResponseMock, -} from './mock'; -import { ConnectorTypes } from '../../../common'; -import { KibanaServices } from '../../common/lib/kibana'; - -const abortCtrl = new AbortController(); -const mockKibanaServices = KibanaServices.get as jest.Mock; -jest.mock('../../common/lib/kibana'); - -const fetchMock = jest.fn(); -mockKibanaServices.mockReturnValue({ http: { fetch: fetchMock } }); - -describe('Case Configuration API', () => { - describe('fetch connectors', () => { - beforeEach(() => { - fetchMock.mockClear(); - fetchMock.mockResolvedValue(connectorsMock); - }); - - test('check url, method, signal', async () => { - await fetchConnectors({ signal: abortCtrl.signal }); - expect(fetchMock).toHaveBeenCalledWith('/api/cases/configure/connectors/_find', { - method: 'GET', - signal: abortCtrl.signal, - }); - }); - - test('happy path', async () => { - const resp = await fetchConnectors({ signal: abortCtrl.signal }); - expect(resp).toEqual(connectorsMock); - }); - }); - - describe('fetch configuration', () => { - beforeEach(() => { - fetchMock.mockClear(); - fetchMock.mockResolvedValue(caseConfigurationResposeMock); - }); - - test('check url, method, signal', async () => { - await getCaseConfigure({ signal: abortCtrl.signal }); - expect(fetchMock).toHaveBeenCalledWith('/api/cases/configure', { - method: 'GET', - signal: abortCtrl.signal, - }); - }); - - test('happy path', async () => { - const resp = await getCaseConfigure({ signal: abortCtrl.signal }); - expect(resp).toEqual(caseConfigurationCamelCaseResponseMock); - }); - - test('return null on empty response', async () => { - fetchMock.mockResolvedValue({}); - const resp = await getCaseConfigure({ signal: abortCtrl.signal }); - expect(resp).toBe(null); - }); - }); - - describe('create configuration', () => { - beforeEach(() => { - fetchMock.mockClear(); - fetchMock.mockResolvedValue(caseConfigurationResposeMock); - }); - - test('check url, body, method, signal', async () => { - await postCaseConfigure(caseConfigurationMock, abortCtrl.signal); - expect(fetchMock).toHaveBeenCalledWith('/api/cases/configure', { - body: - '{"connector":{"id":"123","name":"My connector","type":".jira","fields":null},"closure_type":"close-by-user"}', - method: 'POST', - signal: abortCtrl.signal, - }); - }); - - test('happy path', async () => { - const resp = await postCaseConfigure(caseConfigurationMock, abortCtrl.signal); - expect(resp).toEqual(caseConfigurationCamelCaseResponseMock); - }); - }); - - describe('update configuration', () => { - beforeEach(() => { - fetchMock.mockClear(); - fetchMock.mockResolvedValue(caseConfigurationResposeMock); - }); - - test('check url, body, method, signal', async () => { - await patchCaseConfigure( - { - connector: { id: '456', name: 'My Connector 2', type: ConnectorTypes.none, fields: null }, - version: 'WzHJ12', - }, - abortCtrl.signal - ); - expect(fetchMock).toHaveBeenCalledWith('/api/cases/configure', { - body: - '{"connector":{"id":"456","name":"My Connector 2","type":".none","fields":null},"version":"WzHJ12"}', - method: 'PATCH', - signal: abortCtrl.signal, - }); - }); - - test('happy path', async () => { - const resp = await patchCaseConfigure( - { - connector: { id: '456', name: 'My Connector 2', type: ConnectorTypes.none, fields: null }, - version: 'WzHJ12', - }, - abortCtrl.signal - ); - expect(resp).toEqual(caseConfigurationCamelCaseResponseMock); - }); - }); - - describe('fetch actionTypes', () => { - beforeEach(() => { - fetchMock.mockClear(); - fetchMock.mockResolvedValue(actionTypesMock); - }); - - test('check url, method, signal', async () => { - await fetchActionTypes({ signal: abortCtrl.signal }); - expect(fetchMock).toHaveBeenCalledWith('/api/actions/list_action_types', { - method: 'GET', - signal: abortCtrl.signal, - }); - }); - - test('happy path', async () => { - const resp = await fetchActionTypes({ signal: abortCtrl.signal }); - expect(resp).toEqual(actionTypesMock); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/containers/configure/api.ts b/x-pack/plugins/cases/public/containers/configure/api.ts deleted file mode 100644 index 006370fcb5533..0000000000000 --- a/x-pack/plugins/cases/public/containers/configure/api.ts +++ /dev/null @@ -1,103 +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 { isEmpty } from 'lodash/fp'; -import { - ActionConnector, - ActionTypeConnector, - CasesConfigurePatch, - CasesConfigureResponse, - CasesConfigureRequest, -} from '../../../common'; -import { KibanaServices } from '../../common/lib/kibana'; - -import { - CASE_CONFIGURE_CONNECTORS_URL, - CASE_CONFIGURE_URL, - ACTION_TYPES_URL, -} from '../../../common'; - -import { ApiProps } from '../types'; -import { convertToCamelCase, decodeCaseConfigureResponse } from '../utils'; -import { CaseConfigure } from './types'; - -export const fetchConnectors = async ({ signal }: ApiProps): Promise<ActionConnector[]> => { - const response = await KibanaServices.get().http.fetch(`${CASE_CONFIGURE_CONNECTORS_URL}/_find`, { - method: 'GET', - signal, - }); - - return response; -}; - -export const getCaseConfigure = async ({ signal }: ApiProps): Promise<CaseConfigure | null> => { - const response = await KibanaServices.get().http.fetch<CasesConfigureResponse>( - CASE_CONFIGURE_URL, - { - method: 'GET', - signal, - } - ); - - return !isEmpty(response) - ? convertToCamelCase<CasesConfigureResponse, CaseConfigure>( - decodeCaseConfigureResponse(response) - ) - : null; -}; - -export const getConnectorMappings = async ({ signal }: ApiProps): Promise<ActionConnector[]> => { - const response = await KibanaServices.get().http.fetch(`${CASE_CONFIGURE_CONNECTORS_URL}/_find`, { - method: 'GET', - signal, - }); - - return response; -}; - -export const postCaseConfigure = async ( - caseConfiguration: CasesConfigureRequest, - signal: AbortSignal -): Promise<CaseConfigure> => { - const response = await KibanaServices.get().http.fetch<CasesConfigureResponse>( - CASE_CONFIGURE_URL, - { - method: 'POST', - body: JSON.stringify(caseConfiguration), - signal, - } - ); - return convertToCamelCase<CasesConfigureResponse, CaseConfigure>( - decodeCaseConfigureResponse(response) - ); -}; - -export const patchCaseConfigure = async ( - caseConfiguration: CasesConfigurePatch, - signal: AbortSignal -): Promise<CaseConfigure> => { - const response = await KibanaServices.get().http.fetch<CasesConfigureResponse>( - CASE_CONFIGURE_URL, - { - method: 'PATCH', - body: JSON.stringify(caseConfiguration), - signal, - } - ); - return convertToCamelCase<CasesConfigureResponse, CaseConfigure>( - decodeCaseConfigureResponse(response) - ); -}; - -export const fetchActionTypes = async ({ signal }: ApiProps): Promise<ActionTypeConnector[]> => { - const response = await KibanaServices.get().http.fetch(ACTION_TYPES_URL, { - method: 'GET', - signal, - }); - - return response; -}; diff --git a/x-pack/plugins/cases/public/containers/configure/mock.ts b/x-pack/plugins/cases/public/containers/configure/mock.ts deleted file mode 100644 index 766452e3e58e7..0000000000000 --- a/x-pack/plugins/cases/public/containers/configure/mock.ts +++ /dev/null @@ -1,160 +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 { - ActionConnector, - ActionTypeConnector, - CasesConfigureResponse, - CasesConfigureRequest, - ConnectorTypes, -} from '../../../common'; -import { CaseConfigure, CaseConnectorMapping } from './types'; - -export const mappings: CaseConnectorMapping[] = [ - { - source: 'title', - target: 'short_description', - actionType: 'overwrite', - }, - { - source: 'description', - target: 'description', - actionType: 'overwrite', - }, - { - source: 'comments', - target: 'comments', - actionType: 'append', - }, -]; - -export const connectorsMock: ActionConnector[] = [ - { - id: 'servicenow-1', - actionTypeId: '.servicenow', - name: 'My Connector', - config: { - apiUrl: 'https://instance1.service-now.com', - }, - isPreconfigured: false, - }, - { - id: 'resilient-2', - actionTypeId: '.resilient', - name: 'My Connector 2', - config: { - apiUrl: 'https://test/', - orgId: '201', - }, - isPreconfigured: false, - }, - { - id: 'jira-1', - actionTypeId: '.jira', - name: 'Jira', - config: { - apiUrl: 'https://instance.atlassian.ne', - }, - isPreconfigured: false, - }, - { - id: 'servicenow-sir', - actionTypeId: '.servicenow-sir', - name: 'My Connector SIR', - config: { - apiUrl: 'https://instance1.service-now.com', - }, - isPreconfigured: false, - }, -]; - -export const actionTypesMock: ActionTypeConnector[] = [ - { - id: '.email', - name: 'Email', - minimumLicenseRequired: 'gold', - enabled: true, - enabledInConfig: true, - enabledInLicense: true, - }, - { - id: '.index', - name: 'Index', - minimumLicenseRequired: 'basic', - enabled: true, - enabledInConfig: true, - enabledInLicense: true, - }, - { - id: '.servicenow', - name: 'ServiceNow', - minimumLicenseRequired: 'platinum', - enabled: false, - enabledInConfig: true, - enabledInLicense: true, - }, - { - id: '.jira', - name: 'Jira', - minimumLicenseRequired: 'gold', - enabled: true, - enabledInConfig: true, - enabledInLicense: true, - }, - { - id: '.resilient', - name: 'IBM Resilient', - minimumLicenseRequired: 'platinum', - enabled: false, - enabledInConfig: true, - enabledInLicense: true, - }, -]; - -export const caseConfigurationResposeMock: CasesConfigureResponse = { - created_at: '2020-04-06T13:03:18.657Z', - created_by: { username: 'elastic', full_name: 'Elastic', email: 'elastic@elastic.co' }, - connector: { - id: '123', - name: 'My connector', - type: ConnectorTypes.jira, - fields: null, - }, - closure_type: 'close-by-pushing', - error: null, - mappings: [], - updated_at: '2020-04-06T14:03:18.657Z', - updated_by: { username: 'elastic', full_name: 'Elastic', email: 'elastic@elastic.co' }, - version: 'WzHJ12', -}; - -export const caseConfigurationMock: CasesConfigureRequest = { - connector: { - id: '123', - name: 'My connector', - type: ConnectorTypes.jira, - fields: null, - }, - closure_type: 'close-by-user', -}; - -export const caseConfigurationCamelCaseResponseMock: CaseConfigure = { - createdAt: '2020-04-06T13:03:18.657Z', - createdBy: { username: 'elastic', fullName: 'Elastic', email: 'elastic@elastic.co' }, - connector: { - id: '123', - name: 'My connector', - type: ConnectorTypes.jira, - fields: null, - }, - closureType: 'close-by-pushing', - error: null, - mappings: [], - updatedAt: '2020-04-06T14:03:18.657Z', - updatedBy: { username: 'elastic', fullName: 'Elastic', email: 'elastic@elastic.co' }, - version: 'WzHJ12', -}; diff --git a/x-pack/plugins/cases/public/containers/configure/translations.ts b/x-pack/plugins/cases/public/containers/configure/translations.ts deleted file mode 100644 index e77b9f57c8f4c..0000000000000 --- a/x-pack/plugins/cases/public/containers/configure/translations.ts +++ /dev/null @@ -1,14 +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 { i18n } from '@kbn/i18n'; - -export * from '../translations'; - -export const SUCCESS_CONFIGURE = i18n.translate('xpack.cases.configure.successSaveToast', { - defaultMessage: 'Saved external connection settings', -}); diff --git a/x-pack/plugins/cases/public/containers/configure/types.ts b/x-pack/plugins/cases/public/containers/configure/types.ts deleted file mode 100644 index b021ae2163fa2..0000000000000 --- a/x-pack/plugins/cases/public/containers/configure/types.ts +++ /dev/null @@ -1,46 +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 { ElasticUser } from '../types'; -import { - ActionConnector, - ActionTypeConnector, - ActionType, - CaseConnector, - CaseField, - CasesConfigure, - ClosureType, - ThirdPartyField, -} from '../../../common'; - -export { - ActionConnector, - ActionTypeConnector, - ActionType, - CaseConnector, - CaseField, - ClosureType, - ThirdPartyField, -}; - -export interface CaseConnectorMapping { - actionType: ActionType; - source: CaseField; - target: string; -} - -export interface CaseConfigure { - closureType: ClosureType; - connector: CasesConfigure['connector']; - createdAt: string; - createdBy: ElasticUser; - error: string | null; - mappings: CaseConnectorMapping[]; - updatedAt: string; - updatedBy: ElasticUser; - version: string; -} diff --git a/x-pack/plugins/cases/public/containers/configure/use_action_types.test.tsx b/x-pack/plugins/cases/public/containers/configure/use_action_types.test.tsx deleted file mode 100644 index 25017f7931db8..0000000000000 --- a/x-pack/plugins/cases/public/containers/configure/use_action_types.test.tsx +++ /dev/null @@ -1,102 +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 { renderHook, act } from '@testing-library/react-hooks'; -import { useActionTypes, UseActionTypesResponse } from './use_action_types'; -import { actionTypesMock } from './mock'; -import * as api from './api'; - -jest.mock('./api'); - -describe('useActionTypes', () => { - beforeEach(() => { - jest.clearAllMocks(); - jest.restoreAllMocks(); - }); - - test('init', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UseActionTypesResponse>(() => - useActionTypes() - ); - await waitForNextUpdate(); - expect(result.current).toEqual({ - loading: true, - actionTypes: [], - refetchActionTypes: result.current.refetchActionTypes, - }); - }); - }); - - test('fetch action types', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UseActionTypesResponse>(() => - useActionTypes() - ); - - await waitForNextUpdate(); - await waitForNextUpdate(); - - expect(result.current).toEqual({ - loading: false, - actionTypes: actionTypesMock, - refetchActionTypes: result.current.refetchActionTypes, - }); - }); - }); - - test('refetch actionTypes', async () => { - const spyOnfetchActionTypes = jest.spyOn(api, 'fetchActionTypes'); - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UseActionTypesResponse>(() => - useActionTypes() - ); - - await waitForNextUpdate(); - await waitForNextUpdate(); - - result.current.refetchActionTypes(); - expect(spyOnfetchActionTypes).toHaveBeenCalledTimes(2); - }); - }); - - test('set isLoading to true when refetching actionTypes', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UseActionTypesResponse>(() => - useActionTypes() - ); - - await waitForNextUpdate(); - await waitForNextUpdate(); - - result.current.refetchActionTypes(); - - expect(result.current.loading).toBe(true); - }); - }); - - test('unhappy path', async () => { - const spyOnfetchActionTypes = jest.spyOn(api, 'fetchActionTypes'); - spyOnfetchActionTypes.mockImplementation(() => { - throw new Error('Something went wrong'); - }); - - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UseActionTypesResponse>(() => - useActionTypes() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - - expect(result.current).toEqual({ - loading: false, - actionTypes: [], - refetchActionTypes: result.current.refetchActionTypes, - }); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/containers/configure/use_action_types.tsx b/x-pack/plugins/cases/public/containers/configure/use_action_types.tsx deleted file mode 100644 index 206952661e672..0000000000000 --- a/x-pack/plugins/cases/public/containers/configure/use_action_types.tsx +++ /dev/null @@ -1,73 +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 { useState, useEffect, useCallback, useRef } from 'react'; - -import { useStateToaster, errorToToaster } from '../../components/toasters'; -import * as i18n from '../translations'; -import { fetchActionTypes } from './api'; -import { ActionTypeConnector } from './types'; - -export interface UseActionTypesResponse { - loading: boolean; - actionTypes: ActionTypeConnector[]; - refetchActionTypes: () => void; -} - -export const useActionTypes = (): UseActionTypesResponse => { - const [, dispatchToaster] = useStateToaster(); - const [loading, setLoading] = useState(true); - const [actionTypes, setActionTypes] = useState<ActionTypeConnector[]>([]); - const isCancelledRef = useRef(false); - const abortCtrlRef = useRef(new AbortController()); - const queryFirstTime = useRef(true); - - const refetchActionTypes = useCallback(async () => { - try { - setLoading(true); - isCancelledRef.current = false; - abortCtrlRef.current.abort(); - abortCtrlRef.current = new AbortController(); - - const res = await fetchActionTypes({ signal: abortCtrlRef.current.signal }); - - if (!isCancelledRef.current) { - setLoading(false); - setActionTypes(res); - } - } catch (error) { - if (!isCancelledRef.current) { - setLoading(false); - setActionTypes([]); - errorToToaster({ - title: i18n.ERROR_TITLE, - error: error.body && error.body.message ? new Error(error.body.message) : error, - dispatchToaster, - }); - } - } - }, [dispatchToaster]); - - useEffect(() => { - if (queryFirstTime.current) { - refetchActionTypes(); - queryFirstTime.current = false; - } - - return () => { - isCancelledRef.current = true; - abortCtrlRef.current.abort(); - queryFirstTime.current = true; - }; - }, [refetchActionTypes]); - - return { - loading, - actionTypes, - refetchActionTypes, - }; -}; diff --git a/x-pack/plugins/cases/public/containers/configure/use_configure.test.tsx b/x-pack/plugins/cases/public/containers/configure/use_configure.test.tsx deleted file mode 100644 index 4e4db4cb5e82e..0000000000000 --- a/x-pack/plugins/cases/public/containers/configure/use_configure.test.tsx +++ /dev/null @@ -1,326 +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 { renderHook, act } from '@testing-library/react-hooks'; -import { - initialState, - useCaseConfigure, - ReturnUseCaseConfigure, - ConnectorConfiguration, -} from './use_configure'; -import { mappings, caseConfigurationCamelCaseResponseMock } from './mock'; -import * as api from './api'; -import { ConnectorTypes } from '../../../common'; - -jest.mock('./api'); -const mockErrorToToaster = jest.fn(); -jest.mock('../../components/toasters', () => { - const original = jest.requireActual('../../components/toasters'); - return { - ...original, - errorToToaster: () => mockErrorToToaster(), - }; -}); -const configuration: ConnectorConfiguration = { - connector: { - id: '456', - name: 'My connector 2', - type: ConnectorTypes.none, - fields: null, - }, - closureType: 'close-by-pushing', -}; - -describe('useConfigure', () => { - beforeEach(() => { - jest.clearAllMocks(); - jest.restoreAllMocks(); - }); - - test('init', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, ReturnUseCaseConfigure>(() => - useCaseConfigure() - ); - await waitForNextUpdate(); - expect(result.current).toEqual({ - ...initialState, - refetchCaseConfigure: result.current.refetchCaseConfigure, - persistCaseConfigure: result.current.persistCaseConfigure, - setCurrentConfiguration: result.current.setCurrentConfiguration, - setConnector: result.current.setConnector, - setClosureType: result.current.setClosureType, - setMappings: result.current.setMappings, - }); - }); - }); - - test('fetch case configuration', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, ReturnUseCaseConfigure>(() => - useCaseConfigure() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(result.current).toEqual({ - ...initialState, - closureType: caseConfigurationCamelCaseResponseMock.closureType, - connector: caseConfigurationCamelCaseResponseMock.connector, - currentConfiguration: { - closureType: caseConfigurationCamelCaseResponseMock.closureType, - connector: caseConfigurationCamelCaseResponseMock.connector, - }, - mappings: [], - firstLoad: true, - loading: false, - persistCaseConfigure: result.current.persistCaseConfigure, - refetchCaseConfigure: result.current.refetchCaseConfigure, - setClosureType: result.current.setClosureType, - setConnector: result.current.setConnector, - setCurrentConfiguration: result.current.setCurrentConfiguration, - setMappings: result.current.setMappings, - version: caseConfigurationCamelCaseResponseMock.version, - }); - }); - }); - - test('refetch case configuration', async () => { - const spyOnGetCaseConfigure = jest.spyOn(api, 'getCaseConfigure'); - - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, ReturnUseCaseConfigure>(() => - useCaseConfigure() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - result.current.refetchCaseConfigure(); - expect(spyOnGetCaseConfigure).toHaveBeenCalledTimes(2); - }); - }); - - test('correctly sets mappings', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, ReturnUseCaseConfigure>(() => - useCaseConfigure() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(result.current.mappings).toEqual([]); - result.current.setMappings(mappings); - expect(result.current.mappings).toEqual(mappings); - }); - }); - - test('set isLoading to true when fetching case configuration', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, ReturnUseCaseConfigure>(() => - useCaseConfigure() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - result.current.refetchCaseConfigure(); - - expect(result.current.loading).toBe(true); - }); - }); - - test('persist case configuration', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, ReturnUseCaseConfigure>(() => - useCaseConfigure() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - result.current.persistCaseConfigure(configuration); - expect(result.current.persistLoading).toBeTruthy(); - }); - }); - - test('save case configuration - postCaseConfigure', async () => { - // When there is no version, a configuration is created. Otherwise is updated. - const spyOnGetCaseConfigure = jest.spyOn(api, 'getCaseConfigure'); - spyOnGetCaseConfigure.mockImplementation(() => - Promise.resolve({ - ...caseConfigurationCamelCaseResponseMock, - version: '', - }) - ); - - const spyOnPostCaseConfigure = jest.spyOn(api, 'postCaseConfigure'); - spyOnPostCaseConfigure.mockImplementation(() => - Promise.resolve({ - ...caseConfigurationCamelCaseResponseMock, - ...configuration, - }) - ); - - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, ReturnUseCaseConfigure>(() => - useCaseConfigure() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(mockErrorToToaster).not.toHaveBeenCalled(); - - result.current.persistCaseConfigure(configuration); - - expect(result.current.connector.id).toEqual('123'); - await waitForNextUpdate(); - expect(result.current.connector.id).toEqual('456'); - }); - }); - - test('Displays error when present - getCaseConfigure', async () => { - const spyOnGetCaseConfigure = jest.spyOn(api, 'getCaseConfigure'); - spyOnGetCaseConfigure.mockImplementation(() => - Promise.resolve({ - ...caseConfigurationCamelCaseResponseMock, - error: 'uh oh homeboy', - version: '', - }) - ); - - await act(async () => { - const { waitForNextUpdate } = renderHook<string, ReturnUseCaseConfigure>(() => - useCaseConfigure() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(mockErrorToToaster).toHaveBeenCalled(); - }); - }); - - test('Displays error when present - postCaseConfigure', async () => { - // When there is no version, a configuration is created. Otherwise is updated. - const spyOnGetCaseConfigure = jest.spyOn(api, 'getCaseConfigure'); - spyOnGetCaseConfigure.mockImplementation(() => - Promise.resolve({ - ...caseConfigurationCamelCaseResponseMock, - version: '', - }) - ); - - const spyOnPostCaseConfigure = jest.spyOn(api, 'postCaseConfigure'); - spyOnPostCaseConfigure.mockImplementation(() => - Promise.resolve({ - ...caseConfigurationCamelCaseResponseMock, - ...configuration, - error: 'uh oh homeboy', - }) - ); - - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, ReturnUseCaseConfigure>(() => - useCaseConfigure() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(mockErrorToToaster).not.toHaveBeenCalled(); - - result.current.persistCaseConfigure(configuration); - expect(mockErrorToToaster).not.toHaveBeenCalled(); - await waitForNextUpdate(); - expect(mockErrorToToaster).toHaveBeenCalled(); - }); - }); - - test('save case configuration - patchCaseConfigure', async () => { - const spyOnPatchCaseConfigure = jest.spyOn(api, 'patchCaseConfigure'); - spyOnPatchCaseConfigure.mockImplementation(() => - Promise.resolve({ - ...caseConfigurationCamelCaseResponseMock, - ...configuration, - }) - ); - - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, ReturnUseCaseConfigure>(() => - useCaseConfigure() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - - result.current.persistCaseConfigure(configuration); - - expect(result.current.connector.id).toEqual('123'); - await waitForNextUpdate(); - expect(result.current.connector.id).toEqual('456'); - }); - }); - - test('unhappy path - fetch case configuration', async () => { - const spyOnGetCaseConfigure = jest.spyOn(api, 'getCaseConfigure'); - spyOnGetCaseConfigure.mockImplementation(() => { - throw new Error('Something went wrong'); - }); - - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, ReturnUseCaseConfigure>(() => - useCaseConfigure() - ); - - await waitForNextUpdate(); - await waitForNextUpdate(); - - expect(result.current).toEqual({ - ...initialState, - loading: false, - persistCaseConfigure: result.current.persistCaseConfigure, - persistLoading: false, - refetchCaseConfigure: result.current.refetchCaseConfigure, - setClosureType: result.current.setClosureType, - setConnector: result.current.setConnector, - setCurrentConfiguration: result.current.setCurrentConfiguration, - setMappings: result.current.setMappings, - }); - }); - }); - - test('unhappy path - persist case configuration', async () => { - const spyOnGetCaseConfigure = jest.spyOn(api, 'getCaseConfigure'); - spyOnGetCaseConfigure.mockImplementation(() => - Promise.resolve({ - ...caseConfigurationCamelCaseResponseMock, - version: '', - }) - ); - const spyOnPostCaseConfigure = jest.spyOn(api, 'postCaseConfigure'); - spyOnPostCaseConfigure.mockImplementation(() => { - throw new Error('Something went wrong'); - }); - - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, ReturnUseCaseConfigure>(() => - useCaseConfigure() - ); - - await waitForNextUpdate(); - await waitForNextUpdate(); - - result.current.persistCaseConfigure(configuration); - - expect(result.current).toEqual({ - ...initialState, - closureType: caseConfigurationCamelCaseResponseMock.closureType, - connector: caseConfigurationCamelCaseResponseMock.connector, - currentConfiguration: { - closureType: caseConfigurationCamelCaseResponseMock.closureType, - connector: caseConfigurationCamelCaseResponseMock.connector, - }, - firstLoad: true, - loading: false, - mappings: [], - persistCaseConfigure: result.current.persistCaseConfigure, - refetchCaseConfigure: result.current.refetchCaseConfigure, - setClosureType: result.current.setClosureType, - setConnector: result.current.setConnector, - setCurrentConfiguration: result.current.setCurrentConfiguration, - setMappings: result.current.setMappings, - }); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/containers/configure/use_configure.tsx b/x-pack/plugins/cases/public/containers/configure/use_configure.tsx deleted file mode 100644 index 3d5e43b2772a9..0000000000000 --- a/x-pack/plugins/cases/public/containers/configure/use_configure.tsx +++ /dev/null @@ -1,361 +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 { useEffect, useCallback, useReducer, useRef } from 'react'; -import { getCaseConfigure, patchCaseConfigure, postCaseConfigure } from './api'; - -import { useStateToaster, errorToToaster, displaySuccessToast } from '../../components/toasters'; -import * as i18n from './translations'; -import { ClosureType, CaseConfigure, CaseConnector, CaseConnectorMapping } from './types'; -import { ConnectorTypes } from '../../../common'; - -export type ConnectorConfiguration = { connector: CaseConnector } & { - closureType: CaseConfigure['closureType']; -}; - -export interface State extends ConnectorConfiguration { - currentConfiguration: ConnectorConfiguration; - firstLoad: boolean; - loading: boolean; - mappings: CaseConnectorMapping[]; - persistLoading: boolean; - version: string; -} -export type Action = - | { - type: 'setCurrentConfiguration'; - currentConfiguration: ConnectorConfiguration; - } - | { - type: 'setConnector'; - connector: CaseConnector; - } - | { - type: 'setLoading'; - payload: boolean; - } - | { - type: 'setFirstLoad'; - payload: boolean; - } - | { - type: 'setPersistLoading'; - payload: boolean; - } - | { - type: 'setVersion'; - payload: string; - } - | { - type: 'setClosureType'; - closureType: ClosureType; - } - | { - type: 'setMappings'; - mappings: CaseConnectorMapping[]; - }; - -export const configureCasesReducer = (state: State, action: Action) => { - switch (action.type) { - case 'setLoading': - return { - ...state, - loading: action.payload, - }; - case 'setFirstLoad': - return { - ...state, - firstLoad: action.payload, - }; - case 'setPersistLoading': - return { - ...state, - persistLoading: action.payload, - }; - case 'setVersion': - return { - ...state, - version: action.payload, - }; - case 'setCurrentConfiguration': { - return { - ...state, - currentConfiguration: { ...action.currentConfiguration }, - }; - } - case 'setConnector': { - return { - ...state, - connector: action.connector, - }; - } - case 'setClosureType': { - return { - ...state, - closureType: action.closureType, - }; - } - case 'setMappings': { - return { - ...state, - mappings: action.mappings, - }; - } - default: - return state; - } -}; - -export interface ReturnUseCaseConfigure extends State { - persistCaseConfigure: ({ connector, closureType }: ConnectorConfiguration) => unknown; - refetchCaseConfigure: () => void; - setClosureType: (closureType: ClosureType) => void; - setConnector: (connector: CaseConnector) => void; - setCurrentConfiguration: (configuration: ConnectorConfiguration) => void; - setMappings: (newMapping: CaseConnectorMapping[]) => void; -} - -export const initialState: State = { - closureType: 'close-by-user', - connector: { - fields: null, - id: 'none', - name: 'none', - type: ConnectorTypes.none, - }, - currentConfiguration: { - closureType: 'close-by-user', - connector: { - fields: null, - id: 'none', - name: 'none', - type: ConnectorTypes.none, - }, - }, - firstLoad: false, - loading: true, - mappings: [], - persistLoading: false, - version: '', -}; - -export const useCaseConfigure = (): ReturnUseCaseConfigure => { - const [state, dispatch] = useReducer(configureCasesReducer, initialState); - - const setCurrentConfiguration = useCallback((configuration: ConnectorConfiguration) => { - dispatch({ - currentConfiguration: configuration, - type: 'setCurrentConfiguration', - }); - }, []); - - const setConnector = useCallback((connector: CaseConnector) => { - dispatch({ - connector, - type: 'setConnector', - }); - }, []); - - const setClosureType = useCallback((closureType: ClosureType) => { - dispatch({ - closureType, - type: 'setClosureType', - }); - }, []); - - const setMappings = useCallback((mappings: CaseConnectorMapping[]) => { - dispatch({ - mappings, - type: 'setMappings', - }); - }, []); - - const setLoading = useCallback((isLoading: boolean) => { - dispatch({ - payload: isLoading, - type: 'setLoading', - }); - }, []); - - const setFirstLoad = useCallback((isFirstLoad: boolean) => { - dispatch({ - payload: isFirstLoad, - type: 'setFirstLoad', - }); - }, []); - - const setPersistLoading = useCallback((isPersistLoading: boolean) => { - dispatch({ - payload: isPersistLoading, - type: 'setPersistLoading', - }); - }, []); - - const setVersion = useCallback((version: string) => { - dispatch({ - payload: version, - type: 'setVersion', - }); - }, []); - - const [, dispatchToaster] = useStateToaster(); - const isCancelledRefetchRef = useRef(false); - const abortCtrlRefetchRef = useRef(new AbortController()); - - const isCancelledPersistRef = useRef(false); - const abortCtrlPersistRef = useRef(new AbortController()); - - const refetchCaseConfigure = useCallback(async () => { - try { - isCancelledRefetchRef.current = false; - abortCtrlRefetchRef.current.abort(); - abortCtrlRefetchRef.current = new AbortController(); - - setLoading(true); - const res = await getCaseConfigure({ signal: abortCtrlRefetchRef.current.signal }); - - if (!isCancelledRefetchRef.current) { - if (res != null) { - setConnector(res.connector); - if (setClosureType != null) { - setClosureType(res.closureType); - } - setVersion(res.version); - setMappings(res.mappings); - - if (!state.firstLoad) { - setFirstLoad(true); - if (setCurrentConfiguration != null) { - setCurrentConfiguration({ - closureType: res.closureType, - connector: { - ...res.connector, - }, - }); - } - } - if (res.error != null) { - errorToToaster({ - dispatchToaster, - error: new Error(res.error), - title: i18n.ERROR_TITLE, - }); - } - } - setLoading(false); - } - } catch (error) { - if (!isCancelledRefetchRef.current) { - if (error.name !== 'AbortError') { - errorToToaster({ - dispatchToaster, - error: error.body && error.body.message ? new Error(error.body.message) : error, - title: i18n.ERROR_TITLE, - }); - } - setLoading(false); - } - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [state.firstLoad]); - - const persistCaseConfigure = useCallback( - async ({ connector, closureType }: ConnectorConfiguration) => { - try { - isCancelledPersistRef.current = false; - abortCtrlPersistRef.current.abort(); - abortCtrlPersistRef.current = new AbortController(); - setPersistLoading(true); - - const connectorObj = { - connector, - closure_type: closureType, - }; - - const res = - state.version.length === 0 - ? await postCaseConfigure(connectorObj, abortCtrlPersistRef.current.signal) - : await patchCaseConfigure( - { - ...connectorObj, - version: state.version, - }, - abortCtrlPersistRef.current.signal - ); - - if (!isCancelledPersistRef.current) { - setConnector(res.connector); - if (setClosureType) { - setClosureType(res.closureType); - } - setVersion(res.version); - setMappings(res.mappings); - if (setCurrentConfiguration != null) { - setCurrentConfiguration({ - closureType: res.closureType, - connector: { - ...res.connector, - }, - }); - } - if (res.error != null) { - errorToToaster({ - dispatchToaster, - error: new Error(res.error), - title: i18n.ERROR_TITLE, - }); - } - displaySuccessToast(i18n.SUCCESS_CONFIGURE, dispatchToaster); - setPersistLoading(false); - } - } catch (error) { - if (!isCancelledPersistRef.current) { - if (error.name !== 'AbortError') { - errorToToaster({ - title: i18n.ERROR_TITLE, - error: error.body && error.body.message ? new Error(error.body.message) : error, - dispatchToaster, - }); - } - setConnector(state.currentConfiguration.connector); - setPersistLoading(false); - } - } - }, - [ - dispatchToaster, - setClosureType, - setConnector, - setCurrentConfiguration, - setMappings, - setPersistLoading, - setVersion, - state, - ] - ); - - useEffect(() => { - refetchCaseConfigure(); - return () => { - isCancelledRefetchRef.current = true; - abortCtrlRefetchRef.current.abort(); - isCancelledPersistRef.current = true; - abortCtrlPersistRef.current.abort(); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return { - ...state, - refetchCaseConfigure, - persistCaseConfigure, - setCurrentConfiguration, - setConnector, - setClosureType, - setMappings, - }; -}; diff --git a/x-pack/plugins/cases/public/containers/configure/use_connectors.test.tsx b/x-pack/plugins/cases/public/containers/configure/use_connectors.test.tsx deleted file mode 100644 index ed1dfcbc40c87..0000000000000 --- a/x-pack/plugins/cases/public/containers/configure/use_connectors.test.tsx +++ /dev/null @@ -1,96 +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 { renderHook, act } from '@testing-library/react-hooks'; -import { useConnectors, UseConnectorsResponse } from './use_connectors'; -import { connectorsMock } from './mock'; -import * as api from './api'; - -jest.mock('./api'); - -describe('useConnectors', () => { - beforeEach(() => { - jest.clearAllMocks(); - jest.restoreAllMocks(); - }); - - test('init', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UseConnectorsResponse>(() => - useConnectors() - ); - await waitForNextUpdate(); - expect(result.current).toEqual({ - loading: true, - connectors: [], - refetchConnectors: result.current.refetchConnectors, - }); - }); - }); - - test('fetch connectors', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UseConnectorsResponse>(() => - useConnectors() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(result.current).toEqual({ - loading: false, - connectors: connectorsMock, - refetchConnectors: result.current.refetchConnectors, - }); - }); - }); - - test('refetch connectors', async () => { - const spyOnfetchConnectors = jest.spyOn(api, 'fetchConnectors'); - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UseConnectorsResponse>(() => - useConnectors() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - result.current.refetchConnectors(); - expect(spyOnfetchConnectors).toHaveBeenCalledTimes(2); - }); - }); - - test('set isLoading to true when refetching connectors', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UseConnectorsResponse>(() => - useConnectors() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - result.current.refetchConnectors(); - - expect(result.current.loading).toBe(true); - }); - }); - - test('unhappy path', async () => { - const spyOnfetchConnectors = jest.spyOn(api, 'fetchConnectors'); - spyOnfetchConnectors.mockImplementation(() => { - throw new Error('Something went wrong'); - }); - - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UseConnectorsResponse>(() => - useConnectors() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - - expect(result.current).toEqual({ - loading: false, - connectors: [], - refetchConnectors: result.current.refetchConnectors, - }); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/containers/configure/use_connectors.tsx b/x-pack/plugins/cases/public/containers/configure/use_connectors.tsx deleted file mode 100644 index b385a2676e044..0000000000000 --- a/x-pack/plugins/cases/public/containers/configure/use_connectors.tsx +++ /dev/null @@ -1,72 +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 { useState, useEffect, useCallback, useRef } from 'react'; - -import { useStateToaster, errorToToaster } from '../../components/toasters'; -import * as i18n from '../translations'; -import { fetchConnectors } from './api'; -import { ActionConnector } from './types'; - -export interface UseConnectorsResponse { - loading: boolean; - connectors: ActionConnector[]; - refetchConnectors: () => void; -} - -export const useConnectors = (): UseConnectorsResponse => { - const [, dispatchToaster] = useStateToaster(); - const [loading, setLoading] = useState(true); - const [connectors, setConnectors] = useState<ActionConnector[]>([]); - const isCancelledRef = useRef(false); - const abortCtrlRef = useRef(new AbortController()); - - const refetchConnectors = useCallback(async () => { - try { - isCancelledRef.current = false; - abortCtrlRef.current.abort(); - abortCtrlRef.current = new AbortController(); - - setLoading(true); - const res = await fetchConnectors({ signal: abortCtrlRef.current.signal }); - - if (!isCancelledRef.current) { - setLoading(false); - setConnectors(res); - } - } catch (error) { - if (!isCancelledRef.current) { - if (error.name !== 'AbortError') { - errorToToaster({ - title: i18n.ERROR_TITLE, - error: error.body && error.body.message ? new Error(error.body.message) : error, - dispatchToaster, - }); - } - - setLoading(false); - setConnectors([]); - } - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - useEffect(() => { - refetchConnectors(); - return () => { - isCancelledRef.current = true; - abortCtrlRef.current.abort(); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return { - loading, - connectors, - refetchConnectors, - }; -}; diff --git a/x-pack/plugins/cases/public/containers/constants.ts b/x-pack/plugins/cases/public/containers/constants.ts deleted file mode 100644 index be030f4d2f75b..0000000000000 --- a/x-pack/plugins/cases/public/containers/constants.ts +++ /dev/null @@ -1,9 +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. - */ - -export const DEFAULT_TABLE_ACTIVE_PAGE = 1; -export const DEFAULT_TABLE_LIMIT = 5; diff --git a/x-pack/plugins/cases/public/containers/mock.ts b/x-pack/plugins/cases/public/containers/mock.ts deleted file mode 100644 index 1e7cec29de56b..0000000000000 --- a/x-pack/plugins/cases/public/containers/mock.ts +++ /dev/null @@ -1,377 +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 { ActionLicense, AllCases, Case, CasesStatus, CaseUserActions, Comment } from './types'; - -import { - AssociationType, - CaseResponse, - CasesFindResponse, - CasesResponse, - CasesStatusResponse, - CaseStatuses, - CaseType, - CaseUserActionsResponse, - CommentResponse, - CommentType, - ConnectorTypes, - UserAction, - UserActionField, -} from '../../common'; -import { UseGetCasesState, DEFAULT_FILTER_OPTIONS, DEFAULT_QUERY_PARAMS } from './use_get_cases'; -export { connectorsMock } from './configure/mock'; - -export const basicCaseId = 'basic-case-id'; -export const basicSubCaseId = 'basic-sub-case-id'; -const basicCommentId = 'basic-comment-id'; -const basicCreatedAt = '2020-02-19T23:06:33.798Z'; -const basicUpdatedAt = '2020-02-20T15:02:57.995Z'; -const laterTime = '2020-02-28T15:02:57.995Z'; - -export const elasticUser = { - fullName: 'Leslie Knope', - username: 'lknope', - email: 'leslie.knope@elastic.co', -}; - -export const tags: string[] = ['coke', 'pepsi']; - -export const basicComment: Comment = { - associationType: AssociationType.case, - comment: 'Solve this fast!', - type: CommentType.user, - id: basicCommentId, - createdAt: basicCreatedAt, - createdBy: elasticUser, - pushedAt: null, - pushedBy: null, - updatedAt: null, - updatedBy: null, - version: 'WzQ3LDFc', -}; - -export const alertComment: Comment = { - alertId: 'alert-id-1', - associationType: AssociationType.case, - index: 'alert-index-1', - type: CommentType.alert, - id: 'alert-comment-id', - createdAt: basicCreatedAt, - createdBy: elasticUser, - pushedAt: null, - pushedBy: null, - rule: { - id: 'rule-id-1', - name: 'Awesome rule', - }, - updatedAt: null, - updatedBy: null, - version: 'WzQ3LDFc', -}; - -export const basicCase: Case = { - type: CaseType.individual, - closedAt: null, - closedBy: null, - id: basicCaseId, - comments: [basicComment], - createdAt: basicCreatedAt, - createdBy: elasticUser, - connector: { - id: '123', - name: 'My Connector', - type: ConnectorTypes.none, - fields: null, - }, - description: 'Security banana Issue', - externalService: null, - status: CaseStatuses.open, - tags, - title: 'Another horrible breach!!', - totalComment: 1, - totalAlerts: 0, - updatedAt: basicUpdatedAt, - updatedBy: elasticUser, - version: 'WzQ3LDFd', - settings: { - syncAlerts: true, - }, - subCaseIds: [], -}; - -export const collectionCase: Case = { - type: CaseType.collection, - closedAt: null, - closedBy: null, - id: 'collection-id', - comments: [basicComment], - createdAt: basicCreatedAt, - createdBy: elasticUser, - connector: { - id: '123', - name: 'My Connector', - type: ConnectorTypes.none, - fields: null, - }, - description: 'Security banana Issue', - externalService: null, - status: CaseStatuses.open, - tags, - title: 'Another horrible breach in a collection!!', - totalComment: 1, - totalAlerts: 0, - updatedAt: basicUpdatedAt, - updatedBy: elasticUser, - version: 'WzQ3LDFd', - settings: { - syncAlerts: true, - }, - subCases: [], - subCaseIds: [], -}; - -export const basicCasePost: Case = { - ...basicCase, - updatedAt: null, - updatedBy: null, -}; - -export const basicCommentPatch: Comment = { - ...basicComment, - updatedAt: basicUpdatedAt, - updatedBy: { - username: 'elastic', - }, -}; - -export const basicCaseCommentPatch = { - ...basicCase, - comments: [basicCommentPatch], -}; - -export const casesStatus: CasesStatus = { - countOpenCases: 20, - countInProgressCases: 40, - countClosedCases: 130, -}; - -export const basicPush = { - connectorId: '123', - connectorName: 'connector name', - externalId: 'external_id', - externalTitle: 'external title', - externalUrl: 'basicPush.com', - pushedAt: basicUpdatedAt, - pushedBy: elasticUser, -}; - -export const pushedCase: Case = { - ...basicCase, - externalService: basicPush, -}; - -const basicAction = { - actionAt: basicCreatedAt, - actionBy: elasticUser, - oldValue: null, - newValue: 'what a cool value', - caseId: basicCaseId, - commentId: null, -}; - -export const cases: Case[] = [ - basicCase, - { ...pushedCase, id: '1', totalComment: 0, comments: [] }, - { ...pushedCase, updatedAt: laterTime, id: '2', totalComment: 0, comments: [] }, - { ...basicCase, id: '3', totalComment: 0, comments: [] }, - { ...basicCase, id: '4', totalComment: 0, comments: [] }, -]; - -export const allCases: AllCases = { - cases, - page: 1, - perPage: 5, - total: 10, - ...casesStatus, -}; - -export const actionLicenses: ActionLicense[] = [ - { - id: '.servicenow', - name: 'ServiceNow', - enabled: true, - enabledInConfig: true, - enabledInLicense: true, - }, - { - id: '.jira', - name: 'Jira', - enabled: true, - enabledInConfig: true, - enabledInLicense: true, - }, -]; - -// Snake case for mock api responses -export const elasticUserSnake = { - full_name: 'Leslie Knope', - username: 'lknope', - email: 'leslie.knope@elastic.co', -}; - -export const basicCommentSnake: CommentResponse = { - associationType: AssociationType.case, - comment: 'Solve this fast!', - type: CommentType.user, - id: basicCommentId, - created_at: basicCreatedAt, - created_by: elasticUserSnake, - pushed_at: null, - pushed_by: null, - updated_at: null, - updated_by: null, - version: 'WzQ3LDFc', -}; - -export const basicCaseSnake: CaseResponse = { - ...basicCase, - status: CaseStatuses.open, - closed_at: null, - closed_by: null, - comments: [basicCommentSnake], - connector: { - id: '123', - name: 'My Connector', - type: ConnectorTypes.none, - fields: null, - }, - created_at: basicCreatedAt, - created_by: elasticUserSnake, - external_service: null, - updated_at: basicUpdatedAt, - updated_by: elasticUserSnake, -} as CaseResponse; - -export const casesStatusSnake: CasesStatusResponse = { - count_closed_cases: 130, - count_in_progress_cases: 40, - count_open_cases: 20, -}; - -export const pushSnake = { - connector_id: '123', - connector_name: 'connector name', - external_id: 'external_id', - external_title: 'external title', - external_url: 'basicPush.com', -}; - -export const basicPushSnake = { - ...pushSnake, - pushed_at: basicUpdatedAt, - pushed_by: elasticUserSnake, -}; - -export const pushedCaseSnake = { - ...basicCaseSnake, - external_service: basicPushSnake, -}; - -export const reporters: string[] = ['alexis', 'kim', 'maria', 'steph']; -export const respReporters = [ - { username: 'alexis', full_name: null, email: null }, - { username: 'kim', full_name: null, email: null }, - { username: 'maria', full_name: null, email: null }, - { username: 'steph', full_name: null, email: null }, -]; -export const casesSnake: CasesResponse = [ - basicCaseSnake, - { ...pushedCaseSnake, id: '1', totalComment: 0, comments: [] }, - { ...pushedCaseSnake, updated_at: laterTime, id: '2', totalComment: 0, comments: [] }, - { ...basicCaseSnake, id: '3', totalComment: 0, comments: [] }, - { ...basicCaseSnake, id: '4', totalComment: 0, comments: [] }, -]; - -export const allCasesSnake: CasesFindResponse = { - cases: casesSnake, - page: 1, - per_page: 5, - total: 10, - ...casesStatusSnake, -}; - -const basicActionSnake = { - action_at: basicCreatedAt, - action_by: elasticUserSnake, - old_value: null, - new_value: 'what a cool value', - case_id: basicCaseId, - comment_id: null, -}; -export const getUserActionSnake = (af: UserActionField, a: UserAction) => ({ - ...basicActionSnake, - action_id: `${af[0]}-${a}`, - action_field: af, - action: a, - comment_id: af[0] === 'comment' ? basicCommentId : null, - new_value: - a === 'push-to-service' && af[0] === 'pushed' - ? JSON.stringify(basicPushSnake) - : basicAction.newValue, -}); - -export const caseUserActionsSnake: CaseUserActionsResponse = [ - getUserActionSnake(['description'], 'create'), - getUserActionSnake(['comment'], 'create'), - getUserActionSnake(['description'], 'update'), -]; - -// user actions - -export const getUserAction = (af: UserActionField, a: UserAction) => ({ - ...basicAction, - actionId: `${af[0]}-${a}`, - actionField: af, - action: a, - commentId: af[0] === 'comment' ? basicCommentId : null, - newValue: - a === 'push-to-service' && af[0] === 'pushed' - ? JSON.stringify(basicPushSnake) - : basicAction.newValue, -}); - -export const getAlertUserAction = () => ({ - ...basicAction, - actionId: 'alert-action-id', - actionField: ['comment'], - action: 'create', - commentId: 'alert-comment-id', - newValue: '{"type":"alert","alertId":"alert-id-1","index":"index-id-1"}', -}); - -export const caseUserActions: CaseUserActions[] = [ - getUserAction(['description'], 'create'), - getUserAction(['comment'], 'create'), - getUserAction(['description'], 'update'), -]; - -// components tests -export const useGetCasesMockState: UseGetCasesState = { - data: allCases, - loading: [], - selectedCases: [], - isError: false, - queryParams: DEFAULT_QUERY_PARAMS, - filterOptions: DEFAULT_FILTER_OPTIONS, -}; - -export const basicCaseClosed: Case = { - ...basicCase, - closedAt: '2020-02-25T23:06:33.798Z', - closedBy: elasticUser, - status: CaseStatuses.closed, -}; diff --git a/x-pack/plugins/cases/public/containers/translations.ts b/x-pack/plugins/cases/public/containers/translations.ts deleted file mode 100644 index 966a5e158923f..0000000000000 --- a/x-pack/plugins/cases/public/containers/translations.ts +++ /dev/null @@ -1,90 +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 { i18n } from '@kbn/i18n'; - -export * from '../common/translations'; - -export const ERROR_TITLE = i18n.translate('xpack.cases.containers.errorTitle', { - defaultMessage: 'Error fetching data', -}); - -export const ERROR_DELETING = i18n.translate('xpack.cases.containers.errorDeletingTitle', { - defaultMessage: 'Error deleting data', -}); - -export const UPDATED_CASE = (caseTitle: string) => - i18n.translate('xpack.cases.containers.updatedCase', { - values: { caseTitle }, - defaultMessage: 'Updated "{caseTitle}"', - }); - -export const DELETED_CASES = (totalCases: number, caseTitle?: string) => - i18n.translate('xpack.cases.containers.deletedCases', { - values: { caseTitle, totalCases }, - defaultMessage: 'Deleted {totalCases, plural, =1 {"{caseTitle}"} other {{totalCases} cases}}', - }); - -export const CLOSED_CASES = ({ - totalCases, - caseTitle, -}: { - totalCases: number; - caseTitle?: string; -}) => - i18n.translate('xpack.cases.containers.closedCases', { - values: { caseTitle, totalCases }, - defaultMessage: 'Closed {totalCases, plural, =1 {"{caseTitle}"} other {{totalCases} cases}}', - }); - -export const REOPENED_CASES = ({ - totalCases, - caseTitle, -}: { - totalCases: number; - caseTitle?: string; -}) => - i18n.translate('xpack.cases.containers.reopenedCases', { - values: { caseTitle, totalCases }, - defaultMessage: 'Opened {totalCases, plural, =1 {"{caseTitle}"} other {{totalCases} cases}}', - }); - -export const MARK_IN_PROGRESS_CASES = ({ - totalCases, - caseTitle, -}: { - totalCases: number; - caseTitle?: string; -}) => - i18n.translate('xpack.cases.containers.markInProgressCases', { - values: { caseTitle, totalCases }, - defaultMessage: - 'Marked {totalCases, plural, =1 {"{caseTitle}"} other {{totalCases} cases}} as in progress', - }); - -export const SUCCESS_SEND_TO_EXTERNAL_SERVICE = (serviceName: string) => - i18n.translate('xpack.cases.containers.pushToExternalService', { - values: { serviceName }, - defaultMessage: 'Successfully sent to { serviceName }', - }); - -export const ERROR_GET_FIELDS = i18n.translate('xpack.cases.configure.errorGetFields', { - defaultMessage: 'Error getting fields from service', -}); - -export const SYNC_CASE = (caseTitle: string) => - i18n.translate('xpack.cases.containers.syncCase', { - values: { caseTitle }, - defaultMessage: 'Alerts in "{caseTitle}" have been synced', - }); - -export const STATUS_CHANGED_TOASTER_TEXT = i18n.translate( - 'xpack.cases.containers.statusChangeToasterText', - { - defaultMessage: 'Alerts in this case have been also had their status updated', - } -); diff --git a/x-pack/plugins/cases/public/containers/types.ts b/x-pack/plugins/cases/public/containers/types.ts deleted file mode 100644 index db6c6e678d188..0000000000000 --- a/x-pack/plugins/cases/public/containers/types.ts +++ /dev/null @@ -1,175 +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 { - User, - UserActionField, - UserAction, - CaseConnector, - CommentRequest, - CaseStatuses, - CaseAttributes, - CasePatchRequest, - CaseType, - AssociationType, -} from '../../common'; -import { CaseStatusWithAllStatus } from '../components/status'; - -export { CaseConnector, ActionConnector, CaseStatuses } from '../../common'; - -export type Comment = CommentRequest & { - associationType: AssociationType; - id: string; - createdAt: string; - createdBy: ElasticUser; - pushedAt: string | null; - pushedBy: string | null; - updatedAt: string | null; - updatedBy: ElasticUser | null; - version: string; -}; -export interface CaseUserActions { - actionId: string; - actionField: UserActionField; - action: UserAction; - actionAt: string; - actionBy: ElasticUser; - caseId: string; - commentId: string | null; - newValue: string | null; - oldValue: string | null; -} - -export interface CaseExternalService { - pushedAt: string; - pushedBy: ElasticUser; - connectorId: string; - connectorName: string; - externalId: string; - externalTitle: string; - externalUrl: string; -} - -interface BasicCase { - id: string; - closedAt: string | null; - closedBy: ElasticUser | null; - comments: Comment[]; - createdAt: string; - createdBy: ElasticUser; - status: CaseStatuses; - title: string; - totalAlerts: number; - totalComment: number; - updatedAt: string | null; - updatedBy: ElasticUser | null; - version: string; -} - -export interface SubCase extends BasicCase { - associationType: AssociationType; - caseParentId: string; -} - -export interface Case extends BasicCase { - connector: CaseConnector; - description: string; - externalService: CaseExternalService | null; - subCases?: SubCase[] | null; - subCaseIds: string[]; - settings: CaseAttributes['settings']; - tags: string[]; - type: CaseType; -} - -export interface QueryParams { - page: number; - perPage: number; - sortField: SortFieldCase; - sortOrder: 'asc' | 'desc'; -} - -export interface FilterOptions { - search: string; - status: CaseStatusWithAllStatus; - tags: string[]; - reporters: User[]; - onlyCollectionType?: boolean; -} - -export interface CasesStatus { - countClosedCases: number | null; - countOpenCases: number | null; - countInProgressCases: number | null; -} - -export interface AllCases extends CasesStatus { - cases: Case[]; - page: number; - perPage: number; - total: number; -} - -export enum SortFieldCase { - createdAt = 'createdAt', - closedAt = 'closedAt', - updatedAt = 'updatedAt', -} - -export interface ElasticUser { - readonly email?: string | null; - readonly fullName?: string | null; - readonly username?: string | null; -} - -export interface FetchCasesProps extends ApiProps { - queryParams?: QueryParams; - filterOptions?: FilterOptions; -} - -export interface ApiProps { - signal: AbortSignal; -} - -export interface BulkUpdateStatus { - status: string; - id: string; - version: string; -} -export interface ActionLicense { - id: string; - name: string; - enabled: boolean; - enabledInConfig: boolean; - enabledInLicense: boolean; -} - -export interface DeleteCase { - id: string; - type: CaseType | null; - title?: string; -} - -export interface FieldMappings { - id: string; - title?: string; -} - -export type UpdateKey = keyof Pick< - CasePatchRequest, - 'connector' | 'description' | 'status' | 'tags' | 'title' | 'settings' ->; - -export interface UpdateByKey { - updateKey: UpdateKey; - updateValue: CasePatchRequest[UpdateKey]; - fetchCaseUserActions?: (caseId: string, caseConnectorId: string, subCaseId?: string) => void; - updateCase?: (newCase: Case) => void; - caseData: Case; - onSuccess?: () => void; - onError?: () => void; -} diff --git a/x-pack/plugins/cases/public/containers/use_get_cases.test.tsx b/x-pack/plugins/cases/public/containers/use_get_cases.test.tsx deleted file mode 100644 index 8b5993255552a..0000000000000 --- a/x-pack/plugins/cases/public/containers/use_get_cases.test.tsx +++ /dev/null @@ -1,204 +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 { renderHook, act } from '@testing-library/react-hooks'; -import { CaseStatuses } from '../../common'; -import { - DEFAULT_FILTER_OPTIONS, - DEFAULT_QUERY_PARAMS, - initialData, - useGetCases, - UseGetCases, -} from './use_get_cases'; -import { UpdateKey } from './types'; -import { allCases, basicCase } from './mock'; -import * as api from './api'; - -jest.mock('./api'); - -describe('useGetCases', () => { - const abortCtrl = new AbortController(); - beforeEach(() => { - jest.clearAllMocks(); - jest.restoreAllMocks(); - }); - - it('init', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UseGetCases>(() => useGetCases()); - await waitForNextUpdate(); - expect(result.current).toEqual({ - data: initialData, - dispatchUpdateCaseProperty: result.current.dispatchUpdateCaseProperty, - filterOptions: DEFAULT_FILTER_OPTIONS, - isError: false, - loading: [], - queryParams: DEFAULT_QUERY_PARAMS, - refetchCases: result.current.refetchCases, - selectedCases: [], - setFilters: result.current.setFilters, - setQueryParams: result.current.setQueryParams, - setSelectedCases: result.current.setSelectedCases, - }); - }); - }); - - it('calls getCases with correct arguments', async () => { - const spyOnGetCases = jest.spyOn(api, 'getCases'); - await act(async () => { - const { waitForNextUpdate } = renderHook<string, UseGetCases>(() => useGetCases()); - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(spyOnGetCases).toBeCalledWith({ - filterOptions: DEFAULT_FILTER_OPTIONS, - queryParams: DEFAULT_QUERY_PARAMS, - signal: abortCtrl.signal, - }); - }); - }); - - it('fetch cases', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UseGetCases>(() => useGetCases()); - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(result.current).toEqual({ - data: allCases, - dispatchUpdateCaseProperty: result.current.dispatchUpdateCaseProperty, - filterOptions: DEFAULT_FILTER_OPTIONS, - isError: false, - loading: [], - queryParams: DEFAULT_QUERY_PARAMS, - refetchCases: result.current.refetchCases, - selectedCases: [], - setFilters: result.current.setFilters, - setQueryParams: result.current.setQueryParams, - setSelectedCases: result.current.setSelectedCases, - }); - }); - }); - it('dispatch update case property', async () => { - const spyOnPatchCase = jest.spyOn(api, 'patchCase'); - await act(async () => { - const updateCase = { - updateKey: 'description' as UpdateKey, - updateValue: 'description update', - caseId: basicCase.id, - refetchCasesStatus: jest.fn(), - version: '99999', - }; - const { result, waitForNextUpdate } = renderHook<string, UseGetCases>(() => useGetCases()); - await waitForNextUpdate(); - await waitForNextUpdate(); - result.current.dispatchUpdateCaseProperty(updateCase); - expect(result.current.loading).toEqual(['caseUpdate']); - expect(spyOnPatchCase).toBeCalledWith( - basicCase.id, - { [updateCase.updateKey]: updateCase.updateValue }, - updateCase.version, - abortCtrl.signal - ); - }); - }); - - it('refetch cases', async () => { - const spyOnGetCases = jest.spyOn(api, 'getCases'); - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UseGetCases>(() => useGetCases()); - await waitForNextUpdate(); - await waitForNextUpdate(); - result.current.refetchCases(); - expect(spyOnGetCases).toHaveBeenCalledTimes(2); - }); - }); - - it('set isLoading to true when refetching case', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UseGetCases>(() => useGetCases()); - await waitForNextUpdate(); - await waitForNextUpdate(); - result.current.refetchCases(); - - expect(result.current.loading).toEqual(['cases']); - }); - }); - - it('unhappy path', async () => { - const spyOnGetCases = jest.spyOn(api, 'getCases'); - spyOnGetCases.mockImplementation(() => { - throw new Error('Something went wrong'); - }); - - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UseGetCases>(() => useGetCases()); - await waitForNextUpdate(); - await waitForNextUpdate(); - - expect(result.current).toEqual({ - data: initialData, - dispatchUpdateCaseProperty: result.current.dispatchUpdateCaseProperty, - filterOptions: DEFAULT_FILTER_OPTIONS, - isError: true, - loading: [], - queryParams: DEFAULT_QUERY_PARAMS, - refetchCases: result.current.refetchCases, - selectedCases: [], - setFilters: result.current.setFilters, - setQueryParams: result.current.setQueryParams, - setSelectedCases: result.current.setSelectedCases, - }); - }); - }); - it('set filters', async () => { - await act(async () => { - const spyOnGetCases = jest.spyOn(api, 'getCases'); - const newFilters = { - search: 'new', - tags: ['new'], - status: CaseStatuses.closed, - }; - const { result, waitForNextUpdate } = renderHook<string, UseGetCases>(() => useGetCases()); - await waitForNextUpdate(); - await waitForNextUpdate(); - result.current.setFilters(newFilters); - await waitForNextUpdate(); - expect(spyOnGetCases.mock.calls[1][0]).toEqual({ - filterOptions: { ...DEFAULT_FILTER_OPTIONS, ...newFilters }, - queryParams: DEFAULT_QUERY_PARAMS, - signal: abortCtrl.signal, - }); - }); - }); - it('set query params', async () => { - await act(async () => { - const spyOnGetCases = jest.spyOn(api, 'getCases'); - const newQueryParams = { - page: 2, - }; - const { result, waitForNextUpdate } = renderHook<string, UseGetCases>(() => useGetCases()); - await waitForNextUpdate(); - await waitForNextUpdate(); - result.current.setQueryParams(newQueryParams); - await waitForNextUpdate(); - expect(spyOnGetCases.mock.calls[1][0]).toEqual({ - filterOptions: DEFAULT_FILTER_OPTIONS, - queryParams: { ...DEFAULT_QUERY_PARAMS, ...newQueryParams }, - signal: abortCtrl.signal, - }); - }); - }); - it('set selected cases', async () => { - await act(async () => { - const selectedCases = [basicCase]; - const { result, waitForNextUpdate } = renderHook<string, UseGetCases>(() => useGetCases()); - await waitForNextUpdate(); - await waitForNextUpdate(); - result.current.setSelectedCases(selectedCases); - expect(result.current.selectedCases).toEqual(selectedCases); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/containers/use_get_cases.tsx b/x-pack/plugins/cases/public/containers/use_get_cases.tsx deleted file mode 100644 index e06a47954cdd4..0000000000000 --- a/x-pack/plugins/cases/public/containers/use_get_cases.tsx +++ /dev/null @@ -1,255 +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 { useCallback, useEffect, useReducer, useRef } from 'react'; -import { DEFAULT_TABLE_ACTIVE_PAGE, DEFAULT_TABLE_LIMIT } from './constants'; -import { AllCases, SortFieldCase, FilterOptions, QueryParams, Case, UpdateByKey } from './types'; -import { errorToToaster, useStateToaster } from '../components/toasters'; -import * as i18n from './translations'; -import { getCases, patchCase } from './api'; -import { StatusAll } from '../components/status'; - -export interface UseGetCasesState { - data: AllCases; - filterOptions: FilterOptions; - isError: boolean; - loading: string[]; - queryParams: QueryParams; - selectedCases: Case[]; -} - -export interface UpdateCase extends Omit<UpdateByKey, 'caseData'> { - caseId: string; - version: string; - refetchCasesStatus: () => void; -} - -export type Action = - | { type: 'FETCH_INIT'; payload: string } - | { - type: 'FETCH_CASES_SUCCESS'; - payload: AllCases; - } - | { type: 'FETCH_FAILURE'; payload: string } - | { type: 'FETCH_UPDATE_CASE_SUCCESS' } - | { type: 'UPDATE_FILTER_OPTIONS'; payload: Partial<FilterOptions> } - | { type: 'UPDATE_QUERY_PARAMS'; payload: Partial<QueryParams> } - | { type: 'UPDATE_TABLE_SELECTIONS'; payload: Case[] }; - -const dataFetchReducer = (state: UseGetCasesState, action: Action): UseGetCasesState => { - switch (action.type) { - case 'FETCH_INIT': - return { - ...state, - isError: false, - loading: [...state.loading.filter((e) => e !== action.payload), action.payload], - }; - case 'FETCH_UPDATE_CASE_SUCCESS': - return { - ...state, - loading: state.loading.filter((e) => e !== 'caseUpdate'), - }; - case 'FETCH_CASES_SUCCESS': - return { - ...state, - data: action.payload, - isError: false, - loading: state.loading.filter((e) => e !== 'cases'), - }; - case 'FETCH_FAILURE': - return { - ...state, - isError: true, - loading: state.loading.filter((e) => e !== action.payload), - }; - case 'UPDATE_FILTER_OPTIONS': - return { - ...state, - filterOptions: { - ...state.filterOptions, - ...action.payload, - }, - }; - case 'UPDATE_QUERY_PARAMS': - return { - ...state, - queryParams: { - ...state.queryParams, - ...action.payload, - }, - }; - case 'UPDATE_TABLE_SELECTIONS': - return { - ...state, - selectedCases: action.payload, - }; - default: - return state; - } -}; - -export const DEFAULT_FILTER_OPTIONS: FilterOptions = { - search: '', - reporters: [], - status: StatusAll, - tags: [], - onlyCollectionType: false, -}; - -export const DEFAULT_QUERY_PARAMS: QueryParams = { - page: DEFAULT_TABLE_ACTIVE_PAGE, - perPage: DEFAULT_TABLE_LIMIT, - sortField: SortFieldCase.createdAt, - sortOrder: 'desc', -}; - -export const initialData: AllCases = { - cases: [], - countClosedCases: null, - countInProgressCases: null, - countOpenCases: null, - page: 0, - perPage: 0, - total: 0, -}; -export interface UseGetCases extends UseGetCasesState { - dispatchUpdateCaseProperty: ({ - updateKey, - updateValue, - caseId, - version, - refetchCasesStatus, - }: UpdateCase) => void; - refetchCases: () => void; - setFilters: (filters: Partial<FilterOptions>) => void; - setQueryParams: (queryParams: Partial<QueryParams>) => void; - setSelectedCases: (mySelectedCases: Case[]) => void; -} - -export const useGetCases = ( - initialQueryParams?: QueryParams, - initialFilterOptions?: FilterOptions -): UseGetCases => { - const [state, dispatch] = useReducer(dataFetchReducer, { - data: initialData, - filterOptions: initialFilterOptions ?? DEFAULT_FILTER_OPTIONS, - isError: false, - loading: [], - queryParams: initialQueryParams ?? DEFAULT_QUERY_PARAMS, - selectedCases: [], - }); - const [, dispatchToaster] = useStateToaster(); - const didCancelFetchCases = useRef(false); - const didCancelUpdateCases = useRef(false); - const abortCtrlFetchCases = useRef(new AbortController()); - const abortCtrlUpdateCases = useRef(new AbortController()); - - const setSelectedCases = useCallback((mySelectedCases: Case[]) => { - dispatch({ type: 'UPDATE_TABLE_SELECTIONS', payload: mySelectedCases }); - }, []); - - const setQueryParams = useCallback((newQueryParams: Partial<QueryParams>) => { - dispatch({ type: 'UPDATE_QUERY_PARAMS', payload: newQueryParams }); - }, []); - - const setFilters = useCallback((newFilters: Partial<FilterOptions>) => { - dispatch({ type: 'UPDATE_FILTER_OPTIONS', payload: newFilters }); - }, []); - - const fetchCases = useCallback(async (filterOptions: FilterOptions, queryParams: QueryParams) => { - try { - didCancelFetchCases.current = false; - abortCtrlFetchCases.current.abort(); - abortCtrlFetchCases.current = new AbortController(); - dispatch({ type: 'FETCH_INIT', payload: 'cases' }); - - const response = await getCases({ - filterOptions, - queryParams, - signal: abortCtrlFetchCases.current.signal, - }); - - if (!didCancelFetchCases.current) { - dispatch({ - type: 'FETCH_CASES_SUCCESS', - payload: response, - }); - } - } catch (error) { - if (!didCancelFetchCases.current) { - if (error.name !== 'AbortError') { - errorToToaster({ - title: i18n.ERROR_TITLE, - error: error.body && error.body.message ? new Error(error.body.message) : error, - dispatchToaster, - }); - } - dispatch({ type: 'FETCH_FAILURE', payload: 'cases' }); - } - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - const dispatchUpdateCaseProperty = useCallback( - async ({ updateKey, updateValue, caseId, refetchCasesStatus, version }: UpdateCase) => { - try { - didCancelUpdateCases.current = false; - abortCtrlUpdateCases.current.abort(); - abortCtrlUpdateCases.current = new AbortController(); - dispatch({ type: 'FETCH_INIT', payload: 'caseUpdate' }); - - await patchCase( - caseId, - { [updateKey]: updateValue }, - // saved object versions are typed as string | undefined, hope that's not true - version ?? '', - abortCtrlUpdateCases.current.signal - ); - - if (!didCancelUpdateCases.current) { - dispatch({ type: 'FETCH_UPDATE_CASE_SUCCESS' }); - fetchCases(state.filterOptions, state.queryParams); - refetchCasesStatus(); - } - } catch (error) { - if (!didCancelUpdateCases.current) { - if (error.name !== 'AbortError') { - errorToToaster({ title: i18n.ERROR_TITLE, error, dispatchToaster }); - } - dispatch({ type: 'FETCH_FAILURE', payload: 'caseUpdate' }); - } - } - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [state.filterOptions, state.queryParams] - ); - - const refetchCases = useCallback(() => { - fetchCases(state.filterOptions, state.queryParams); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [state.filterOptions, state.queryParams]); - - useEffect(() => { - fetchCases(state.filterOptions, state.queryParams); - return () => { - didCancelFetchCases.current = true; - didCancelUpdateCases.current = true; - abortCtrlFetchCases.current.abort(); - abortCtrlUpdateCases.current.abort(); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [state.queryParams, state.filterOptions]); - - return { - ...state, - dispatchUpdateCaseProperty, - refetchCases, - setFilters, - setQueryParams, - setSelectedCases, - }; -}; diff --git a/x-pack/plugins/cases/public/containers/use_get_tags.test.tsx b/x-pack/plugins/cases/public/containers/use_get_tags.test.tsx deleted file mode 100644 index 8042e560df350..0000000000000 --- a/x-pack/plugins/cases/public/containers/use_get_tags.test.tsx +++ /dev/null @@ -1,89 +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 { renderHook, act } from '@testing-library/react-hooks'; -import { useGetTags, UseGetTags } from './use_get_tags'; -import { tags } from './mock'; -import * as api from './api'; - -jest.mock('./api'); - -describe('useGetTags', () => { - const abortCtrl = new AbortController(); - beforeEach(() => { - jest.clearAllMocks(); - jest.restoreAllMocks(); - }); - - it('init', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UseGetTags>(() => useGetTags()); - await waitForNextUpdate(); - expect(result.current).toEqual({ - tags: [], - isLoading: true, - isError: false, - fetchTags: result.current.fetchTags, - }); - }); - }); - - it('calls getTags api', async () => { - const spyOnGetTags = jest.spyOn(api, 'getTags'); - await act(async () => { - const { waitForNextUpdate } = renderHook<string, UseGetTags>(() => useGetTags()); - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(spyOnGetTags).toBeCalledWith(abortCtrl.signal); - }); - }); - - it('fetch tags', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UseGetTags>(() => useGetTags()); - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(result.current).toEqual({ - tags, - isLoading: false, - isError: false, - fetchTags: result.current.fetchTags, - }); - }); - }); - - it('refetch tags', async () => { - const spyOnGetTags = jest.spyOn(api, 'getTags'); - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UseGetTags>(() => useGetTags()); - await waitForNextUpdate(); - await waitForNextUpdate(); - result.current.fetchTags(); - expect(spyOnGetTags).toHaveBeenCalledTimes(2); - }); - }); - - it('unhappy path', async () => { - const spyOnGetTags = jest.spyOn(api, 'getTags'); - spyOnGetTags.mockImplementation(() => { - throw new Error('Something went wrong'); - }); - - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UseGetTags>(() => useGetTags()); - await waitForNextUpdate(); - await waitForNextUpdate(); - - expect(result.current).toEqual({ - tags: [], - isLoading: false, - isError: true, - fetchTags: result.current.fetchTags, - }); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/containers/use_get_tags.tsx b/x-pack/plugins/cases/public/containers/use_get_tags.tsx deleted file mode 100644 index 33b863fba5da3..0000000000000 --- a/x-pack/plugins/cases/public/containers/use_get_tags.tsx +++ /dev/null @@ -1,100 +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 { useEffect, useReducer, useRef, useCallback } from 'react'; -import { errorToToaster, useStateToaster } from '../components/toasters'; -import { getTags } from './api'; -import * as i18n from './translations'; - -export interface TagsState { - tags: string[]; - isLoading: boolean; - isError: boolean; -} -type Action = - | { type: 'FETCH_INIT' } - | { type: 'FETCH_SUCCESS'; payload: string[] } - | { type: 'FETCH_FAILURE' }; - -export interface UseGetTags extends TagsState { - fetchTags: () => void; -} - -const dataFetchReducer = (state: TagsState, action: Action): TagsState => { - switch (action.type) { - case 'FETCH_INIT': - return { - ...state, - isLoading: true, - isError: false, - }; - case 'FETCH_SUCCESS': - return { - ...state, - isLoading: false, - isError: false, - tags: action.payload, - }; - case 'FETCH_FAILURE': - return { - ...state, - isLoading: false, - isError: true, - }; - default: - return state; - } -}; -const initialData: string[] = []; - -export const useGetTags = (): UseGetTags => { - const [state, dispatch] = useReducer(dataFetchReducer, { - isLoading: true, - isError: false, - tags: initialData, - }); - const [, dispatchToaster] = useStateToaster(); - const isCancelledRef = useRef(false); - const abortCtrlRef = useRef(new AbortController()); - - const callFetch = useCallback(async () => { - try { - isCancelledRef.current = false; - abortCtrlRef.current.abort(); - abortCtrlRef.current = new AbortController(); - dispatch({ type: 'FETCH_INIT' }); - - const response = await getTags(abortCtrlRef.current.signal); - - if (!isCancelledRef.current) { - dispatch({ type: 'FETCH_SUCCESS', payload: response }); - } - } catch (error) { - if (!isCancelledRef.current) { - if (error.name !== 'AbortError') { - errorToToaster({ - title: i18n.ERROR_TITLE, - error: error.body && error.body.message ? new Error(error.body.message) : error, - dispatchToaster, - }); - } - dispatch({ type: 'FETCH_FAILURE' }); - } - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - useEffect(() => { - callFetch(); - return () => { - isCancelledRef.current = true; - abortCtrlRef.current.abort(); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - return { ...state, fetchTags: callFetch }; -}; diff --git a/x-pack/plugins/cases/public/containers/use_post_case.test.tsx b/x-pack/plugins/cases/public/containers/use_post_case.test.tsx deleted file mode 100644 index 72ea368f10317..0000000000000 --- a/x-pack/plugins/cases/public/containers/use_post_case.test.tsx +++ /dev/null @@ -1,114 +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 { renderHook, act } from '@testing-library/react-hooks'; -import { usePostCase, UsePostCase } from './use_post_case'; -import * as api from './api'; -import { ConnectorTypes } from '../../common'; -import { basicCasePost } from './mock'; - -jest.mock('./api'); - -describe('usePostCase', () => { - const abortCtrl = new AbortController(); - const samplePost = { - description: 'description', - tags: ['tags'], - title: 'title', - connector: { - id: 'none', - name: 'none', - type: ConnectorTypes.none, - fields: null, - }, - settings: { - syncAlerts: true, - }, - }; - beforeEach(() => { - jest.clearAllMocks(); - jest.restoreAllMocks(); - }); - - it('init', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UsePostCase>(() => usePostCase()); - await waitForNextUpdate(); - expect(result.current).toEqual({ - isLoading: false, - isError: false, - postCase: result.current.postCase, - }); - }); - }); - - it('calls postCase with correct arguments', async () => { - const spyOnPostCase = jest.spyOn(api, 'postCase'); - - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UsePostCase>(() => usePostCase()); - await waitForNextUpdate(); - - result.current.postCase(samplePost); - await waitForNextUpdate(); - expect(spyOnPostCase).toBeCalledWith(samplePost, abortCtrl.signal); - }); - }); - - it('calls postCase with correct result', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UsePostCase>(() => usePostCase()); - await waitForNextUpdate(); - - const postData = await result.current.postCase(samplePost); - expect(postData).toEqual(basicCasePost); - }); - }); - - it('post case', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UsePostCase>(() => usePostCase()); - await waitForNextUpdate(); - result.current.postCase(samplePost); - await waitForNextUpdate(); - expect(result.current).toEqual({ - isLoading: false, - isError: false, - postCase: result.current.postCase, - }); - }); - }); - - it('set isLoading to true when posting case', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UsePostCase>(() => usePostCase()); - await waitForNextUpdate(); - result.current.postCase(samplePost); - - expect(result.current.isLoading).toBe(true); - }); - }); - - it('unhappy path', async () => { - const spyOnPostCase = jest.spyOn(api, 'postCase'); - spyOnPostCase.mockImplementation(() => { - throw new Error('Something went wrong'); - }); - - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UsePostCase>(() => usePostCase()); - await waitForNextUpdate(); - result.current.postCase(samplePost); - - expect(result.current).toEqual({ - isLoading: false, - isError: true, - postCase: result.current.postCase, - }); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/containers/use_post_case.tsx b/x-pack/plugins/cases/public/containers/use_post_case.tsx deleted file mode 100644 index 503ac8bf0209d..0000000000000 --- a/x-pack/plugins/cases/public/containers/use_post_case.tsx +++ /dev/null @@ -1,91 +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 { useReducer, useCallback, useRef, useEffect } from 'react'; -import { CasePostRequest } from '../../common'; -import { errorToToaster, useStateToaster } from '../components/toasters'; -import { postCase } from './api'; -import * as i18n from './translations'; -import { Case } from './types'; -interface NewCaseState { - isLoading: boolean; - isError: boolean; -} -type Action = { type: 'FETCH_INIT' } | { type: 'FETCH_SUCCESS' } | { type: 'FETCH_FAILURE' }; - -const dataFetchReducer = (state: NewCaseState, action: Action): NewCaseState => { - switch (action.type) { - case 'FETCH_INIT': - return { - ...state, - isLoading: true, - isError: false, - }; - case 'FETCH_SUCCESS': - return { - ...state, - isLoading: false, - isError: false, - }; - case 'FETCH_FAILURE': - return { - ...state, - isLoading: false, - isError: true, - }; - default: - return state; - } -}; -export interface UsePostCase extends NewCaseState { - postCase: (data: CasePostRequest) => Promise<Case | undefined>; -} -export const usePostCase = (): UsePostCase => { - const [state, dispatch] = useReducer(dataFetchReducer, { - isLoading: false, - isError: false, - }); - const [, dispatchToaster] = useStateToaster(); - const isCancelledRef = useRef(false); - const abortCtrlRef = useRef(new AbortController()); - - const postMyCase = useCallback(async (data: CasePostRequest) => { - try { - isCancelledRef.current = false; - abortCtrlRef.current.abort(); - abortCtrlRef.current = new AbortController(); - - dispatch({ type: 'FETCH_INIT' }); - const response = await postCase(data, abortCtrlRef.current.signal); - - if (!isCancelledRef.current) { - dispatch({ type: 'FETCH_SUCCESS' }); - } - return response; - } catch (error) { - if (!isCancelledRef.current) { - if (error.name !== 'AbortError') { - errorToToaster({ - title: i18n.ERROR_TITLE, - error: error.body && error.body.message ? new Error(error.body.message) : error, - dispatchToaster, - }); - } - dispatch({ type: 'FETCH_FAILURE' }); - } - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - useEffect(() => { - return () => { - isCancelledRef.current = true; - abortCtrlRef.current.abort(); - }; - }, []); - return { ...state, postCase: postMyCase }; -}; diff --git a/x-pack/plugins/cases/public/containers/use_post_push_to_service.test.tsx b/x-pack/plugins/cases/public/containers/use_post_push_to_service.test.tsx deleted file mode 100644 index 3d43180d60aff..0000000000000 --- a/x-pack/plugins/cases/public/containers/use_post_push_to_service.test.tsx +++ /dev/null @@ -1,101 +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 { renderHook, act } from '@testing-library/react-hooks'; -import { usePostPushToService, UsePostPushToService } from './use_post_push_to_service'; -import { pushedCase } from './mock'; -import * as api from './api'; -import { CaseConnector, ConnectorTypes } from '../../common'; - -jest.mock('./api'); - -describe('usePostPushToService', () => { - const abortCtrl = new AbortController(); - const connector = { - id: '123', - name: 'connector name', - type: ConnectorTypes.jira, - fields: { issueType: 'Task', priority: 'Low', parent: null }, - } as CaseConnector; - const caseId = pushedCase.id; - - it('init', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UsePostPushToService>(() => - usePostPushToService() - ); - await waitForNextUpdate(); - expect(result.current).toEqual({ - isLoading: false, - isError: false, - pushCaseToExternalService: result.current.pushCaseToExternalService, - }); - }); - }); - - it('calls pushCase with correct arguments', async () => { - const spyOnPushToService = jest.spyOn(api, 'pushCase'); - - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UsePostPushToService>(() => - usePostPushToService() - ); - await waitForNextUpdate(); - result.current.pushCaseToExternalService({ caseId, connector }); - await waitForNextUpdate(); - expect(spyOnPushToService).toBeCalledWith(caseId, connector.id, abortCtrl.signal); - }); - }); - - it('post push to service', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UsePostPushToService>(() => - usePostPushToService() - ); - await waitForNextUpdate(); - result.current.pushCaseToExternalService({ caseId, connector }); - await waitForNextUpdate(); - expect(result.current).toEqual({ - isLoading: false, - isError: false, - pushCaseToExternalService: result.current.pushCaseToExternalService, - }); - }); - }); - - it('set isLoading to true when pushing case', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UsePostPushToService>(() => - usePostPushToService() - ); - await waitForNextUpdate(); - result.current.pushCaseToExternalService({ caseId, connector }); - expect(result.current.isLoading).toBe(true); - }); - }); - - it('unhappy path', async () => { - const spyOnPushToService = jest.spyOn(api, 'pushCase'); - spyOnPushToService.mockImplementation(() => { - throw new Error('Something went wrong'); - }); - - await act(async () => { - const { result, waitForNextUpdate } = renderHook<string, UsePostPushToService>(() => - usePostPushToService() - ); - await waitForNextUpdate(); - result.current.pushCaseToExternalService({ caseId, connector }); - - expect(result.current).toEqual({ - isLoading: false, - isError: true, - pushCaseToExternalService: result.current.pushCaseToExternalService, - }); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/containers/use_post_push_to_service.tsx b/x-pack/plugins/cases/public/containers/use_post_push_to_service.tsx deleted file mode 100644 index 636edd33b5e92..0000000000000 --- a/x-pack/plugins/cases/public/containers/use_post_push_to_service.tsx +++ /dev/null @@ -1,112 +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 { useReducer, useCallback, useRef, useEffect } from 'react'; -import { CaseConnector } from '../../common'; -import { errorToToaster, useStateToaster, displaySuccessToast } from '../components/toasters'; - -import { pushCase } from './api'; -import * as i18n from './translations'; -import { Case } from './types'; - -interface PushToServiceState { - isLoading: boolean; - isError: boolean; -} -type Action = { type: 'FETCH_INIT' } | { type: 'FETCH_SUCCESS' } | { type: 'FETCH_FAILURE' }; - -const dataFetchReducer = (state: PushToServiceState, action: Action): PushToServiceState => { - switch (action.type) { - case 'FETCH_INIT': - return { - ...state, - isLoading: true, - isError: false, - }; - case 'FETCH_SUCCESS': - return { - ...state, - isLoading: false, - isError: false, - }; - case 'FETCH_FAILURE': - return { - ...state, - isLoading: false, - isError: true, - }; - default: - return state; - } -}; - -interface PushToServiceRequest { - caseId: string; - connector: CaseConnector; -} - -export interface UsePostPushToService extends PushToServiceState { - pushCaseToExternalService: ({ - caseId, - connector, - }: PushToServiceRequest) => Promise<Case | undefined>; -} - -export const usePostPushToService = (): UsePostPushToService => { - const [state, dispatch] = useReducer(dataFetchReducer, { - isLoading: false, - isError: false, - }); - const [, dispatchToaster] = useStateToaster(); - const cancel = useRef(false); - const abortCtrlRef = useRef(new AbortController()); - - const pushCaseToExternalService = useCallback( - async ({ caseId, connector }: PushToServiceRequest) => { - try { - abortCtrlRef.current.abort(); - cancel.current = false; - abortCtrlRef.current = new AbortController(); - dispatch({ type: 'FETCH_INIT' }); - - const response = await pushCase(caseId, connector.id, abortCtrlRef.current.signal); - - if (!cancel.current) { - dispatch({ type: 'FETCH_SUCCESS' }); - displaySuccessToast( - i18n.SUCCESS_SEND_TO_EXTERNAL_SERVICE(connector.name), - dispatchToaster - ); - } - - return response; - } catch (error) { - if (!cancel.current) { - if (error.name !== 'AbortError') { - errorToToaster({ - title: i18n.ERROR_TITLE, - error: error.body && error.body.message ? new Error(error.body.message) : error, - dispatchToaster, - }); - } - dispatch({ type: 'FETCH_FAILURE' }); - } - } - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [] - ); - - useEffect(() => { - return () => { - abortCtrlRef.current.abort(); - cancel.current = true; - }; - }, []); - - return { ...state, pushCaseToExternalService }; -}; diff --git a/x-pack/plugins/cases/public/containers/utils.test.ts b/x-pack/plugins/cases/public/containers/utils.test.ts deleted file mode 100644 index 6c1fb60298938..0000000000000 --- a/x-pack/plugins/cases/public/containers/utils.test.ts +++ /dev/null @@ -1,170 +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 { - valueToUpdateIsSettings, - valueToUpdateIsStatus, - createUpdateSuccessToaster, -} from './utils'; - -import { Case } from './types'; - -const caseBeforeUpdate = { - comments: [ - { - type: 'alert', - }, - ], - settings: { - syncAlerts: true, - }, -} as Case; - -const caseAfterUpdate = { title: 'My case' } as Case; - -describe('utils', () => { - describe('valueToUpdateIsSettings', () => { - it('returns true if key is settings', () => { - expect(valueToUpdateIsSettings('settings', 'value')).toBe(true); - }); - - it('returns false if key is NOT settings', () => { - expect(valueToUpdateIsSettings('tags', 'value')).toBe(false); - }); - }); - - describe('valueToUpdateIsStatus', () => { - it('returns true if key is status', () => { - expect(valueToUpdateIsStatus('status', 'value')).toBe(true); - }); - - it('returns false if key is NOT status', () => { - expect(valueToUpdateIsStatus('tags', 'value')).toBe(false); - }); - }); - - describe('createUpdateSuccessToaster', () => { - it('creates the correct toast when sync alerts is turned on and case has alerts', () => { - // We remove the id as is randomly generated - const { id, ...toast } = createUpdateSuccessToaster( - caseBeforeUpdate, - caseAfterUpdate, - 'settings', - { - syncAlerts: true, - } - ); - - expect(toast).toEqual({ - color: 'success', - iconType: 'check', - title: 'Alerts in "My case" have been synced', - }); - }); - - it('creates the correct toast when sync alerts is turned on and case does NOT have alerts', () => { - // We remove the id as is randomly generated - const { id, ...toast } = createUpdateSuccessToaster( - { ...caseBeforeUpdate, comments: [] }, - caseAfterUpdate, - 'settings', - { - syncAlerts: true, - } - ); - - expect(toast).toEqual({ - color: 'success', - iconType: 'check', - title: 'Updated "My case"', - }); - }); - - it('creates the correct toast when sync alerts is turned off and case has alerts', () => { - // We remove the id as is randomly generated - const { id, ...toast } = createUpdateSuccessToaster( - caseBeforeUpdate, - caseAfterUpdate, - 'settings', - { - syncAlerts: false, - } - ); - - expect(toast).toEqual({ - color: 'success', - iconType: 'check', - title: 'Updated "My case"', - }); - }); - - it('creates the correct toast when the status change, case has alerts, and sync alerts is on', () => { - // We remove the id as is randomly generated - const { id, ...toast } = createUpdateSuccessToaster( - caseBeforeUpdate, - caseAfterUpdate, - 'status', - 'closed' - ); - - expect(toast).toEqual({ - color: 'success', - iconType: 'check', - title: 'Updated "My case"', - text: 'Alerts in this case have been also had their status updated', - }); - }); - - it('creates the correct toast when the status change, case has alerts, and sync alerts is off', () => { - // We remove the id as is randomly generated - const { id, ...toast } = createUpdateSuccessToaster( - { ...caseBeforeUpdate, settings: { syncAlerts: false } }, - caseAfterUpdate, - 'status', - 'closed' - ); - - expect(toast).toEqual({ - color: 'success', - iconType: 'check', - title: 'Updated "My case"', - }); - }); - - it('creates the correct toast when the status change, case does NOT have alerts, and sync alerts is on', () => { - // We remove the id as is randomly generated - const { id, ...toast } = createUpdateSuccessToaster( - { ...caseBeforeUpdate, comments: [] }, - caseAfterUpdate, - 'status', - 'closed' - ); - - expect(toast).toEqual({ - color: 'success', - iconType: 'check', - title: 'Updated "My case"', - }); - }); - - it('creates the correct toast if not a status or a setting', () => { - // We remove the id as is randomly generated - const { id, ...toast } = createUpdateSuccessToaster( - caseBeforeUpdate, - caseAfterUpdate, - 'title', - 'My new title' - ); - - expect(toast).toEqual({ - color: 'success', - iconType: 'check', - title: 'Updated "My case"', - }); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/containers/utils.ts b/x-pack/plugins/cases/public/containers/utils.ts deleted file mode 100644 index a7eeaff1c2637..0000000000000 --- a/x-pack/plugins/cases/public/containers/utils.ts +++ /dev/null @@ -1,150 +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 uuid from 'uuid'; -import { set } from '@elastic/safer-lodash-set'; -import { camelCase, isArray, isObject } from 'lodash'; -import { fold } from 'fp-ts/lib/Either'; -import { identity } from 'fp-ts/lib/function'; -import { pipe } from 'fp-ts/lib/pipeable'; - -import { - CasesFindResponse, - CasesFindResponseRt, - CaseResponse, - CaseResponseRt, - CasesResponse, - CasesResponseRt, - CasesStatusResponseRt, - CasesStatusResponse, - throwErrors, - CasesConfigureResponse, - CaseConfigureResponseRt, - CaseUserActionsResponse, - CaseUserActionsResponseRt, - CommentType, - CasePatchRequest, -} from '../../common'; -import { AppToast, ToasterError } from '../components/toasters'; -import { AllCases, Case, UpdateByKey } from './types'; -import * as i18n from './translations'; - -export const getTypedPayload = <T>(a: unknown): T => a as T; - -export const parseString = (params: string) => { - try { - return JSON.parse(params); - } catch { - return null; - } -}; - -export const convertArrayToCamelCase = (arrayOfSnakes: unknown[]): unknown[] => - arrayOfSnakes.reduce((acc: unknown[], value) => { - if (isArray(value)) { - return [...acc, convertArrayToCamelCase(value)]; - } else if (isObject(value)) { - return [...acc, convertToCamelCase(value)]; - } else { - return [...acc, value]; - } - }, []); - -export const convertToCamelCase = <T, U extends {}>(snakeCase: T): U => - Object.entries(snakeCase).reduce((acc, [key, value]) => { - if (isArray(value)) { - set(acc, camelCase(key), convertArrayToCamelCase(value)); - } else if (isObject(value)) { - set(acc, camelCase(key), convertToCamelCase(value)); - } else { - set(acc, camelCase(key), value); - } - return acc; - }, {} as U); - -export const convertAllCasesToCamel = (snakeCases: CasesFindResponse): AllCases => ({ - cases: snakeCases.cases.map((snakeCase) => convertToCamelCase<CaseResponse, Case>(snakeCase)), - countOpenCases: snakeCases.count_open_cases, - countInProgressCases: snakeCases.count_in_progress_cases, - countClosedCases: snakeCases.count_closed_cases, - page: snakeCases.page, - perPage: snakeCases.per_page, - total: snakeCases.total, -}); - -export const decodeCasesStatusResponse = (respCase?: CasesStatusResponse) => - pipe( - CasesStatusResponseRt.decode(respCase), - fold(throwErrors(createToasterPlainError), identity) - ); - -export const createToasterPlainError = (message: string) => new ToasterError([message]); - -export const decodeCaseResponse = (respCase?: CaseResponse) => - pipe(CaseResponseRt.decode(respCase), fold(throwErrors(createToasterPlainError), identity)); - -export const decodeCasesResponse = (respCase?: CasesResponse) => - pipe(CasesResponseRt.decode(respCase), fold(throwErrors(createToasterPlainError), identity)); - -export const decodeCasesFindResponse = (respCases?: CasesFindResponse) => - pipe(CasesFindResponseRt.decode(respCases), fold(throwErrors(createToasterPlainError), identity)); - -export const decodeCaseConfigureResponse = (respCase?: CasesConfigureResponse) => - pipe( - CaseConfigureResponseRt.decode(respCase), - fold(throwErrors(createToasterPlainError), identity) - ); - -export const decodeCaseUserActionsResponse = (respUserActions?: CaseUserActionsResponse) => - pipe( - CaseUserActionsResponseRt.decode(respUserActions), - fold(throwErrors(createToasterPlainError), identity) - ); - -export const valueToUpdateIsSettings = ( - key: UpdateByKey['updateKey'], - value: UpdateByKey['updateValue'] -): value is CasePatchRequest['settings'] => key === 'settings'; - -export const valueToUpdateIsStatus = ( - key: UpdateByKey['updateKey'], - value: UpdateByKey['updateValue'] -): value is CasePatchRequest['status'] => key === 'status'; - -export const createUpdateSuccessToaster = ( - caseBeforeUpdate: Case, - caseAfterUpdate: Case, - key: UpdateByKey['updateKey'], - value: UpdateByKey['updateValue'] -): AppToast => { - const caseHasAlerts = caseBeforeUpdate.comments.some( - (comment) => comment.type === CommentType.alert - ); - - const toast: AppToast = { - id: uuid.v4(), - color: 'success', - iconType: 'check', - title: i18n.UPDATED_CASE(caseAfterUpdate.title), - }; - - if (valueToUpdateIsSettings(key, value) && value?.syncAlerts && caseHasAlerts) { - return { - ...toast, - title: i18n.SYNC_CASE(caseAfterUpdate.title), - }; - } - - if (valueToUpdateIsStatus(key, value) && caseHasAlerts && caseBeforeUpdate.settings.syncAlerts) { - return { - ...toast, - text: i18n.STATUS_CHANGED_TOASTER_TEXT, - }; - } - - return toast; -}; diff --git a/x-pack/plugins/cases/public/get_create_case.tsx b/x-pack/plugins/cases/public/get_create_case.tsx deleted file mode 100644 index ec13d9ae9e305..0000000000000 --- a/x-pack/plugins/cases/public/get_create_case.tsx +++ /dev/null @@ -1,19 +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, { lazy, Suspense } from 'react'; -import { EuiLoadingSpinner } from '@elastic/eui'; -import { CreateCaseProps } from './components/create'; - -export const getCreateCaseLazy = (props: CreateCaseProps) => { - const CreateCaseLazy = lazy(() => import('./components/create')); - return ( - <Suspense fallback={<EuiLoadingSpinner />}> - <CreateCaseLazy {...props} /> - </Suspense> - ); -}; diff --git a/x-pack/plugins/cases/public/index.tsx b/x-pack/plugins/cases/public/index.tsx deleted file mode 100644 index 1cf2d2e8d7067..0000000000000 --- a/x-pack/plugins/cases/public/index.tsx +++ /dev/null @@ -1,20 +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 { PluginInitializerContext } from 'kibana/public'; -import React from 'react'; -import { CasesUiPlugin } from './plugin'; - -export const TestComponent = () => <div>{'Hello from cases plugin!'}</div>; - -export function plugin(initializerContext: PluginInitializerContext) { - return new CasesUiPlugin(initializerContext); -} - -export { CasesUiPlugin }; -export * from './plugin'; -export * from './types'; diff --git a/x-pack/plugins/cases/public/plugin.ts b/x-pack/plugins/cases/public/plugin.ts deleted file mode 100644 index c594e8677a086..0000000000000 --- a/x-pack/plugins/cases/public/plugin.ts +++ /dev/null @@ -1,33 +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 { CoreStart, Plugin, PluginInitializerContext } from 'src/core/public'; -import { TestComponent } from '.'; -import { CasesUiStart, SetupPlugins, StartPlugins } from './types'; -import { getCreateCaseLazy } from './get_create_case'; -import { KibanaServices } from './common/lib/kibana'; - -export class CasesUiPlugin implements Plugin<void, CasesUiStart, SetupPlugins, StartPlugins> { - private kibanaVersion: string; - - constructor(initializerContext: PluginInitializerContext) { - this.kibanaVersion = initializerContext.env.packageInfo.version; - } - public setup() {} - - public start(core: CoreStart, plugins: StartPlugins): CasesUiStart { - KibanaServices.init({ ...core, ...plugins, kibanaVersion: this.kibanaVersion }); - return { - casesComponent: TestComponent, - getCreateCase: (props) => { - return getCreateCaseLazy(props); - }, - }; - } - - public stop() {} -} diff --git a/x-pack/plugins/cases/public/types.ts b/x-pack/plugins/cases/public/types.ts deleted file mode 100644 index 07a0b2c723914..0000000000000 --- a/x-pack/plugins/cases/public/types.ts +++ /dev/null @@ -1,29 +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 { CoreStart } from 'kibana/public'; -import { ReactElement } from 'react'; -import { - TriggersAndActionsUIPublicPluginSetup as TriggersActionsSetup, - TriggersAndActionsUIPublicPluginStart as TriggersActionsStart, -} from '../../triggers_actions_ui/public'; -import { CreateCaseProps } from './components/create'; - -export interface SetupPlugins { - triggersActionsUi: TriggersActionsSetup; -} - -export interface StartPlugins { - triggersActionsUi: TriggersActionsStart; -} - -export type StartServices = CoreStart & StartPlugins; - -export interface CasesUiStart { - casesComponent: () => JSX.Element; - getCreateCase: (props: CreateCaseProps) => ReactElement<CreateCaseProps>; -} diff --git a/x-pack/plugins/cases/server/client/alerts/update_status.test.ts b/x-pack/plugins/cases/server/client/alerts/update_status.test.ts index d6456cb3183ef..5dfe6060da1db 100644 --- a/x-pack/plugins/cases/server/client/alerts/update_status.test.ts +++ b/x-pack/plugins/cases/server/client/alerts/update_status.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { CaseStatuses } from '../../../common'; +import { CaseStatuses } from '../../../common/api'; import { createMockSavedObjectsRepository } from '../../routes/api/__fixtures__'; import { createCasesClientWithMockSavedObjectsClient } from '../mocks'; diff --git a/x-pack/plugins/cases/server/client/cases/create.test.ts b/x-pack/plugins/cases/server/client/cases/create.test.ts index 9cbe2a448d3b4..fe301dcca37ac 100644 --- a/x-pack/plugins/cases/server/client/cases/create.test.ts +++ b/x-pack/plugins/cases/server/client/cases/create.test.ts @@ -5,7 +5,12 @@ * 2.0. */ -import { ConnectorTypes, CaseStatuses, CaseType, CasesClientPostRequest } from '../../../common'; +import { + ConnectorTypes, + CaseStatuses, + CaseType, + CasesClientPostRequest, +} from '../../../common/api'; import { isCaseError } from '../../common/error'; import { diff --git a/x-pack/plugins/cases/server/client/cases/create.ts b/x-pack/plugins/cases/server/client/cases/create.ts index 1dbb2dc496a99..59f9688836341 100644 --- a/x-pack/plugins/cases/server/client/cases/create.ts +++ b/x-pack/plugins/cases/server/client/cases/create.ts @@ -22,7 +22,7 @@ import { CasePostRequest, CaseType, User, -} from '../../../common'; +} from '../../../common/api'; import { buildCaseUserActionItem } from '../../services/user_actions/helpers'; import { getConnectorFromConfiguration, diff --git a/x-pack/plugins/cases/server/client/cases/get.ts b/x-pack/plugins/cases/server/client/cases/get.ts index e230e665da865..fa556986ee8d3 100644 --- a/x-pack/plugins/cases/server/client/cases/get.ts +++ b/x-pack/plugins/cases/server/client/cases/get.ts @@ -7,7 +7,7 @@ import { SavedObjectsClientContract, Logger } from 'kibana/server'; import { flattenCaseSavedObject } from '../../routes/api/utils'; -import { CaseResponseRt, CaseResponse } from '../../../common'; +import { CaseResponseRt, CaseResponse } from '../../../common/api'; import { CaseServiceSetup } from '../../services'; import { countAlertsForID } from '../../common'; import { createCaseError } from '../../common/error'; diff --git a/x-pack/plugins/cases/server/client/cases/mock.ts b/x-pack/plugins/cases/server/client/cases/mock.ts index 0e589b901c8d1..490519187f49e 100644 --- a/x-pack/plugins/cases/server/client/cases/mock.ts +++ b/x-pack/plugins/cases/server/client/cases/mock.ts @@ -12,7 +12,7 @@ import { CaseUserActionsResponse, AssociationType, CommentResponseAlertsType, -} from '../../../common'; +} from '../../../common/api'; import { BasicParams } from './types'; diff --git a/x-pack/plugins/cases/server/client/cases/push.ts b/x-pack/plugins/cases/server/client/cases/push.ts index eeaf91b13fa89..3217178768f89 100644 --- a/x-pack/plugins/cases/server/client/cases/push.ts +++ b/x-pack/plugins/cases/server/client/cases/push.ts @@ -29,7 +29,7 @@ import { User, ESCasesConfigureAttributes, CaseType, -} from '../../../common'; +} from '../../../common/api'; import { buildCaseUserActionItem } from '../../services/user_actions/helpers'; import { createIncident, getCommentContextFromAttributes } from './utils'; diff --git a/x-pack/plugins/cases/server/client/cases/types.ts b/x-pack/plugins/cases/server/client/cases/types.ts index fb400675136ef..f1d56e7132bd1 100644 --- a/x-pack/plugins/cases/server/client/cases/types.ts +++ b/x-pack/plugins/cases/server/client/cases/types.ts @@ -19,7 +19,7 @@ import { PushToServiceApiParamsSIR as ServiceNowSIRPushToServiceApiParams, ServiceNowITSMIncident, } from '../../../../actions/server/builtin_action_types/servicenow/types'; -import { CaseResponse, ConnectorMappingsAttributes } from '../../../common'; +import { CaseResponse, ConnectorMappingsAttributes } from '../../../common/api'; export type Incident = JiraIncident | ResilientIncident | ServiceNowITSMIncident; export type PushToServiceApiParams = diff --git a/x-pack/plugins/cases/server/client/cases/update.test.ts b/x-pack/plugins/cases/server/client/cases/update.test.ts index 18b4e8d9d7b66..79c3b2838c3b2 100644 --- a/x-pack/plugins/cases/server/client/cases/update.test.ts +++ b/x-pack/plugins/cases/server/client/cases/update.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ConnectorTypes, CasesPatchRequest, CaseStatuses } from '../../../common'; +import { ConnectorTypes, CasesPatchRequest, CaseStatuses } from '../../../common/api'; import { isCaseError } from '../../common/error'; import { createMockSavedObjectsRepository, diff --git a/x-pack/plugins/cases/server/client/cases/update.ts b/x-pack/plugins/cases/server/client/cases/update.ts index 6a59bf60a4ece..ff3c0a62407a1 100644 --- a/x-pack/plugins/cases/server/client/cases/update.ts +++ b/x-pack/plugins/cases/server/client/cases/update.ts @@ -38,7 +38,7 @@ import { AssociationType, CommentAttributes, User, -} from '../../../common'; +} from '../../../common/api'; import { buildCaseUserActions } from '../../services/user_actions/helpers'; import { getCaseToUpdate, diff --git a/x-pack/plugins/cases/server/client/cases/utils.test.ts b/x-pack/plugins/cases/server/client/cases/utils.test.ts index c24812048376e..859114a5e8fb0 100644 --- a/x-pack/plugins/cases/server/client/cases/utils.test.ts +++ b/x-pack/plugins/cases/server/client/cases/utils.test.ts @@ -539,7 +539,7 @@ describe('utils', () => { commentId: 'comment-user-1', }, { - comment: 'Elastic Alerts attached to the case: 3', + comment: 'Elastic Security Alerts attached to the case: 3', commentId: 'mock-id-1-total-alerts', }, ]); @@ -569,7 +569,7 @@ describe('utils', () => { commentId: 'comment-user-1', }, { - comment: 'Elastic Alerts attached to the case: 4', + comment: 'Elastic Security Alerts attached to the case: 4', commentId: 'mock-id-1-total-alerts', }, ]); diff --git a/x-pack/plugins/cases/server/client/cases/utils.ts b/x-pack/plugins/cases/server/client/cases/utils.ts index 7749bce8042eb..7e77bf4ac84cc 100644 --- a/x-pack/plugins/cases/server/client/cases/utils.ts +++ b/x-pack/plugins/cases/server/client/cases/utils.ts @@ -20,7 +20,7 @@ import { CommentAttributes, CommentRequestUserType, CommentRequestAlertType, -} from '../../../common'; +} from '../../../common/api'; import { ActionsClient } from '../../../../actions/server'; import { externalServiceFormatters, FormatterConnectorTypes } from '../../connectors'; import { CasesClientGetAlertsResponse } from '../../client/alerts/types'; @@ -184,7 +184,7 @@ export const createIncident = async ({ if (totalAlerts > 0) { comments.push({ - comment: `Elastic Alerts attached to the case: ${totalAlerts}`, + comment: `Elastic Security Alerts attached to the case: ${totalAlerts}`, commentId: `${theCase.id}-total-alerts`, }); } diff --git a/x-pack/plugins/cases/server/client/client.ts b/x-pack/plugins/cases/server/client/client.ts index 3bd25b6b61bc5..8f9058654d6fd 100644 --- a/x-pack/plugins/cases/server/client/client.ts +++ b/x-pack/plugins/cases/server/client/client.ts @@ -31,7 +31,7 @@ import { CaseUserActionServiceSetup, AlertServiceContract, } from '../services'; -import { CasesPatchRequest, CasePostRequest, User } from '../../common'; +import { CasesPatchRequest, CasePostRequest, User } from '../../common/api'; import { get } from './cases/get'; import { get as getUserActions } from './user_actions/get'; import { get as getAlerts } from './alerts/get'; diff --git a/x-pack/plugins/cases/server/client/comments/add.test.ts b/x-pack/plugins/cases/server/client/comments/add.test.ts index bd04e0ea6ef14..23b7bc37dc814 100644 --- a/x-pack/plugins/cases/server/client/comments/add.test.ts +++ b/x-pack/plugins/cases/server/client/comments/add.test.ts @@ -6,7 +6,7 @@ */ import { omit } from 'lodash/fp'; -import { CommentType } from '../../../common'; +import { CommentType } from '../../../common/api'; import { isCaseError } from '../../common/error'; import { createMockSavedObjectsRepository, diff --git a/x-pack/plugins/cases/server/client/comments/add.ts b/x-pack/plugins/cases/server/client/comments/add.ts index 98b914fb7486b..45746613dc1d4 100644 --- a/x-pack/plugins/cases/server/client/comments/add.ts +++ b/x-pack/plugins/cases/server/client/comments/add.ts @@ -25,7 +25,7 @@ import { User, CommentRequestAlertType, AlertCommentRequestRt, -} from '../../../common'; +} from '../../../common/api'; import { buildCaseUserActionItem, buildCommentUserActionItem, @@ -36,7 +36,7 @@ import { CommentableCase, createAlertUpdateRequest } from '../../common'; import { CasesClientHandler } from '..'; import { createCaseError } from '../../common/error'; import { CASE_COMMENT_SAVED_OBJECT } from '../../saved_object_types'; -import { MAX_GENERATED_ALERTS_PER_SUB_CASE } from '../../../common'; +import { MAX_GENERATED_ALERTS_PER_SUB_CASE } from '../../../common/constants'; async function getSubCase({ caseService, diff --git a/x-pack/plugins/cases/server/client/configure/get_fields.test.ts b/x-pack/plugins/cases/server/client/configure/get_fields.test.ts index c474361293da4..2e2973516d0fd 100644 --- a/x-pack/plugins/cases/server/client/configure/get_fields.test.ts +++ b/x-pack/plugins/cases/server/client/configure/get_fields.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ConnectorTypes } from '../../../common'; +import { ConnectorTypes } from '../../../common/api'; import { createMockSavedObjectsRepository, mockCaseMappings } from '../../routes/api/__fixtures__'; import { createCasesClientWithMockSavedObjectsClient } from '../mocks'; diff --git a/x-pack/plugins/cases/server/client/configure/get_fields.ts b/x-pack/plugins/cases/server/client/configure/get_fields.ts index 8d899f0df1a76..deabae33810b2 100644 --- a/x-pack/plugins/cases/server/client/configure/get_fields.ts +++ b/x-pack/plugins/cases/server/client/configure/get_fields.ts @@ -7,7 +7,7 @@ import Boom from '@hapi/boom'; -import { GetFieldsResponse } from '../../../common'; +import { GetFieldsResponse } from '../../../common/api'; import { ConfigureFields } from '../types'; import { createDefaultMapping, formatFields } from './utils'; diff --git a/x-pack/plugins/cases/server/client/configure/get_mappings.test.ts b/x-pack/plugins/cases/server/client/configure/get_mappings.test.ts index 7d9593899bb2e..0ec2fc8b4621d 100644 --- a/x-pack/plugins/cases/server/client/configure/get_mappings.test.ts +++ b/x-pack/plugins/cases/server/client/configure/get_mappings.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ConnectorTypes } from '../../../common'; +import { ConnectorTypes } from '../../../common/api'; import { createMockSavedObjectsRepository, mockCaseMappings } from '../../routes/api/__fixtures__'; import { createCasesClientWithMockSavedObjectsClient } from '../mocks'; diff --git a/x-pack/plugins/cases/server/client/configure/get_mappings.ts b/x-pack/plugins/cases/server/client/configure/get_mappings.ts index 1f767ea682843..558c961f89e5b 100644 --- a/x-pack/plugins/cases/server/client/configure/get_mappings.ts +++ b/x-pack/plugins/cases/server/client/configure/get_mappings.ts @@ -7,7 +7,7 @@ import { SavedObjectsClientContract, Logger } from 'src/core/server'; import { ActionsClient } from '../../../../actions/server'; -import { ConnectorMappingsAttributes, ConnectorTypes } from '../../../common'; +import { ConnectorMappingsAttributes, ConnectorTypes } from '../../../common/api'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ACTION_SAVED_OBJECT_TYPE } from '../../../../actions/server/saved_objects'; import { ConnectorMappingsServiceSetup } from '../../services'; diff --git a/x-pack/plugins/cases/server/client/configure/mock.ts b/x-pack/plugins/cases/server/client/configure/mock.ts index ad982a5cc1243..ee214de9b51d4 100644 --- a/x-pack/plugins/cases/server/client/configure/mock.ts +++ b/x-pack/plugins/cases/server/client/configure/mock.ts @@ -5,7 +5,11 @@ * 2.0. */ -import { ConnectorField, ConnectorMappingsAttributes, ConnectorTypes } from '../../../common'; +import { + ConnectorField, + ConnectorMappingsAttributes, + ConnectorTypes, +} from '../../../common/api/connectors'; import { JiraGetFieldsResponse, ResilientGetFieldsResponse, diff --git a/x-pack/plugins/cases/server/client/configure/utils.test.ts b/x-pack/plugins/cases/server/client/configure/utils.test.ts index bf571388994c0..403854693e36c 100644 --- a/x-pack/plugins/cases/server/client/configure/utils.test.ts +++ b/x-pack/plugins/cases/server/client/configure/utils.test.ts @@ -11,7 +11,7 @@ export { ServiceNowGetFieldsResponse, } from '../../../../actions/server/types'; import { createDefaultMapping, formatFields } from './utils'; -import { ConnectorTypes } from '../../../common'; +import { ConnectorTypes } from '../../../common/api/connectors'; import { mappings, formatFieldsTestData } from './mock'; describe('client/configure/utils', () => { diff --git a/x-pack/plugins/cases/server/client/configure/utils.ts b/x-pack/plugins/cases/server/client/configure/utils.ts index b9ef813735e25..80e6c7a3b886c 100644 --- a/x-pack/plugins/cases/server/client/configure/utils.ts +++ b/x-pack/plugins/cases/server/client/configure/utils.ts @@ -5,7 +5,11 @@ * 2.0. */ -import { ConnectorField, ConnectorMappingsAttributes, ConnectorTypes } from '../../../common'; +import { + ConnectorField, + ConnectorMappingsAttributes, + ConnectorTypes, +} from '../../../common/api/connectors'; import { JiraGetFieldsResponse, ResilientGetFieldsResponse, diff --git a/x-pack/plugins/cases/server/client/types.ts b/x-pack/plugins/cases/server/client/types.ts index 3311b7ac6f921..c62b3913da763 100644 --- a/x-pack/plugins/cases/server/client/types.ts +++ b/x-pack/plugins/cases/server/client/types.ts @@ -18,7 +18,7 @@ import { GetFieldsResponse, CaseUserActionsResponse, User, -} from '../../common'; +} from '../../common/api'; import { AlertInfo } from '../common'; import { CaseConfigureServiceSetup, diff --git a/x-pack/plugins/cases/server/client/user_actions/get.ts b/x-pack/plugins/cases/server/client/user_actions/get.ts index 79b8ef25ab0f6..f6371b8e8b1e7 100644 --- a/x-pack/plugins/cases/server/client/user_actions/get.ts +++ b/x-pack/plugins/cases/server/client/user_actions/get.ts @@ -11,7 +11,7 @@ import { CASE_COMMENT_SAVED_OBJECT, SUB_CASE_SAVED_OBJECT, } from '../../saved_object_types'; -import { CaseUserActionsResponseRt, CaseUserActionsResponse } from '../../../common'; +import { CaseUserActionsResponseRt, CaseUserActionsResponse } from '../../../common/api'; import { CaseUserActionServiceSetup } from '../../services'; interface GetParams { diff --git a/x-pack/plugins/cases/server/common/models/commentable_case.ts b/x-pack/plugins/cases/server/common/models/commentable_case.ts index 3daccf87bdc19..1ff5b7beadcaf 100644 --- a/x-pack/plugins/cases/server/common/models/commentable_case.ts +++ b/x-pack/plugins/cases/server/common/models/commentable_case.ts @@ -27,7 +27,7 @@ import { ESCaseAttributes, SubCaseAttributes, User, -} from '../../../common'; +} from '../../../common/api'; import { transformESConnectorToCaseConnector } from '../../routes/api/cases/helpers'; import { flattenCommentSavedObjects, diff --git a/x-pack/plugins/cases/server/common/utils.test.ts b/x-pack/plugins/cases/server/common/utils.test.ts index df16fe4f0a67d..5e6a86358de25 100644 --- a/x-pack/plugins/cases/server/common/utils.test.ts +++ b/x-pack/plugins/cases/server/common/utils.test.ts @@ -6,7 +6,7 @@ */ import { SavedObjectsFindResponse } from 'kibana/server'; -import { AssociationType, CommentAttributes, CommentRequest, CommentType } from '../../common'; +import { AssociationType, CommentAttributes, CommentRequest, CommentType } from '../../common/api'; import { transformNewComment } from '../routes/api/utils'; import { combineFilters, countAlerts, countAlertsForID, groupTotalAlertsByID } from './utils'; diff --git a/x-pack/plugins/cases/server/common/utils.ts b/x-pack/plugins/cases/server/common/utils.ts index d3bc3850e4210..dce26f3d5998a 100644 --- a/x-pack/plugins/cases/server/common/utils.ts +++ b/x-pack/plugins/cases/server/common/utils.ts @@ -6,7 +6,13 @@ */ import { SavedObjectsFindResult, SavedObjectsFindResponse } from 'kibana/server'; -import { CaseStatuses, CommentAttributes, CommentRequest, CommentType, User } from '../../common'; +import { + CaseStatuses, + CommentAttributes, + CommentRequest, + CommentType, + User, +} from '../../common/api'; import { UpdateAlertRequest } from '../client/types'; import { getAlertInfoFromComments } from '../routes/api/utils'; diff --git a/x-pack/plugins/cases/server/connectors/case/index.test.ts b/x-pack/plugins/cases/server/connectors/case/index.test.ts index b8c80a101f4c4..fa2b10a0ccbdb 100644 --- a/x-pack/plugins/cases/server/connectors/case/index.test.ts +++ b/x-pack/plugins/cases/server/connectors/case/index.test.ts @@ -18,7 +18,7 @@ import { AssociationType, CaseResponse, CasesResponse, -} from '../../../common'; +} from '../../../common/api'; import { connectorMappingsServiceMock, createCaseServiceMock, diff --git a/x-pack/plugins/cases/server/connectors/case/index.ts b/x-pack/plugins/cases/server/connectors/case/index.ts index d223c70221e37..da993faf0ef5c 100644 --- a/x-pack/plugins/cases/server/connectors/case/index.ts +++ b/x-pack/plugins/cases/server/connectors/case/index.ts @@ -8,7 +8,12 @@ import { curry } from 'lodash'; import { Logger } from 'src/core/server'; import { ActionTypeExecutorResult } from '../../../../actions/common'; -import { CasePatchRequest, CasePostRequest, CommentRequest, CommentType } from '../../../common'; +import { + CasePatchRequest, + CasePostRequest, + CommentRequest, + CommentType, +} from '../../../common/api'; import { createExternalCasesClient } from '../../client'; import { CaseExecutorParamsSchema, CaseConfigurationSchema, CommentSchemaType } from './schema'; import { diff --git a/x-pack/plugins/cases/server/connectors/case/schema.ts b/x-pack/plugins/cases/server/connectors/case/schema.ts index 803b01cbbdc57..1637cec7520be 100644 --- a/x-pack/plugins/cases/server/connectors/case/schema.ts +++ b/x-pack/plugins/cases/server/connectors/case/schema.ts @@ -6,7 +6,7 @@ */ import { schema } from '@kbn/config-schema'; -import { CommentType } from '../../../common'; +import { CommentType } from '../../../common/api'; import { validateConnector } from './validators'; // Reserved for future implementation diff --git a/x-pack/plugins/cases/server/connectors/case/types.ts b/x-pack/plugins/cases/server/connectors/case/types.ts index a71007f0b4946..6a7dfd9c2e687 100644 --- a/x-pack/plugins/cases/server/connectors/case/types.ts +++ b/x-pack/plugins/cases/server/connectors/case/types.ts @@ -16,7 +16,7 @@ import { ConnectorSchema, CommentSchema, } from './schema'; -import { CaseResponse, CasesResponse } from '../../../common'; +import { CaseResponse, CasesResponse } from '../../../common/api'; export type CaseConfiguration = TypeOf<typeof CaseConfigurationSchema>; export type Connector = TypeOf<typeof ConnectorSchema>; diff --git a/x-pack/plugins/cases/server/connectors/index.ts b/x-pack/plugins/cases/server/connectors/index.ts index ecf04e4f7b0f1..a6b6e193361be 100644 --- a/x-pack/plugins/cases/server/connectors/index.ts +++ b/x-pack/plugins/cases/server/connectors/index.ts @@ -17,7 +17,7 @@ import { serviceNowITSMExternalServiceFormatter } from './servicenow/itsm_format import { serviceNowSIRExternalServiceFormatter } from './servicenow/sir_formatter'; import { jiraExternalServiceFormatter } from './jira/external_service_formatter'; import { resilientExternalServiceFormatter } from './resilient/external_service_formatter'; -import { CommentRequest, CommentType } from '../../common'; +import { CommentRequest, CommentType } from '../../common/api'; export * from './types'; export { transformConnectorComment } from './case'; diff --git a/x-pack/plugins/cases/server/connectors/jira/external_service_formatter.test.ts b/x-pack/plugins/cases/server/connectors/jira/external_service_formatter.test.ts index f5d76aeddf313..0bfaf7cdbd9e3 100644 --- a/x-pack/plugins/cases/server/connectors/jira/external_service_formatter.test.ts +++ b/x-pack/plugins/cases/server/connectors/jira/external_service_formatter.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { CaseResponse } from '../../../common'; +import { CaseResponse } from '../../../common/api'; import { jiraExternalServiceFormatter } from './external_service_formatter'; describe('Jira formatter', () => { diff --git a/x-pack/plugins/cases/server/connectors/jira/external_service_formatter.ts b/x-pack/plugins/cases/server/connectors/jira/external_service_formatter.ts index 15ee2fd468dda..74376d295fea5 100644 --- a/x-pack/plugins/cases/server/connectors/jira/external_service_formatter.ts +++ b/x-pack/plugins/cases/server/connectors/jira/external_service_formatter.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { JiraFieldsType, ConnectorJiraTypeFields } from '../../../common'; +import { JiraFieldsType, ConnectorJiraTypeFields } from '../../../common/api'; import { ExternalServiceFormatter } from '../types'; interface ExternalServiceParams extends JiraFieldsType { diff --git a/x-pack/plugins/cases/server/connectors/resilient/external_service_formatter.test.ts b/x-pack/plugins/cases/server/connectors/resilient/external_service_formatter.test.ts index b7096179b0fab..01280e9692b5e 100644 --- a/x-pack/plugins/cases/server/connectors/resilient/external_service_formatter.test.ts +++ b/x-pack/plugins/cases/server/connectors/resilient/external_service_formatter.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { CaseResponse } from '../../../common'; +import { CaseResponse } from '../../../common/api'; import { resilientExternalServiceFormatter } from './external_service_formatter'; describe('IBM Resilient formatter', () => { diff --git a/x-pack/plugins/cases/server/connectors/resilient/external_service_formatter.ts b/x-pack/plugins/cases/server/connectors/resilient/external_service_formatter.ts index 6dea452565d7c..76554dce32797 100644 --- a/x-pack/plugins/cases/server/connectors/resilient/external_service_formatter.ts +++ b/x-pack/plugins/cases/server/connectors/resilient/external_service_formatter.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ResilientFieldsType, ConnectorResillientTypeFields } from '../../../common'; +import { ResilientFieldsType, ConnectorResillientTypeFields } from '../../../common/api'; import { ExternalServiceFormatter } from '../types'; const format: ExternalServiceFormatter<ResilientFieldsType>['format'] = (theCase) => { diff --git a/x-pack/plugins/cases/server/connectors/servicenow/itsm_formatter.ts b/x-pack/plugins/cases/server/connectors/servicenow/itsm_formatter.ts index a4fa8a198fea7..b49eed6a4ad26 100644 --- a/x-pack/plugins/cases/server/connectors/servicenow/itsm_formatter.ts +++ b/x-pack/plugins/cases/server/connectors/servicenow/itsm_formatter.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ServiceNowITSMFieldsType, ConnectorServiceNowITSMTypeFields } from '../../../common'; +import { ServiceNowITSMFieldsType, ConnectorServiceNowITSMTypeFields } from '../../../common/api'; import { ExternalServiceFormatter } from '../types'; const format: ExternalServiceFormatter<ServiceNowITSMFieldsType>['format'] = (theCase) => { diff --git a/x-pack/plugins/cases/server/connectors/servicenow/itsm_formmater.test.ts b/x-pack/plugins/cases/server/connectors/servicenow/itsm_formmater.test.ts index 78242e4c3848a..ea3a4e41e17b8 100644 --- a/x-pack/plugins/cases/server/connectors/servicenow/itsm_formmater.test.ts +++ b/x-pack/plugins/cases/server/connectors/servicenow/itsm_formmater.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { CaseResponse } from '../../../common'; +import { CaseResponse } from '../../../common/api'; import { serviceNowITSMExternalServiceFormatter } from './itsm_formatter'; describe('ITSM formatter', () => { diff --git a/x-pack/plugins/cases/server/connectors/servicenow/sir_formatter.test.ts b/x-pack/plugins/cases/server/connectors/servicenow/sir_formatter.test.ts index 1f7716424cfa9..4faca62c6e706 100644 --- a/x-pack/plugins/cases/server/connectors/servicenow/sir_formatter.test.ts +++ b/x-pack/plugins/cases/server/connectors/servicenow/sir_formatter.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { CaseResponse } from '../../../common'; +import { CaseResponse } from '../../../common/api'; import { serviceNowSIRExternalServiceFormatter } from './sir_formatter'; describe('ITSM formatter', () => { diff --git a/x-pack/plugins/cases/server/connectors/servicenow/sir_formatter.ts b/x-pack/plugins/cases/server/connectors/servicenow/sir_formatter.ts index 1c528cd2b47bf..d2458e6c7ae53 100644 --- a/x-pack/plugins/cases/server/connectors/servicenow/sir_formatter.ts +++ b/x-pack/plugins/cases/server/connectors/servicenow/sir_formatter.ts @@ -5,7 +5,7 @@ * 2.0. */ import { get } from 'lodash/fp'; -import { ConnectorServiceNowSIRTypeFields } from '../../../common'; +import { ConnectorServiceNowSIRTypeFields } from '../../../common/api'; import { ExternalServiceFormatter } from '../types'; interface ExternalServiceParams { dest_ip: string | null; diff --git a/x-pack/plugins/cases/server/connectors/types.ts b/x-pack/plugins/cases/server/connectors/types.ts index fae1ec2976bc0..f6c284b74667b 100644 --- a/x-pack/plugins/cases/server/connectors/types.ts +++ b/x-pack/plugins/cases/server/connectors/types.ts @@ -13,7 +13,7 @@ import { ActionType, // eslint-disable-next-line @kbn/eslint/no-restricted-paths } from '../../../actions/server/types'; -import { CaseResponse, ConnectorTypes } from '../../common'; +import { CaseResponse, ConnectorTypes } from '../../common/api'; import { CasesClientGetAlertsResponse } from '../client/alerts/types'; import { CaseServiceSetup, diff --git a/x-pack/plugins/cases/server/plugin.ts b/x-pack/plugins/cases/server/plugin.ts index 82e2e0b10e771..0c661cc18c21b 100644 --- a/x-pack/plugins/cases/server/plugin.ts +++ b/x-pack/plugins/cases/server/plugin.ts @@ -10,7 +10,7 @@ import { CoreSetup, CoreStart } from 'src/core/server'; import { SecurityPluginSetup } from '../../security/server'; import { PluginSetupContract as ActionsPluginSetup } from '../../actions/server'; -import { APP_ID } from '../common'; +import { APP_ID } from '../common/constants'; import { ConfigType } from './config'; import { initCaseApi } from './routes/api'; diff --git a/x-pack/plugins/cases/server/routes/api/__fixtures__/mock_saved_objects.ts b/x-pack/plugins/cases/server/routes/api/__fixtures__/mock_saved_objects.ts index c9d7ac4125141..f2318c45e6ed3 100644 --- a/x-pack/plugins/cases/server/routes/api/__fixtures__/mock_saved_objects.ts +++ b/x-pack/plugins/cases/server/routes/api/__fixtures__/mock_saved_objects.ts @@ -17,7 +17,7 @@ import { ConnectorTypes, ESCaseAttributes, ESCasesConfigureAttributes, -} from '../../../../common'; +} from '../../../../common/api'; import { CASE_CONNECTOR_MAPPINGS_SAVED_OBJECT, CASE_USER_ACTION_SAVED_OBJECT, diff --git a/x-pack/plugins/cases/server/routes/api/__mocks__/request_responses.ts b/x-pack/plugins/cases/server/routes/api/__mocks__/request_responses.ts index 9df94cd0923c9..ae14b44e7dffe 100644 --- a/x-pack/plugins/cases/server/routes/api/__mocks__/request_responses.ts +++ b/x-pack/plugins/cases/server/routes/api/__mocks__/request_responses.ts @@ -10,7 +10,7 @@ import { CasePostRequest, CasesConfigureRequest, ConnectorTypes, -} from '../../../../common'; +} from '../../../../common/api'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { FindActionResult } from '../../../../../actions/server/types'; diff --git a/x-pack/plugins/cases/server/routes/api/cases/comments/delete_all_comments.ts b/x-pack/plugins/cases/server/routes/api/cases/comments/delete_all_comments.ts index 77db06680fd59..fd250b74fff1e 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/comments/delete_all_comments.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/comments/delete_all_comments.ts @@ -10,7 +10,8 @@ import { schema } from '@kbn/config-schema'; import { buildCommentUserActionItem } from '../../../../services/user_actions/helpers'; import { RouteDeps } from '../../types'; import { wrapError } from '../../utils'; -import { AssociationType, CASE_COMMENTS_URL } from '../../../../../common'; +import { CASE_COMMENTS_URL } from '../../../../../common/constants'; +import { AssociationType } from '../../../../../common/api'; export function initDeleteAllCommentsApi({ caseService, diff --git a/x-pack/plugins/cases/server/routes/api/cases/comments/delete_comment.test.ts b/x-pack/plugins/cases/server/routes/api/cases/comments/delete_comment.test.ts index d0968c3232459..dcbcd7b9e246d 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/comments/delete_comment.test.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/comments/delete_comment.test.ts @@ -16,7 +16,7 @@ import { mockCaseComments, } from '../../__fixtures__'; import { initDeleteCommentApi } from './delete_comment'; -import { CASE_COMMENT_DETAILS_URL } from '../../../../../common'; +import { CASE_COMMENT_DETAILS_URL } from '../../../../../common/constants'; describe('DELETE comment', () => { let routeHandler: RequestHandler<any, any, any>; diff --git a/x-pack/plugins/cases/server/routes/api/cases/comments/delete_comment.ts b/x-pack/plugins/cases/server/routes/api/cases/comments/delete_comment.ts index 3ba93142bdcce..f1c5fdc2b7cc8 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/comments/delete_comment.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/comments/delete_comment.ts @@ -12,7 +12,7 @@ import { CASE_SAVED_OBJECT, SUB_CASE_SAVED_OBJECT } from '../../../../saved_obje import { buildCommentUserActionItem } from '../../../../services/user_actions/helpers'; import { RouteDeps } from '../../types'; import { wrapError } from '../../utils'; -import { CASE_COMMENT_DETAILS_URL } from '../../../../../common'; +import { CASE_COMMENT_DETAILS_URL } from '../../../../../common/constants'; export function initDeleteCommentApi({ caseService, diff --git a/x-pack/plugins/cases/server/routes/api/cases/comments/find_comments.ts b/x-pack/plugins/cases/server/routes/api/cases/comments/find_comments.ts index 75d0f9f59657a..57ddd84e8742c 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/comments/find_comments.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/comments/find_comments.ts @@ -19,10 +19,10 @@ import { CommentsResponseRt, SavedObjectFindOptionsRt, throwErrors, -} from '../../../../../common'; +} from '../../../../../common/api'; import { RouteDeps } from '../../types'; import { escapeHatch, transformComments, wrapError } from '../../utils'; -import { CASE_COMMENTS_URL } from '../../../../../common'; +import { CASE_COMMENTS_URL } from '../../../../../common/constants'; import { defaultPage, defaultPerPage } from '../..'; const FindQueryParamsRt = rt.partial({ diff --git a/x-pack/plugins/cases/server/routes/api/cases/comments/get_all_comment.ts b/x-pack/plugins/cases/server/routes/api/cases/comments/get_all_comment.ts index a400f944dddfa..770efe0109744 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/comments/get_all_comment.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/comments/get_all_comment.ts @@ -8,10 +8,10 @@ import { schema } from '@kbn/config-schema'; import { SavedObjectsFindResponse } from 'kibana/server'; -import { AllCommentsResponseRt, CommentAttributes } from '../../../../../common'; +import { AllCommentsResponseRt, CommentAttributes } from '../../../../../common/api'; import { RouteDeps } from '../../types'; import { flattenCommentSavedObjects, wrapError } from '../../utils'; -import { CASE_COMMENTS_URL } from '../../../../../common'; +import { CASE_COMMENTS_URL } from '../../../../../common/constants'; import { defaultSortField } from '../../../../common'; export function initGetAllCommentsApi({ caseService, router, logger }: RouteDeps) { diff --git a/x-pack/plugins/cases/server/routes/api/cases/comments/get_comment.test.ts b/x-pack/plugins/cases/server/routes/api/cases/comments/get_comment.test.ts index 46accdc58d460..8ee43eaba8a82 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/comments/get_comment.test.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/comments/get_comment.test.ts @@ -17,7 +17,7 @@ import { } from '../../__fixtures__'; import { flattenCommentSavedObject } from '../../utils'; import { initGetCommentApi } from './get_comment'; -import { CASE_COMMENT_DETAILS_URL } from '../../../../../common'; +import { CASE_COMMENT_DETAILS_URL } from '../../../../../common/constants'; describe('GET comment', () => { let routeHandler: RequestHandler<any, any, any>; diff --git a/x-pack/plugins/cases/server/routes/api/cases/comments/get_comment.ts b/x-pack/plugins/cases/server/routes/api/cases/comments/get_comment.ts index f86f733306043..9dedfccd3a250 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/comments/get_comment.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/comments/get_comment.ts @@ -7,10 +7,10 @@ import { schema } from '@kbn/config-schema'; -import { CommentResponseRt } from '../../../../../common'; +import { CommentResponseRt } from '../../../../../common/api'; import { RouteDeps } from '../../types'; import { flattenCommentSavedObject, wrapError } from '../../utils'; -import { CASE_COMMENT_DETAILS_URL } from '../../../../../common'; +import { CASE_COMMENT_DETAILS_URL } from '../../../../../common/constants'; export function initGetCommentApi({ caseService, router, logger }: RouteDeps) { router.get( diff --git a/x-pack/plugins/cases/server/routes/api/cases/comments/patch_comment.test.ts b/x-pack/plugins/cases/server/routes/api/cases/comments/patch_comment.test.ts index 32a0133d455c2..9cc0575f9bb94 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/comments/patch_comment.test.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/comments/patch_comment.test.ts @@ -17,8 +17,8 @@ import { mockCases, } from '../../__fixtures__'; import { initPatchCommentApi } from './patch_comment'; -import { CASE_COMMENTS_URL } from '../../../../../common'; -import { CommentType } from '../../../../../common'; +import { CASE_COMMENTS_URL } from '../../../../../common/constants'; +import { CommentType } from '../../../../../common/api'; describe('PATCH comment', () => { let routeHandler: RequestHandler<any, any, any>; diff --git a/x-pack/plugins/cases/server/routes/api/cases/comments/patch_comment.ts b/x-pack/plugins/cases/server/routes/api/cases/comments/patch_comment.ts index b47236f4693cf..f5db2dc004a1d 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/comments/patch_comment.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/comments/patch_comment.ts @@ -14,12 +14,12 @@ import Boom from '@hapi/boom'; import { SavedObjectsClientContract, Logger } from 'kibana/server'; import { CommentableCase } from '../../../../common'; -import { CommentPatchRequestRt, throwErrors, User } from '../../../../../common'; +import { CommentPatchRequestRt, throwErrors, User } from '../../../../../common/api'; import { CASE_SAVED_OBJECT, SUB_CASE_SAVED_OBJECT } from '../../../../saved_object_types'; import { buildCommentUserActionItem } from '../../../../services/user_actions/helpers'; import { RouteDeps } from '../../types'; import { escapeHatch, wrapError, decodeCommentRequest } from '../../utils'; -import { CASE_COMMENTS_URL } from '../../../../../common'; +import { CASE_COMMENTS_URL } from '../../../../../common/constants'; import { CaseServiceSetup } from '../../../../services'; interface CombinedCaseParams { diff --git a/x-pack/plugins/cases/server/routes/api/cases/comments/post_comment.test.ts b/x-pack/plugins/cases/server/routes/api/cases/comments/post_comment.test.ts index 27d5c47d47399..807ec0d089a52 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/comments/post_comment.test.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/comments/post_comment.test.ts @@ -17,8 +17,8 @@ import { mockCaseComments, } from '../../__fixtures__'; import { initPostCommentApi } from './post_comment'; -import { CASE_COMMENTS_URL } from '../../../../../common'; -import { CommentType } from '../../../../../common'; +import { CASE_COMMENTS_URL } from '../../../../../common/constants'; +import { CommentType } from '../../../../../common/api'; describe('POST comment', () => { let routeHandler: RequestHandler<any, any, any>; diff --git a/x-pack/plugins/cases/server/routes/api/cases/comments/post_comment.ts b/x-pack/plugins/cases/server/routes/api/cases/comments/post_comment.ts index 47d41b60165d7..110a16a610014 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/comments/post_comment.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/comments/post_comment.ts @@ -8,8 +8,8 @@ import { schema } from '@kbn/config-schema'; import { escapeHatch, wrapError } from '../../utils'; import { RouteDeps } from '../../types'; -import { CASE_COMMENTS_URL } from '../../../../../common'; -import { CommentRequest } from '../../../../../common'; +import { CASE_COMMENTS_URL } from '../../../../../common/constants'; +import { CommentRequest } from '../../../../../common/api'; export function initPostCommentApi({ router, logger }: RouteDeps) { router.post( diff --git a/x-pack/plugins/cases/server/routes/api/cases/configure/get_configure.test.ts b/x-pack/plugins/cases/server/routes/api/cases/configure/get_configure.test.ts index 626f53cdf4263..f328844acfd00 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/configure/get_configure.test.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/configure/get_configure.test.ts @@ -17,8 +17,9 @@ import { } from '../../__fixtures__'; import { initGetCaseConfigure } from './get_configure'; -import { CASE_CONFIGURE_URL, ConnectorTypes } from '../../../../../common'; +import { CASE_CONFIGURE_URL } from '../../../../../common/constants'; import { mappings } from '../../../../client/configure/mock'; +import { ConnectorTypes } from '../../../../../common/api/connectors'; import { CasesClient } from '../../../../client'; describe('GET configuration', () => { diff --git a/x-pack/plugins/cases/server/routes/api/cases/configure/get_configure.ts b/x-pack/plugins/cases/server/routes/api/cases/configure/get_configure.ts index 03ac3dd8b13b3..c916bd8f4140b 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/configure/get_configure.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/configure/get_configure.ts @@ -6,10 +6,10 @@ */ import Boom from '@hapi/boom'; -import { CaseConfigureResponseRt, ConnectorMappingsAttributes } from '../../../../../common'; +import { CaseConfigureResponseRt, ConnectorMappingsAttributes } from '../../../../../common/api'; import { RouteDeps } from '../../types'; import { wrapError } from '../../utils'; -import { CASE_CONFIGURE_URL } from '../../../../../common'; +import { CASE_CONFIGURE_URL } from '../../../../../common/constants'; import { transformESConnectorToCaseConnector } from '../helpers'; export function initGetCaseConfigure({ caseConfigureService, router, logger }: RouteDeps) { diff --git a/x-pack/plugins/cases/server/routes/api/cases/configure/get_connectors.test.ts b/x-pack/plugins/cases/server/routes/api/cases/configure/get_connectors.test.ts index 082adf7b4803f..3fa0fe2f83f79 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/configure/get_connectors.test.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/configure/get_connectors.test.ts @@ -17,7 +17,7 @@ import { } from '../../__fixtures__'; import { initCaseConfigureGetActionConnector } from './get_connectors'; -import { CASE_CONFIGURE_CONNECTORS_URL } from '../../../../../common'; +import { CASE_CONFIGURE_CONNECTORS_URL } from '../../../../../common/constants'; import { getActions } from '../../__mocks__/request_responses'; describe('GET connectors', () => { diff --git a/x-pack/plugins/cases/server/routes/api/cases/configure/get_connectors.ts b/x-pack/plugins/cases/server/routes/api/cases/configure/get_connectors.ts index 7aec7e4f086b4..81ffc06355ff5 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/configure/get_connectors.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/configure/get_connectors.ts @@ -12,7 +12,10 @@ import { ActionType } from '../../../../../../actions/common'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { FindActionResult } from '../../../../../../actions/server/types'; -import { CASE_CONFIGURE_CONNECTORS_URL, SUPPORTED_CONNECTORS } from '../../../../../common'; +import { + CASE_CONFIGURE_CONNECTORS_URL, + SUPPORTED_CONNECTORS, +} from '../../../../../common/constants'; const isConnectorSupported = ( action: FindActionResult, diff --git a/x-pack/plugins/cases/server/routes/api/cases/configure/patch_configure.test.ts b/x-pack/plugins/cases/server/routes/api/cases/configure/patch_configure.test.ts index c4e2b6af1cd6b..48d88e0f622f5 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/configure/patch_configure.test.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/configure/patch_configure.test.ts @@ -17,7 +17,8 @@ import { import { mockCaseConfigure } from '../../__fixtures__/mock_saved_objects'; import { initPatchCaseConfigure } from './patch_configure'; -import { CASE_CONFIGURE_URL, ConnectorTypes } from '../../../../../common'; +import { CASE_CONFIGURE_URL } from '../../../../../common/constants'; +import { ConnectorTypes } from '../../../../../common/api/connectors'; import { CasesClient } from '../../../../client'; describe('PATCH configuration', () => { diff --git a/x-pack/plugins/cases/server/routes/api/cases/configure/patch_configure.ts b/x-pack/plugins/cases/server/routes/api/cases/configure/patch_configure.ts index 5fe38cf0efe48..ba0ea6eb17936 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/configure/patch_configure.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/configure/patch_configure.ts @@ -15,10 +15,10 @@ import { CaseConfigureResponseRt, throwErrors, ConnectorMappingsAttributes, -} from '../../../../../common'; +} from '../../../../../common/api'; import { RouteDeps } from '../../types'; import { wrapError, escapeHatch } from '../../utils'; -import { CASE_CONFIGURE_URL } from '../../../../../common'; +import { CASE_CONFIGURE_URL } from '../../../../../common/constants'; import { transformCaseConnectorToEsConnector, transformESConnectorToCaseConnector, diff --git a/x-pack/plugins/cases/server/routes/api/cases/configure/post_configure.test.ts b/x-pack/plugins/cases/server/routes/api/cases/configure/post_configure.test.ts index 35b662078fe9c..882a10742d733 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/configure/post_configure.test.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/configure/post_configure.test.ts @@ -18,7 +18,8 @@ import { import { initPostCaseConfigure } from './post_configure'; import { newConfiguration } from '../../__mocks__/request_responses'; -import { CASE_CONFIGURE_URL, ConnectorTypes } from '../../../../../common'; +import { CASE_CONFIGURE_URL } from '../../../../../common/constants'; +import { ConnectorTypes } from '../../../../../common/api/connectors'; import { CasesClient } from '../../../../client'; describe('POST configuration', () => { diff --git a/x-pack/plugins/cases/server/routes/api/cases/configure/post_configure.ts b/x-pack/plugins/cases/server/routes/api/cases/configure/post_configure.ts index 74ad02f47e178..469151a126898 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/configure/post_configure.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/configure/post_configure.ts @@ -15,10 +15,10 @@ import { CaseConfigureResponseRt, throwErrors, ConnectorMappingsAttributes, -} from '../../../../../common'; +} from '../../../../../common/api'; import { RouteDeps } from '../../types'; import { wrapError, escapeHatch } from '../../utils'; -import { CASE_CONFIGURE_URL } from '../../../../../common'; +import { CASE_CONFIGURE_URL } from '../../../../../common/constants'; import { transformCaseConnectorToEsConnector, transformESConnectorToCaseConnector, diff --git a/x-pack/plugins/cases/server/routes/api/cases/delete_cases.test.ts b/x-pack/plugins/cases/server/routes/api/cases/delete_cases.test.ts index 7748a079ceb4d..a441a027769bf 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/delete_cases.test.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/delete_cases.test.ts @@ -17,7 +17,7 @@ import { mockCaseComments, } from '../__fixtures__'; import { initDeleteCasesApi } from './delete_cases'; -import { CASES_URL } from '../../../../common'; +import { CASES_URL } from '../../../../common/constants'; describe('DELETE case', () => { let routeHandler: RequestHandler<any, any, any>; diff --git a/x-pack/plugins/cases/server/routes/api/cases/delete_cases.ts b/x-pack/plugins/cases/server/routes/api/cases/delete_cases.ts index 43710dfab93eb..5f2a6c67220c3 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/delete_cases.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/delete_cases.ts @@ -11,7 +11,7 @@ import { SavedObjectsClientContract } from 'src/core/server'; import { buildCaseUserActionItem } from '../../../services/user_actions/helpers'; import { RouteDeps } from '../types'; import { wrapError } from '../utils'; -import { CASES_URL } from '../../../../common'; +import { CASES_URL } from '../../../../common/constants'; import { CaseServiceSetup } from '../../../services'; async function deleteSubCases({ diff --git a/x-pack/plugins/cases/server/routes/api/cases/find_cases.test.ts b/x-pack/plugins/cases/server/routes/api/cases/find_cases.test.ts index 75586896390fc..ca9f731ca5010 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/find_cases.test.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/find_cases.test.ts @@ -15,7 +15,7 @@ import { mockCases, } from '../__fixtures__'; import { initFindCasesApi } from './find_cases'; -import { CASES_URL } from '../../../../common'; +import { CASES_URL } from '../../../../common/constants'; import { mockCaseConfigure, mockCaseNoConnectorId } from '../__fixtures__/mock_saved_objects'; describe('FIND all cases', () => { diff --git a/x-pack/plugins/cases/server/routes/api/cases/find_cases.ts b/x-pack/plugins/cases/server/routes/api/cases/find_cases.ts index 97455e9e08f7b..bc6907f52b9eb 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/find_cases.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/find_cases.ts @@ -16,10 +16,10 @@ import { CasesFindRequestRt, throwErrors, caseStatuses, -} from '../../../../common'; +} from '../../../../common/api'; import { transformCases, wrapError, escapeHatch } from '../utils'; import { RouteDeps } from '../types'; -import { CASES_URL } from '../../../../common'; +import { CASES_URL } from '../../../../common/constants'; import { constructQueryOptions } from './helpers'; export function initFindCasesApi({ caseService, router, logger }: RouteDeps) { diff --git a/x-pack/plugins/cases/server/routes/api/cases/get_case.test.ts b/x-pack/plugins/cases/server/routes/api/cases/get_case.test.ts index 768bbca62f3fe..b9312331b4df2 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/get_case.test.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/get_case.test.ts @@ -8,7 +8,7 @@ import { kibanaResponseFactory, RequestHandler, SavedObject } from 'src/core/server'; import { httpServerMock } from 'src/core/server/mocks'; -import { ConnectorTypes, ESCaseAttributes } from '../../../../common'; +import { ConnectorTypes, ESCaseAttributes } from '../../../../common/api'; import { createMockSavedObjectsRepository, createRoute, @@ -21,7 +21,7 @@ import { } from '../__fixtures__'; import { flattenCaseSavedObject } from '../utils'; import { initGetCaseApi } from './get_case'; -import { CASE_DETAILS_URL } from '../../../../common'; +import { CASE_DETAILS_URL } from '../../../../common/constants'; describe('GET case', () => { let routeHandler: RequestHandler<any, any, any>; diff --git a/x-pack/plugins/cases/server/routes/api/cases/get_case.ts b/x-pack/plugins/cases/server/routes/api/cases/get_case.ts index e2d08dcd23f2e..f464f7e47fe7a 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/get_case.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/get_case.ts @@ -9,7 +9,7 @@ import { schema } from '@kbn/config-schema'; import { RouteDeps } from '../types'; import { wrapError } from '../utils'; -import { CASE_DETAILS_URL } from '../../../../common'; +import { CASE_DETAILS_URL } from '../../../../common/constants'; export function initGetCaseApi({ router, logger }: RouteDeps) { router.get( diff --git a/x-pack/plugins/cases/server/routes/api/cases/helpers.test.ts b/x-pack/plugins/cases/server/routes/api/cases/helpers.test.ts index a1d25aa295799..f7cfebeaea749 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/helpers.test.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/helpers.test.ts @@ -11,7 +11,7 @@ import { ConnectorTypes, ESCaseConnector, ESCasesConfigureAttributes, -} from '../../../../common'; +} from '../../../../common/api'; import { mockCaseConfigure } from '../__fixtures__'; import { transformCaseConnectorToEsConnector, diff --git a/x-pack/plugins/cases/server/routes/api/cases/helpers.ts b/x-pack/plugins/cases/server/routes/api/cases/helpers.ts index 5f51c9b1f8d8c..8659ab02d6d53 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/helpers.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/helpers.ts @@ -11,15 +11,14 @@ import deepEqual from 'fast-deep-equal'; import { SavedObjectsFindResponse } from 'kibana/server'; import { CaseConnector, - CaseStatuses, - CaseType, - ConnectorTypeFields, - ConnectorTypes, ESCaseConnector, ESCasesConfigureAttributes, - ESConnectorFields, + ConnectorTypes, + CaseStatuses, + CaseType, SavedObjectFindOptions, -} from '../../../../common'; +} from '../../../../common/api'; +import { ESConnectorFields, ConnectorTypeFields } from '../../../../common/api/connectors'; import { CASE_SAVED_OBJECT, SUB_CASE_SAVED_OBJECT } from '../../../saved_object_types'; import { sortToSnake } from '../utils'; import { combineFilters } from '../../../common'; diff --git a/x-pack/plugins/cases/server/routes/api/cases/patch_cases.test.ts b/x-pack/plugins/cases/server/routes/api/cases/patch_cases.test.ts index 96a891441ea5f..b3f87211c9547 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/patch_cases.test.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/patch_cases.test.ts @@ -17,7 +17,7 @@ import { } from '../__fixtures__'; import { initPatchCasesApi } from './patch_cases'; import { mockCaseConfigure, mockCaseNoConnectorId } from '../__fixtures__/mock_saved_objects'; -import { CaseStatuses } from '../../../../common'; +import { CaseStatuses } from '../../../../common/api'; describe('PATCH cases', () => { let routeHandler: RequestHandler<any, any, any>; diff --git a/x-pack/plugins/cases/server/routes/api/cases/patch_cases.ts b/x-pack/plugins/cases/server/routes/api/cases/patch_cases.ts index 092f88c1a8a20..8e779087bcafe 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/patch_cases.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/patch_cases.ts @@ -7,8 +7,8 @@ import { escapeHatch, wrapError } from '../utils'; import { RouteDeps } from '../types'; -import { CASES_URL } from '../../../../common'; -import { CasesPatchRequest } from '../../../../common'; +import { CASES_URL } from '../../../../common/constants'; +import { CasesPatchRequest } from '../../../../common/api'; export function initPatchCasesApi({ router, logger }: RouteDeps) { router.patch( diff --git a/x-pack/plugins/cases/server/routes/api/cases/post_case.test.ts b/x-pack/plugins/cases/server/routes/api/cases/post_case.test.ts index 669d3a5e58874..e1669203d3ded 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/post_case.test.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/post_case.test.ts @@ -15,9 +15,9 @@ import { mockCases, } from '../__fixtures__'; import { initPostCaseApi } from './post_case'; -import { CASES_URL } from '../../../../common'; +import { CASES_URL } from '../../../../common/constants'; import { mockCaseConfigure } from '../__fixtures__/mock_saved_objects'; -import { ConnectorTypes, CaseStatuses } from '../../../../common'; +import { ConnectorTypes, CaseStatuses } from '../../../../common/api'; describe('POST cases', () => { let routeHandler: RequestHandler<any, any, any>; diff --git a/x-pack/plugins/cases/server/routes/api/cases/post_case.ts b/x-pack/plugins/cases/server/routes/api/cases/post_case.ts index a7951a1a71344..e2d71c5837353 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/post_case.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/post_case.ts @@ -8,8 +8,8 @@ import { wrapError, escapeHatch } from '../utils'; import { RouteDeps } from '../types'; -import { CASES_URL } from '../../../../common'; -import { CasePostRequest } from '../../../../common'; +import { CASES_URL } from '../../../../common/constants'; +import { CasePostRequest } from '../../../../common/api'; export function initPostCaseApi({ router, logger }: RouteDeps) { router.post( diff --git a/x-pack/plugins/cases/server/routes/api/cases/push_case.test.ts b/x-pack/plugins/cases/server/routes/api/cases/push_case.test.ts index 378d092c8be0b..fb0ba5e3b5d9a 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/push_case.test.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/push_case.test.ts @@ -20,7 +20,7 @@ import { } from '../__fixtures__'; import { initPushCaseApi } from './push_case'; import { CasesRequestHandlerContext } from '../../../types'; -import { getCasePushUrl } from '../../../../common'; +import { getCasePushUrl } from '../../../../common/api/helpers'; describe('Push case', () => { let routeHandler: RequestHandler<any, any, any>; diff --git a/x-pack/plugins/cases/server/routes/api/cases/push_case.ts b/x-pack/plugins/cases/server/routes/api/cases/push_case.ts index 9bfb30e0d63ad..7395758210cf4 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/push_case.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/push_case.ts @@ -12,9 +12,9 @@ import { identity } from 'fp-ts/lib/function'; import { wrapError, escapeHatch } from '../utils'; -import { throwErrors, CasePushRequestParamsRt } from '../../../../common'; +import { throwErrors, CasePushRequestParamsRt } from '../../../../common/api'; import { RouteDeps } from '../types'; -import { CASE_PUSH_URL } from '../../../../common'; +import { CASE_PUSH_URL } from '../../../../common/constants'; export function initPushCaseApi({ router, logger }: RouteDeps) { router.post( diff --git a/x-pack/plugins/cases/server/routes/api/cases/reporters/get_reporters.ts b/x-pack/plugins/cases/server/routes/api/cases/reporters/get_reporters.ts index 53fdc298ef267..e5433f4972239 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/reporters/get_reporters.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/reporters/get_reporters.ts @@ -5,10 +5,10 @@ * 2.0. */ -import { UsersRt } from '../../../../../common'; +import { UsersRt } from '../../../../../common/api'; import { RouteDeps } from '../../types'; import { wrapError } from '../../utils'; -import { CASE_REPORTERS_URL } from '../../../../../common'; +import { CASE_REPORTERS_URL } from '../../../../../common/constants'; export function initGetReportersApi({ caseService, router, logger }: RouteDeps) { router.get( diff --git a/x-pack/plugins/cases/server/routes/api/cases/status/get_status.test.ts b/x-pack/plugins/cases/server/routes/api/cases/status/get_status.test.ts index 60ad0c60f944f..1c399a415e470 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/status/get_status.test.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/status/get_status.test.ts @@ -15,8 +15,8 @@ import { mockCases, } from '../../__fixtures__'; import { initGetCasesStatusApi } from './get_status'; -import { CASE_STATUS_URL } from '../../../../../common'; -import { CaseType } from '../../../../../common'; +import { CASE_STATUS_URL } from '../../../../../common/constants'; +import { CaseType } from '../../../../../common/api'; describe('GET status', () => { let routeHandler: RequestHandler<any, any, any>; diff --git a/x-pack/plugins/cases/server/routes/api/cases/status/get_status.ts b/x-pack/plugins/cases/server/routes/api/cases/status/get_status.ts index 73642fdee0eac..d0addfff09124 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/status/get_status.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/status/get_status.ts @@ -8,8 +8,8 @@ import { RouteDeps } from '../../types'; import { wrapError } from '../../utils'; -import { CasesStatusResponseRt, caseStatuses } from '../../../../../common'; -import { CASE_STATUS_URL } from '../../../../../common'; +import { CasesStatusResponseRt, caseStatuses } from '../../../../../common/api'; +import { CASE_STATUS_URL } from '../../../../../common/constants'; import { constructQueryOptions } from '../helpers'; export function initGetCasesStatusApi({ caseService, router, logger }: RouteDeps) { diff --git a/x-pack/plugins/cases/server/routes/api/cases/sub_case/delete_sub_cases.ts b/x-pack/plugins/cases/server/routes/api/cases/sub_case/delete_sub_cases.ts index ef60c743ec822..fd33afbd7df8e 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/sub_case/delete_sub_cases.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/sub_case/delete_sub_cases.ts @@ -10,7 +10,7 @@ import { schema } from '@kbn/config-schema'; import { buildCaseUserActionItem } from '../../../../services/user_actions/helpers'; import { RouteDeps } from '../../types'; import { wrapError } from '../../utils'; -import { SUB_CASES_PATCH_DEL_URL } from '../../../../../common'; +import { SUB_CASES_PATCH_DEL_URL } from '../../../../../common/constants'; import { CASE_SAVED_OBJECT } from '../../../../saved_object_types'; export function initDeleteSubCasesApi({ diff --git a/x-pack/plugins/cases/server/routes/api/cases/sub_case/find_sub_cases.ts b/x-pack/plugins/cases/server/routes/api/cases/sub_case/find_sub_cases.ts index 81d5517b8ce59..c24dde1944f83 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/sub_case/find_sub_cases.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/sub_case/find_sub_cases.ts @@ -17,10 +17,10 @@ import { SubCasesFindRequestRt, SubCasesFindResponseRt, throwErrors, -} from '../../../../../common'; +} from '../../../../../common/api'; import { RouteDeps } from '../../types'; import { escapeHatch, transformSubCases, wrapError } from '../../utils'; -import { SUB_CASES_URL } from '../../../../../common'; +import { SUB_CASES_URL } from '../../../../../common/constants'; import { constructQueryOptions } from '../helpers'; import { defaultPage, defaultPerPage } from '../..'; diff --git a/x-pack/plugins/cases/server/routes/api/cases/sub_case/get_sub_case.ts b/x-pack/plugins/cases/server/routes/api/cases/sub_case/get_sub_case.ts index b5ebfb4de348b..32dcc924e1a08 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/sub_case/get_sub_case.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/sub_case/get_sub_case.ts @@ -7,10 +7,10 @@ import { schema } from '@kbn/config-schema'; -import { SubCaseResponseRt } from '../../../../../common'; +import { SubCaseResponseRt } from '../../../../../common/api'; import { RouteDeps } from '../../types'; import { flattenSubCaseSavedObject, wrapError } from '../../utils'; -import { SUB_CASE_DETAILS_URL } from '../../../../../common'; +import { SUB_CASE_DETAILS_URL } from '../../../../../common/constants'; import { countAlertsForID } from '../../../../common'; export function initGetSubCaseApi({ caseService, router, logger }: RouteDeps) { diff --git a/x-pack/plugins/cases/server/routes/api/cases/sub_case/patch_sub_cases.ts b/x-pack/plugins/cases/server/routes/api/cases/sub_case/patch_sub_cases.ts index 0b142fb5279e5..08836615e1d39 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/sub_case/patch_sub_cases.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/sub_case/patch_sub_cases.ts @@ -35,8 +35,8 @@ import { SubCasesResponseRt, User, CommentAttributes, -} from '../../../../../common'; -import { SUB_CASES_PATCH_DEL_URL } from '../../../../../common'; +} from '../../../../../common/api'; +import { SUB_CASES_PATCH_DEL_URL } from '../../../../../common/constants'; import { RouteDeps } from '../../types'; import { escapeHatch, diff --git a/x-pack/plugins/cases/server/routes/api/cases/tags/get_tags.ts b/x-pack/plugins/cases/server/routes/api/cases/tags/get_tags.ts index d70d6e0b57ee9..f066aa70ec472 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/tags/get_tags.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/tags/get_tags.ts @@ -7,7 +7,7 @@ import { RouteDeps } from '../../types'; import { wrapError } from '../../utils'; -import { CASE_TAGS_URL } from '../../../../../common'; +import { CASE_TAGS_URL } from '../../../../../common/constants'; export function initGetTagsApi({ caseService, router }: RouteDeps) { router.get( diff --git a/x-pack/plugins/cases/server/routes/api/cases/user_actions/get_all_user_actions.ts b/x-pack/plugins/cases/server/routes/api/cases/user_actions/get_all_user_actions.ts index 48393b6af34ae..b5c564648c185 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/user_actions/get_all_user_actions.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/user_actions/get_all_user_actions.ts @@ -9,7 +9,7 @@ import { schema } from '@kbn/config-schema'; import { RouteDeps } from '../../types'; import { wrapError } from '../../utils'; -import { CASE_USER_ACTIONS_URL, SUB_CASE_USER_ACTIONS_URL } from '../../../../../common'; +import { CASE_USER_ACTIONS_URL, SUB_CASE_USER_ACTIONS_URL } from '../../../../../common/constants'; export function initGetAllCaseUserActionsApi({ router, logger }: RouteDeps) { router.get( diff --git a/x-pack/plugins/cases/server/routes/api/utils.test.ts b/x-pack/plugins/cases/server/routes/api/utils.test.ts index 2df17e3abacfa..f6bc1e4f71897 100644 --- a/x-pack/plugins/cases/server/routes/api/utils.test.ts +++ b/x-pack/plugins/cases/server/routes/api/utils.test.ts @@ -30,7 +30,7 @@ import { AssociationType, CaseType, CaseResponse, -} from '../../../common'; +} from '../../../common/api'; describe('Utils', () => { describe('transformNewCase', () => { diff --git a/x-pack/plugins/cases/server/routes/api/utils.ts b/x-pack/plugins/cases/server/routes/api/utils.ts index 9234472c13f5d..8e8862f4157f1 100644 --- a/x-pack/plugins/cases/server/routes/api/utils.ts +++ b/x-pack/plugins/cases/server/routes/api/utils.ts @@ -41,7 +41,7 @@ import { SubCasesFindResponse, User, AlertCommentRequestRt, -} from '../../../common'; +} from '../../../common/api'; import { transformESConnectorToCaseConnector } from './cases/helpers'; import { SortFieldCase } from './types'; diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations.ts b/x-pack/plugins/cases/server/saved_object_types/migrations.ts index 8bbc481124870..bf9694d7e6bb0 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations.ts @@ -14,7 +14,7 @@ import { CaseType, AssociationType, ESConnectorFields, -} from '../../common'; +} from '../../common/api'; interface UnsanitizedCaseConnector { connector_id: string; diff --git a/x-pack/plugins/cases/server/scripts/sub_cases/index.ts b/x-pack/plugins/cases/server/scripts/sub_cases/index.ts index 56f842c10e8f5..ba3bcaa65091c 100644 --- a/x-pack/plugins/cases/server/scripts/sub_cases/index.ts +++ b/x-pack/plugins/cases/server/scripts/sub_cases/index.ts @@ -8,7 +8,9 @@ import yargs from 'yargs'; import { ToolingLog } from '@kbn/dev-utils'; import { KbnClient } from '@kbn/test'; -import { CaseResponse, CaseType, CommentType, ConnectorTypes, CASES_URL } from '../../../common'; +import { CaseResponse, CaseType, ConnectorTypes } from '../../../common/api'; +import { CommentType } from '../../../common/api/cases/comment'; +import { CASES_URL } from '../../../common/constants'; import { ActionResult, ActionTypeExecutorResult } from '../../../../actions/common'; import { ContextTypeGeneratedAlertType, createAlertsString } from '../../connectors'; diff --git a/x-pack/plugins/cases/server/services/alerts/index.test.ts b/x-pack/plugins/cases/server/services/alerts/index.test.ts index 28c3a6278d544..042e415b77e43 100644 --- a/x-pack/plugins/cases/server/services/alerts/index.test.ts +++ b/x-pack/plugins/cases/server/services/alerts/index.test.ts @@ -6,7 +6,7 @@ */ import { KibanaRequest } from 'kibana/server'; -import { CaseStatuses } from '../../../common'; +import { CaseStatuses } from '../../../common/api'; import { AlertService, AlertServiceContract } from '.'; import { elasticsearchServiceMock, loggingSystemMock } from 'src/core/server/mocks'; diff --git a/x-pack/plugins/cases/server/services/alerts/index.ts b/x-pack/plugins/cases/server/services/alerts/index.ts index 876814719442c..6ce4db61ab956 100644 --- a/x-pack/plugins/cases/server/services/alerts/index.ts +++ b/x-pack/plugins/cases/server/services/alerts/index.ts @@ -10,7 +10,7 @@ import { isEmpty } from 'lodash'; import type { PublicMethodsOf } from '@kbn/utility-types'; import { ElasticsearchClient, Logger } from 'kibana/server'; -import { MAX_ALERTS_PER_SUB_CASE } from '../../../common'; +import { MAX_ALERTS_PER_SUB_CASE } from '../../../common/constants'; import { UpdateAlertRequest } from '../../client/types'; import { AlertInfo } from '../../common'; import { createCaseError } from '../../common/error'; diff --git a/x-pack/plugins/cases/server/services/configure/index.ts b/x-pack/plugins/cases/server/services/configure/index.ts index 0ca63bce2d1d0..46dca4d9a0d0e 100644 --- a/x-pack/plugins/cases/server/services/configure/index.ts +++ b/x-pack/plugins/cases/server/services/configure/index.ts @@ -13,7 +13,7 @@ import { SavedObjectsUpdateResponse, } from 'kibana/server'; -import { ESCasesConfigureAttributes, SavedObjectFindOptions } from '../../../common'; +import { ESCasesConfigureAttributes, SavedObjectFindOptions } from '../../../common/api'; import { CASE_CONFIGURE_SAVED_OBJECT } from '../../saved_object_types'; interface ClientArgs { diff --git a/x-pack/plugins/cases/server/services/connector_mappings/index.ts b/x-pack/plugins/cases/server/services/connector_mappings/index.ts index 82f37190b4ecc..d4fda10276d2b 100644 --- a/x-pack/plugins/cases/server/services/connector_mappings/index.ts +++ b/x-pack/plugins/cases/server/services/connector_mappings/index.ts @@ -13,7 +13,7 @@ import { SavedObjectsFindResponse, } from 'kibana/server'; -import { ConnectorMappings, SavedObjectFindOptions } from '../../../common'; +import { ConnectorMappings, SavedObjectFindOptions } from '../../../common/api'; import { CASE_CONNECTOR_MAPPINGS_SAVED_OBJECT } from '../../saved_object_types'; interface ClientArgs { diff --git a/x-pack/plugins/cases/server/services/index.ts b/x-pack/plugins/cases/server/services/index.ts index 18b78300e6632..11ceb48d11e9f 100644 --- a/x-pack/plugins/cases/server/services/index.ts +++ b/x-pack/plugins/cases/server/services/index.ts @@ -33,7 +33,7 @@ import { CaseResponse, caseTypeField, CasesFindRequest, -} from '../../common'; +} from '../../common/api'; import { combineFilters, defaultSortField, groupTotalAlertsByID } from '../common'; import { defaultPage, defaultPerPage } from '../routes/api'; import { diff --git a/x-pack/plugins/cases/server/services/reporters/read_reporters.ts b/x-pack/plugins/cases/server/services/reporters/read_reporters.ts index b47fa185ff78e..d2708780b2ccf 100644 --- a/x-pack/plugins/cases/server/services/reporters/read_reporters.ts +++ b/x-pack/plugins/cases/server/services/reporters/read_reporters.ts @@ -7,7 +7,7 @@ import { SavedObject, SavedObjectsClientContract } from 'kibana/server'; -import { CaseAttributes, User } from '../../../common'; +import { CaseAttributes, User } from '../../../common/api'; import { CASE_SAVED_OBJECT } from '../../saved_object_types'; export const convertToReporters = (caseObjects: Array<SavedObject<CaseAttributes>>): User[] => diff --git a/x-pack/plugins/cases/server/services/tags/read_tags.ts b/x-pack/plugins/cases/server/services/tags/read_tags.ts index a00b0b6f26fb7..4c4a948453730 100644 --- a/x-pack/plugins/cases/server/services/tags/read_tags.ts +++ b/x-pack/plugins/cases/server/services/tags/read_tags.ts @@ -7,7 +7,7 @@ import { SavedObject, SavedObjectsClientContract } from 'kibana/server'; -import { CaseAttributes } from '../../../common'; +import { CaseAttributes } from '../../../common/api'; import { CASE_SAVED_OBJECT } from '../../saved_object_types'; export const convertToTags = (tagObjects: Array<SavedObject<CaseAttributes>>): string[] => diff --git a/x-pack/plugins/cases/server/services/user_actions/helpers.ts b/x-pack/plugins/cases/server/services/user_actions/helpers.ts index be32717039d9d..c600a96234b3d 100644 --- a/x-pack/plugins/cases/server/services/user_actions/helpers.ts +++ b/x-pack/plugins/cases/server/services/user_actions/helpers.ts @@ -17,7 +17,7 @@ import { User, UserActionFieldType, SubCaseAttributes, -} from '../../../common'; +} from '../../../common/api'; import { isTwoArraysDifference, transformESConnectorToCaseConnector, diff --git a/x-pack/plugins/cases/server/services/user_actions/index.ts b/x-pack/plugins/cases/server/services/user_actions/index.ts index a038d843a5331..785c81021b584 100644 --- a/x-pack/plugins/cases/server/services/user_actions/index.ts +++ b/x-pack/plugins/cases/server/services/user_actions/index.ts @@ -12,7 +12,7 @@ import { SavedObjectReference, } from 'kibana/server'; -import { CaseUserActionAttributes } from '../../../common'; +import { CaseUserActionAttributes } from '../../../common/api'; import { CASE_USER_ACTION_SAVED_OBJECT, CASE_SAVED_OBJECT, diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index 47606983b8368..143384d160471 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -23,8 +23,6 @@ export const DEFAULT_REFRESH_RATE_INTERVAL = 'timepicker:refreshIntervalDefaults export const DEFAULT_APP_TIME_RANGE = 'securitySolution:timeDefaults'; export const DEFAULT_APP_REFRESH_INTERVAL = 'securitySolution:refreshIntervalDefaults'; export const DEFAULT_SIGNALS_INDEX = '.siem-signals'; -// The DEFAULT_MAX_SIGNALS value exists also in `x-pack/plugins/cases/common/constants.ts` -// If either changes, engineer should ensure both values are updated export const DEFAULT_MAX_SIGNALS = 100; export const DEFAULT_SEARCH_AFTER_PAGE_SIZE = 100; export const DEFAULT_ANOMALY_SCORE = 'securitySolution:defaultAnomalyScore'; @@ -208,10 +206,3 @@ export const showAllOthersBucket: string[] = [ 'destination.ip', 'user.name', ]; - -/* - Feature Flag for Cases RAC UI - DO NOT MERGE to master as true, dev only -*/ - -export const USE_RAC_CASES_UI = false; diff --git a/x-pack/plugins/security_solution/kibana.json b/x-pack/plugins/security_solution/kibana.json index 50a5f62740271..d4551f76ae390 100644 --- a/x-pack/plugins/security_solution/kibana.json +++ b/x-pack/plugins/security_solution/kibana.json @@ -7,7 +7,6 @@ "requiredPlugins": [ "actions", "alerting", - "cases", "data", "dataEnhanced", "embeddable", diff --git a/x-pack/plugins/security_solution/public/cases/components/add_comment/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/add_comment/index.test.tsx index 6ffce4f2af454..9c06fc032f819 100644 --- a/x-pack/plugins/security_solution/public/cases/components/add_comment/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/add_comment/index.test.tsx @@ -13,7 +13,7 @@ import { noop } from 'lodash/fp'; import { TestProviders } from '../../../common/mock'; import { Router, routeData, mockHistory, mockLocation } from '../__mock__/router'; -import { CommentRequest, CommentType } from '../../../../../cases/common'; +import { CommentRequest, CommentType } from '../../../../../cases/common/api'; import { useInsertTimeline } from '../use_insert_timeline'; import { usePostComment } from '../../containers/use_post_comment'; import { AddComment, AddCommentRefObject } from '.'; diff --git a/x-pack/plugins/security_solution/public/cases/components/add_comment/index.tsx b/x-pack/plugins/security_solution/public/cases/components/add_comment/index.tsx index ff5ef11fd923f..acd27e99a857f 100644 --- a/x-pack/plugins/security_solution/public/cases/components/add_comment/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/add_comment/index.tsx @@ -9,10 +9,10 @@ import { EuiButton, EuiLoadingSpinner } from '@elastic/eui'; import React, { useCallback, forwardRef, useImperativeHandle } from 'react'; import styled from 'styled-components'; -import { CommentType } from '../../../../../cases/common'; +import { CommentType } from '../../../../../cases/common/api'; import { usePostComment } from '../../containers/use_post_comment'; import { Case } from '../../containers/types'; -import { MarkdownEditorForm } from '../../../common/components/markdown_editor'; +import { MarkdownEditorForm } from '../../../common/components/markdown_editor/eui_form'; import { Form, useForm, UseField, useFormData } from '../../../shared_imports'; import * as i18n from './translations'; diff --git a/x-pack/plugins/security_solution/public/cases/components/add_comment/schema.tsx b/x-pack/plugins/security_solution/public/cases/components/add_comment/schema.tsx index bf625fc065089..2cf7d3c6c555b 100644 --- a/x-pack/plugins/security_solution/public/cases/components/add_comment/schema.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/add_comment/schema.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { CommentRequestUserType } from '../../../../../cases/common'; +import { CommentRequestUserType } from '../../../../../cases/common/api'; import { FIELD_TYPES, fieldValidators, FormSchema } from '../../../shared_imports'; import * as i18n from './translations'; diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx index 0353f48e6ee38..daa988641fbab 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx @@ -8,7 +8,7 @@ import { Dispatch } from 'react'; import { DefaultItemIconButtonAction } from '@elastic/eui/src/components/basic_table/action_types'; -import { CaseStatuses } from '../../../../../cases/common'; +import { CaseStatuses } from '../../../../../cases/common/api'; import { Case, SubCase } from '../../containers/types'; import { UpdateCase } from '../../containers/use_get_cases'; import { statuses } from '../status'; diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/columns.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/columns.tsx index 079943d8cbd3b..86f854fd0a145 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/columns.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/columns.tsx @@ -19,7 +19,7 @@ import { RIGHT_ALIGNMENT } from '@elastic/eui/lib/services'; import styled from 'styled-components'; import { DefaultItemIconButtonAction } from '@elastic/eui/src/components/basic_table/action_types'; -import { CaseStatuses, CaseType } from '../../../../../cases/common'; +import { CaseStatuses, CaseType } from '../../../../../cases/common/api'; import { getEmptyTagValue } from '../../../common/components/empty_value'; import { Case, SubCase } from '../../containers/types'; import { FormattedRelativePreferenceDate } from '../../../common/components/formatted_date'; diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/expanded_row.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/expanded_row.tsx index f40e159306e92..43f0d9df49e94 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/expanded_row.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/expanded_row.tsx @@ -10,7 +10,7 @@ import { EuiBasicTable as _EuiBasicTable } from '@elastic/eui'; import styled from 'styled-components'; import { Case, SubCase } from '../../containers/types'; import { CasesColumns } from './columns'; -import { AssociationType } from '../../../../../cases/common'; +import { AssociationType } from '../../../../../cases/common/api'; type ExpandedRowMap = Record<string, Element> | {}; diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/helpers.ts b/x-pack/plugins/security_solution/public/cases/components/all_cases/helpers.ts index 0d5eb2c9ba407..8962d67319371 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/helpers.ts +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/helpers.ts @@ -6,7 +6,7 @@ */ import { filter } from 'lodash/fp'; -import { AssociationType, CaseStatuses, CaseType } from '../../../../../cases/common'; +import { AssociationType, CaseStatuses, CaseType } from '../../../../../cases/common/api'; import { Case, SubCase } from '../../containers/types'; import { statuses } from '../status'; diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx index c079bbc991601..0fafdaf81f095 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx @@ -14,7 +14,7 @@ import { TestProviders } from '../../../common/mock'; import { casesStatus, useGetCasesMockState, collectionCase } from '../../containers/mock'; import * as i18n from './translations'; -import { CaseStatuses, CaseType } from '../../../../../cases/common'; +import { CaseStatuses, CaseType } from '../../../../../cases/common/api'; import { useKibana } from '../../../common/lib/kibana'; import { getEmptyTagValue } from '../../../common/components/empty_value'; import { useDeleteCases } from '../../containers/use_delete_cases'; diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx index a0820486f423f..c5748a321c19b 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx @@ -22,7 +22,7 @@ import styled, { css } from 'styled-components'; import classnames from 'classnames'; import * as i18n from './translations'; -import { CaseStatuses, CaseType } from '../../../../../cases/common'; +import { CaseStatuses, CaseType } from '../../../../../cases/common/api'; import { getCasesColumns } from './columns'; import { Case, DeleteCase, FilterOptions, SortFieldCase, SubCase } from '../../containers/types'; import { useGetCases, UpdateCase } from '../../containers/use_get_cases'; diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/status_filter.test.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/status_filter.test.tsx index f31eda12b3399..5c9f11d1e3a83 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/status_filter.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/status_filter.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { mount } from 'enzyme'; import { waitFor } from '@testing-library/react'; -import { CaseStatuses } from '../../../../../cases/common'; +import { CaseStatuses } from '../../../../../cases/common/api'; import { StatusFilter } from './status_filter'; import { StatusAll } from '../status'; diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.test.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.test.tsx index c4486365cd292..48a642aaf51a9 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { mount } from 'enzyme'; -import { CaseStatuses } from '../../../../../cases/common'; +import { CaseStatuses } from '../../../../../cases/common/api'; import { TestProviders } from '../../../common/mock'; import { useGetTags } from '../../containers/use_get_tags'; import { useGetReporters } from '../../containers/use_get_reporters'; diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx index 434ae46fcfb7a..ff5b511ef9026 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx @@ -10,7 +10,7 @@ import { isEqual } from 'lodash/fp'; import styled from 'styled-components'; import { EuiFlexGroup, EuiFlexItem, EuiFieldSearch, EuiFilterGroup } from '@elastic/eui'; -import { CaseStatuses } from '../../../../../cases/common'; +import { CaseStatuses } from '../../../../../cases/common/api'; import { FilterOptions } from '../../containers/types'; import { useGetTags } from '../../containers/use_get_tags'; import { useGetReporters } from '../../containers/use_get_reporters'; diff --git a/x-pack/plugins/security_solution/public/cases/components/bulk_actions/index.tsx b/x-pack/plugins/security_solution/public/cases/components/bulk_actions/index.tsx index e90ae2b036866..24897a14f0754 100644 --- a/x-pack/plugins/security_solution/public/cases/components/bulk_actions/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/bulk_actions/index.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { EuiContextMenuItem } from '@elastic/eui'; -import { CaseStatuses } from '../../../../../cases/common'; +import { CaseStatuses } from '../../../../../cases/common/api'; import { statuses, CaseStatusWithAllStatus } from '../status'; import * as i18n from './translations'; import { Case } from '../../containers/types'; diff --git a/x-pack/plugins/security_solution/public/cases/components/case_action_bar/helpers.test.ts b/x-pack/plugins/security_solution/public/cases/components/case_action_bar/helpers.test.ts index 64d37de0a6ea9..8e26c0fd7a7ff 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_action_bar/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/cases/components/case_action_bar/helpers.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { CaseStatuses } from '../../../../../cases/common'; +import { CaseStatuses } from '../../../../../cases/common/api'; import { basicCase } from '../../containers/mock'; import { getStatusDate, getStatusTitle } from './helpers'; diff --git a/x-pack/plugins/security_solution/public/cases/components/case_action_bar/helpers.ts b/x-pack/plugins/security_solution/public/cases/components/case_action_bar/helpers.ts index 9dd666c72335b..68a243040145a 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_action_bar/helpers.ts +++ b/x-pack/plugins/security_solution/public/cases/components/case_action_bar/helpers.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { CaseStatuses } from '../../../../../cases/common'; +import { CaseStatuses } from '../../../../../cases/common/api'; import { Case } from '../../containers/types'; import { statuses } from '../status'; diff --git a/x-pack/plugins/security_solution/public/cases/components/case_action_bar/index.tsx b/x-pack/plugins/security_solution/public/cases/components/case_action_bar/index.tsx index fd4e49400d464..63ce441732251 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_action_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/case_action_bar/index.tsx @@ -16,7 +16,7 @@ import { EuiFlexItem, EuiIconTip, } from '@elastic/eui'; -import { CaseStatuses, CaseType } from '../../../../../cases/common'; +import { CaseStatuses, CaseType } from '../../../../../cases/common/api'; import * as i18n from '../case_view/translations'; import { FormattedRelativePreferenceDate } from '../../../common/components/formatted_date'; import { Actions } from './actions'; diff --git a/x-pack/plugins/security_solution/public/cases/components/case_action_bar/status_context_menu.test.tsx b/x-pack/plugins/security_solution/public/cases/components/case_action_bar/status_context_menu.test.tsx index 1f3b9c39017d9..4e414706d1fd7 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_action_bar/status_context_menu.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/case_action_bar/status_context_menu.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { mount } from 'enzyme'; -import { CaseStatuses } from '../../../../../cases/common'; +import { CaseStatuses } from '../../../../../cases/common/api'; import { StatusContextMenu } from './status_context_menu'; describe('SyncAlertsSwitch', () => { diff --git a/x-pack/plugins/security_solution/public/cases/components/case_action_bar/status_context_menu.tsx b/x-pack/plugins/security_solution/public/cases/components/case_action_bar/status_context_menu.tsx index 298d0d7695e8e..92dcd16a86193 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_action_bar/status_context_menu.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/case_action_bar/status_context_menu.tsx @@ -8,7 +8,7 @@ import React, { memo, useCallback, useMemo, useState } from 'react'; import { memoize } from 'lodash/fp'; import { EuiPopover, EuiContextMenuPanel, EuiContextMenuItem } from '@elastic/eui'; -import { caseStatuses, CaseStatuses } from '../../../../../cases/common'; +import { caseStatuses, CaseStatuses } from '../../../../../cases/common/api'; import { Status } from '../status'; interface Props { diff --git a/x-pack/plugins/security_solution/public/cases/components/case_view/helpers.test.tsx b/x-pack/plugins/security_solution/public/cases/components/case_view/helpers.test.tsx index 657a19d40fdd9..18a76e2766d8d 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_view/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/case_view/helpers.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { AssociationType, CommentType } from '../../../../../cases/common'; +import { AssociationType, CommentType } from '../../../../../cases/common/api'; import { Comment } from '../../containers/types'; import { getManualAlertIdsWithNoRuleId, buildAlertsQuery } from './helpers'; diff --git a/x-pack/plugins/security_solution/public/cases/components/case_view/helpers.ts b/x-pack/plugins/security_solution/public/cases/components/case_view/helpers.ts index 741880d886c89..7211f4bca6a37 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_view/helpers.ts +++ b/x-pack/plugins/security_solution/public/cases/components/case_view/helpers.ts @@ -6,7 +6,7 @@ */ import { isEmpty } from 'lodash'; -import { CommentType } from '../../../../../cases/common'; +import { CommentType } from '../../../../../cases/common/api'; import { Comment } from '../../containers/types'; export const getManualAlertIdsWithNoRuleId = (comments: Comment[]): string[] => { diff --git a/x-pack/plugins/security_solution/public/cases/components/case_view/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/case_view/index.test.tsx index 75f91c8ef3035..f28c7791d0110 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_view/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/case_view/index.test.tsx @@ -28,7 +28,8 @@ import { useConnectors } from '../../containers/configure/use_connectors'; import { connectorsMock } from '../../containers/configure/mock'; import { usePostPushToService } from '../../containers/use_post_push_to_service'; import { useQueryAlerts } from '../../../detections/containers/detection_engine/alerts/use_query'; -import { CaseType, ConnectorTypes } from '../../../../../cases/common'; +import { ConnectorTypes } from '../../../../../cases/common/api/connectors'; +import { CaseType } from '../../../../../cases/common/api'; const mockDispatch = jest.fn(); jest.mock('react-redux', () => { diff --git a/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx b/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx index e16f1d7683abc..892663c783293 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx @@ -17,7 +17,7 @@ import { EuiHorizontalRule, } from '@elastic/eui'; -import { CaseStatuses, CaseAttributes, CaseType } from '../../../../../cases/common'; +import { CaseStatuses, CaseAttributes, CaseType } from '../../../../../cases/common/api'; import { Case, CaseConnector } from '../../containers/types'; import { getCaseDetailsUrl, getCaseUrl, useFormatUrl } from '../../../common/components/link_to'; import { gutterTimeline } from '../../../common/lib/helpers'; diff --git a/x-pack/plugins/security_solution/public/cases/components/configure_cases/__mock__/index.tsx b/x-pack/plugins/security_solution/public/cases/components/configure_cases/__mock__/index.tsx index e18e0ef004ceb..ccc697a2ae84e 100644 --- a/x-pack/plugins/security_solution/public/cases/components/configure_cases/__mock__/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/configure_cases/__mock__/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { ConnectorTypes } from '../../../../../../cases/common'; +import { ConnectorTypes } from '../../../../../../cases/common/api'; import { ActionConnector } from '../../../containers/configure/types'; import { UseConnectorsResponse } from '../../../containers/configure/use_connectors'; import { ReturnUseCaseConfigure } from '../../../containers/configure/use_configure'; diff --git a/x-pack/plugins/security_solution/public/cases/components/configure_cases/connectors.test.tsx b/x-pack/plugins/security_solution/public/cases/components/configure_cases/connectors.test.tsx index 1c01bb3fdeb7b..c34651c3e1dc4 100644 --- a/x-pack/plugins/security_solution/public/cases/components/configure_cases/connectors.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/configure_cases/connectors.test.tsx @@ -12,7 +12,7 @@ import { Connectors, Props } from './connectors'; import { TestProviders } from '../../../common/mock'; import { ConnectorsDropdown } from './connectors_dropdown'; import { connectors } from './__mock__'; -import { ConnectorTypes } from '../../../../../cases/common'; +import { ConnectorTypes } from '../../../../../cases/common/api/connectors'; describe('Connectors', () => { let wrapper: ReactWrapper; diff --git a/x-pack/plugins/security_solution/public/cases/components/configure_cases/connectors.tsx b/x-pack/plugins/security_solution/public/cases/components/configure_cases/connectors.tsx index c0a5e3c4c8f72..1e0ae95ff901c 100644 --- a/x-pack/plugins/security_solution/public/cases/components/configure_cases/connectors.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/configure_cases/connectors.tsx @@ -21,7 +21,7 @@ import * as i18n from './translations'; import { ActionConnector, CaseConnectorMapping } from '../../containers/configure/types'; import { Mapping } from './mapping'; -import { ConnectorTypes } from '../../../../../cases/common'; +import { ConnectorTypes } from '../../../../../cases/common/api/connectors'; const EuiFormRowExtended = styled(EuiFormRow)` .euiFormRow__labelWrapper { diff --git a/x-pack/plugins/security_solution/public/cases/components/configure_cases/connectors_dropdown.tsx b/x-pack/plugins/security_solution/public/cases/components/configure_cases/connectors_dropdown.tsx index 27f7f4d50a0c9..01d975a445ab4 100644 --- a/x-pack/plugins/security_solution/public/cases/components/configure_cases/connectors_dropdown.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/configure_cases/connectors_dropdown.tsx @@ -9,7 +9,7 @@ import React, { useMemo } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiSuperSelect } from '@elastic/eui'; import styled from 'styled-components'; -import { ConnectorTypes } from '../../../../../cases/common'; +import { ConnectorTypes } from '../../../../../cases/common/api'; import { ActionConnector } from '../../containers/configure/types'; import { connectorsConfiguration } from '../connectors'; import * as i18n from './translations'; diff --git a/x-pack/plugins/security_solution/public/cases/components/configure_cases/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/configure_cases/index.test.tsx index e78cd4c509d5d..8dbefdb731141 100644 --- a/x-pack/plugins/security_solution/public/cases/components/configure_cases/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/configure_cases/index.test.tsx @@ -33,7 +33,7 @@ import { useConnectorsResponse, useActionTypesResponse, } from './__mock__'; -import { ConnectorTypes } from '../../../../../cases/common'; +import { ConnectorTypes } from '../../../../../cases/common/api/connectors'; jest.mock('../../../common/lib/kibana'); jest.mock('../../containers/configure/use_connectors'); diff --git a/x-pack/plugins/security_solution/public/cases/components/configure_cases/index.tsx b/x-pack/plugins/security_solution/public/cases/components/configure_cases/index.tsx index e951498c6c3c9..25155ff77c2d0 100644 --- a/x-pack/plugins/security_solution/public/cases/components/configure_cases/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/configure_cases/index.tsx @@ -10,7 +10,7 @@ import styled, { css } from 'styled-components'; import { EuiCallOut } from '@elastic/eui'; -import { SUPPORTED_CONNECTORS } from '../../../../../cases/common'; +import { SUPPORTED_CONNECTORS } from '../../../../../cases/common/constants'; import { useKibana } from '../../../common/lib/kibana'; import { useConnectors } from '../../containers/configure/use_connectors'; import { useActionTypes } from '../../containers/configure/use_action_types'; diff --git a/x-pack/plugins/security_solution/public/cases/components/configure_cases/utils.ts b/x-pack/plugins/security_solution/public/cases/components/configure_cases/utils.ts index dfb19250f5bd6..db14371b625d8 100644 --- a/x-pack/plugins/security_solution/public/cases/components/configure_cases/utils.ts +++ b/x-pack/plugins/security_solution/public/cases/components/configure_cases/utils.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ConnectorTypeFields, ConnectorTypes } from '../../../../../cases/common'; +import { ConnectorTypeFields, ConnectorTypes } from '../../../../../cases/common/api'; import { CaseField, ActionType, diff --git a/x-pack/plugins/security_solution/public/cases/components/connector_selector/form.tsx b/x-pack/plugins/security_solution/public/cases/components/connector_selector/form.tsx index f0e77648cee6c..63c6f265b1ab2 100644 --- a/x-pack/plugins/security_solution/public/cases/components/connector_selector/form.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/connector_selector/form.tsx @@ -11,7 +11,7 @@ import { EuiFormRow } from '@elastic/eui'; import { FieldHook, getFieldValidityAndErrorMessage } from '../../../shared_imports'; import { ConnectorsDropdown } from '../configure_cases/connectors_dropdown'; -import { ActionConnector } from '../../../../../cases/common'; +import { ActionConnector } from '../../../../../cases/common/api'; interface ConnectorSelectorProps { connectors: ActionConnector[]; diff --git a/x-pack/plugins/security_solution/public/cases/components/connectors/card.tsx b/x-pack/plugins/security_solution/public/cases/components/connectors/card.tsx index dded090eb3f98..af9a86b0b711b 100644 --- a/x-pack/plugins/security_solution/public/cases/components/connectors/card.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/connectors/card.tsx @@ -10,7 +10,7 @@ import { EuiCard, EuiIcon, EuiLoadingSpinner } from '@elastic/eui'; import styled from 'styled-components'; import { connectorsConfiguration } from '.'; -import { ConnectorTypes } from '../../../../../cases/common'; +import { ConnectorTypes } from '../../../../../cases/common/api/connectors'; interface ConnectorCardProps { connectorType: ConnectorTypes; diff --git a/x-pack/plugins/security_solution/public/cases/components/connectors/case/alert_fields.tsx b/x-pack/plugins/security_solution/public/cases/components/connectors/case/alert_fields.tsx index b182c878d78e6..05161456976c6 100644 --- a/x-pack/plugins/security_solution/public/cases/components/connectors/case/alert_fields.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/connectors/case/alert_fields.tsx @@ -12,7 +12,7 @@ import styled from 'styled-components'; import { EuiCallOut, EuiSpacer } from '@elastic/eui'; import { ActionParamsProps } from '../../../../../../triggers_actions_ui/public/types'; -import { CommentType } from '../../../../../../cases/common'; +import { CommentType } from '../../../../../../cases/common/api'; import { CaseActionParams } from './types'; import { ExistingCase } from './existing_case'; diff --git a/x-pack/plugins/security_solution/public/cases/components/connectors/case/existing_case.tsx b/x-pack/plugins/security_solution/public/cases/components/connectors/case/existing_case.tsx index c503a62ef515e..3c6c5f47c6d12 100644 --- a/x-pack/plugins/security_solution/public/cases/components/connectors/case/existing_case.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/connectors/case/existing_case.tsx @@ -6,7 +6,7 @@ */ import React, { memo, useMemo, useCallback } from 'react'; -import { CaseType } from '../../../../../../cases/common'; +import { CaseType } from '../../../../../../cases/common/api'; import { useGetCases, DEFAULT_QUERY_PARAMS, diff --git a/x-pack/plugins/security_solution/public/cases/components/connectors/fields_form.tsx b/x-pack/plugins/security_solution/public/cases/components/connectors/fields_form.tsx index 035f1fa2b63ac..841c2a9e38f6d 100644 --- a/x-pack/plugins/security_solution/public/cases/components/connectors/fields_form.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/connectors/fields_form.tsx @@ -10,7 +10,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; import { CaseActionConnector, ConnectorFieldsProps } from './types'; import { getCaseConnectors } from '.'; -import { ConnectorTypeFields } from '../../../../../cases/common'; +import { ConnectorTypeFields } from '../../../../../cases/common/api/connectors'; interface Props extends Omit<ConnectorFieldsProps<ConnectorTypeFields['fields']>, 'connector'> { connector: CaseActionConnector | null; diff --git a/x-pack/plugins/security_solution/public/cases/components/connectors/index.ts b/x-pack/plugins/security_solution/public/cases/components/connectors/index.ts index 76f6ccb6a1adb..dad7070aad705 100644 --- a/x-pack/plugins/security_solution/public/cases/components/connectors/index.ts +++ b/x-pack/plugins/security_solution/public/cases/components/connectors/index.ts @@ -15,7 +15,7 @@ import { ServiceNowITSMFieldsType, ServiceNowSIRFieldsType, ResilientFieldsType, -} from '../../../../../cases/common'; +} from '../../../../../cases/common/api/connectors'; export { getActionType as getCaseConnectorUI } from './case'; diff --git a/x-pack/plugins/security_solution/public/cases/components/connectors/jira/case_fields.tsx b/x-pack/plugins/security_solution/public/cases/components/connectors/jira/case_fields.tsx index 985537e799596..22e80d43f34e1 100644 --- a/x-pack/plugins/security_solution/public/cases/components/connectors/jira/case_fields.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/connectors/jira/case_fields.tsx @@ -10,7 +10,7 @@ import { map } from 'lodash/fp'; import { EuiFormRow, EuiSelect, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import * as i18n from './translations'; -import { ConnectorTypes, JiraFieldsType } from '../../../../../../cases/common'; +import { ConnectorTypes, JiraFieldsType } from '../../../../../../cases/common/api/connectors'; import { useKibana } from '../../../../common/lib/kibana'; import { ConnectorFieldsProps } from '../types'; import { useGetIssueTypes } from './use_get_issue_types'; diff --git a/x-pack/plugins/security_solution/public/cases/components/connectors/jira/index.ts b/x-pack/plugins/security_solution/public/cases/components/connectors/jira/index.ts index 1069e489ada09..40e59a081a449 100644 --- a/x-pack/plugins/security_solution/public/cases/components/connectors/jira/index.ts +++ b/x-pack/plugins/security_solution/public/cases/components/connectors/jira/index.ts @@ -8,7 +8,7 @@ import { lazy } from 'react'; import { CaseConnector } from '../types'; -import { JiraFieldsType } from '../../../../../../cases/common'; +import { JiraFieldsType } from '../../../../../../cases/common/api/connectors'; import * as i18n from './translations'; export * from './types'; diff --git a/x-pack/plugins/security_solution/public/cases/components/connectors/resilient/case_fields.tsx b/x-pack/plugins/security_solution/public/cases/components/connectors/resilient/case_fields.tsx index ae9b5a4dd6f49..b1fbfb1169d08 100644 --- a/x-pack/plugins/security_solution/public/cases/components/connectors/resilient/case_fields.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/connectors/resilient/case_fields.tsx @@ -21,7 +21,7 @@ import { useGetIncidentTypes } from './use_get_incident_types'; import { useGetSeverity } from './use_get_severity'; import * as i18n from './translations'; -import { ConnectorTypes, ResilientFieldsType } from '../../../../../../cases/common'; +import { ConnectorTypes, ResilientFieldsType } from '../../../../../../cases/common/api/connectors'; import { ConnectorCard } from '../card'; const ResilientFieldsComponent: React.FunctionComponent< diff --git a/x-pack/plugins/security_solution/public/cases/components/connectors/resilient/index.ts b/x-pack/plugins/security_solution/public/cases/components/connectors/resilient/index.ts index 21850cdfe4d92..8a2603f39e102 100644 --- a/x-pack/plugins/security_solution/public/cases/components/connectors/resilient/index.ts +++ b/x-pack/plugins/security_solution/public/cases/components/connectors/resilient/index.ts @@ -8,7 +8,7 @@ import { lazy } from 'react'; import { CaseConnector } from '../types'; -import { ResilientFieldsType } from '../../../../../../cases/common'; +import { ResilientFieldsType } from '../../../../../../cases/common/api/connectors'; import * as i18n from './translations'; export * from './types'; diff --git a/x-pack/plugins/security_solution/public/cases/components/connectors/servicenow/index.ts b/x-pack/plugins/security_solution/public/cases/components/connectors/servicenow/index.ts index 02441b2b9f7aa..b342095c39ff0 100644 --- a/x-pack/plugins/security_solution/public/cases/components/connectors/servicenow/index.ts +++ b/x-pack/plugins/security_solution/public/cases/components/connectors/servicenow/index.ts @@ -8,7 +8,10 @@ import { lazy } from 'react'; import { CaseConnector } from '../types'; -import { ServiceNowITSMFieldsType, ServiceNowSIRFieldsType } from '../../../../../../cases/common'; +import { + ServiceNowITSMFieldsType, + ServiceNowSIRFieldsType, +} from '../../../../../../cases/common/api/connectors'; import * as i18n from './translations'; export const getServiceNowITSMCaseConnector = (): CaseConnector<ServiceNowITSMFieldsType> => { diff --git a/x-pack/plugins/security_solution/public/cases/components/connectors/servicenow/servicenow_itsm_case_fields.tsx b/x-pack/plugins/security_solution/public/cases/components/connectors/servicenow/servicenow_itsm_case_fields.tsx index f705c9005e480..accb8450802d4 100644 --- a/x-pack/plugins/security_solution/public/cases/components/connectors/servicenow/servicenow_itsm_case_fields.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/connectors/servicenow/servicenow_itsm_case_fields.tsx @@ -10,7 +10,10 @@ import { EuiFormRow, EuiSelect, EuiSpacer, EuiFlexGroup, EuiFlexItem } from '@el import * as i18n from './translations'; import { ConnectorFieldsProps } from '../types'; -import { ConnectorTypes, ServiceNowITSMFieldsType } from '../../../../../../cases/common'; +import { + ConnectorTypes, + ServiceNowITSMFieldsType, +} from '../../../../../../cases/common/api/connectors'; import { useKibana } from '../../../../common/lib/kibana'; import { ConnectorCard } from '../card'; import { useGetChoices } from './use_get_choices'; diff --git a/x-pack/plugins/security_solution/public/cases/components/connectors/servicenow/servicenow_sir_case_fields.tsx b/x-pack/plugins/security_solution/public/cases/components/connectors/servicenow/servicenow_sir_case_fields.tsx index 2bac7e01a00b2..63502e3454fcf 100644 --- a/x-pack/plugins/security_solution/public/cases/components/connectors/servicenow/servicenow_sir_case_fields.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/connectors/servicenow/servicenow_sir_case_fields.tsx @@ -8,7 +8,10 @@ import React, { useCallback, useEffect, useMemo, useState, useRef } from 'react'; import { EuiFormRow, EuiSelect, EuiFlexGroup, EuiFlexItem, EuiCheckbox } from '@elastic/eui'; -import { ConnectorTypes, ServiceNowSIRFieldsType } from '../../../../../../cases/common'; +import { + ConnectorTypes, + ServiceNowSIRFieldsType, +} from '../../../../../../cases/common/api/connectors'; import { useKibana } from '../../../../common/lib/kibana'; import { ConnectorFieldsProps } from '../types'; import { ConnectorCard } from '../card'; diff --git a/x-pack/plugins/security_solution/public/cases/components/connectors/types.ts b/x-pack/plugins/security_solution/public/cases/components/connectors/types.ts index 86f0238dd450f..11452b966670b 100644 --- a/x-pack/plugins/security_solution/public/cases/components/connectors/types.ts +++ b/x-pack/plugins/security_solution/public/cases/components/connectors/types.ts @@ -12,9 +12,9 @@ import { CaseField, ActionConnector, ConnectorTypeFields, -} from '../../../../../cases/common'; +} from '../../../../../cases/common/api'; -export { ThirdPartyField as AllThirdPartyFields } from '../../../../../cases/common'; +export { ThirdPartyField as AllThirdPartyFields } from '../../../../../cases/common/api'; export type CaseActionConnector = ActionConnector; export interface ThirdPartyField { diff --git a/x-pack/plugins/security_solution/public/cases/components/create/connector.tsx b/x-pack/plugins/security_solution/public/cases/components/create/connector.tsx index 516cc5a0d23a5..7912d97528cd2 100644 --- a/x-pack/plugins/security_solution/public/cases/components/create/connector.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/create/connector.tsx @@ -8,7 +8,7 @@ import React, { memo, useCallback } from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { ConnectorTypes } from '../../../../../cases/common'; +import { ConnectorTypes } from '../../../../../cases/common/api'; import { UseField, useFormData, FieldHook, useFormContext } from '../../../shared_imports'; import { useConnectors } from '../../containers/configure/use_connectors'; import { ConnectorSelector } from '../connector_selector/form'; diff --git a/x-pack/plugins/security_solution/public/cases/components/create/form_context.test.tsx b/x-pack/plugins/security_solution/public/cases/components/create/form_context.test.tsx index 9d14acc96c192..99626c4cfb797 100644 --- a/x-pack/plugins/security_solution/public/cases/components/create/form_context.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/create/form_context.test.tsx @@ -10,7 +10,7 @@ import { mount, ReactWrapper } from 'enzyme'; import { act, waitFor } from '@testing-library/react'; import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; -import { ConnectorTypes } from '../../../../../cases/common'; +import { ConnectorTypes } from '../../../../../cases/common/api'; import { TestProviders } from '../../../common/mock'; import { usePostCase } from '../../containers/use_post_case'; import { useGetTags } from '../../containers/use_get_tags'; diff --git a/x-pack/plugins/security_solution/public/cases/components/create/form_context.tsx b/x-pack/plugins/security_solution/public/cases/components/create/form_context.tsx index 597726e7bb3f3..b575dfe42f074 100644 --- a/x-pack/plugins/security_solution/public/cases/components/create/form_context.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/create/form_context.tsx @@ -19,7 +19,7 @@ import { usePostPushToService } from '../../containers/use_post_push_to_service' import { useConnectors } from '../../containers/configure/use_connectors'; import { useCaseConfigure } from '../../containers/configure/use_configure'; import { Case } from '../../containers/types'; -import { CaseType, ConnectorTypes } from '../../../../../cases/common'; +import { CaseType, ConnectorTypes } from '../../../../../cases/common/api'; const initialCaseValue: FormProps = { description: '', diff --git a/x-pack/plugins/security_solution/public/cases/components/create/index.tsx b/x-pack/plugins/security_solution/public/cases/components/create/index.tsx index 484a45248d8c0..9f904350b772e 100644 --- a/x-pack/plugins/security_solution/public/cases/components/create/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/create/index.tsx @@ -18,8 +18,6 @@ import { FormContext } from './form_context'; import { useInsertTimeline } from '../use_insert_timeline'; import { fieldName as descriptionFieldName } from './description'; import { SubmitCaseButton } from './submit_button'; -import { USE_RAC_CASES_UI } from '../../../../common/constants'; -import { useKibana } from '../../../common/lib/kibana'; export const CommonUseField = getUseField({ component: Field }); @@ -41,7 +39,6 @@ const InsertTimeline = () => { }; export const Create = React.memo(() => { - const { cases } = useKibana().services; const history = useHistory(); const onSuccess = useCallback( async ({ id }) => { @@ -56,39 +53,32 @@ export const Create = React.memo(() => { return ( <EuiPanel> - {USE_RAC_CASES_UI ? ( - cases.getCreateCase({ - onCancel: handleSetIsCancel, - onSuccess, - }) - ) : ( - <FormContext onSuccess={onSuccess}> - <CreateCaseForm /> - <Container> - <EuiFlexGroup - alignItems="center" - justifyContent="flexEnd" - gutterSize="xs" - responsive={false} - > - <EuiFlexItem grow={false}> - <EuiButtonEmpty - data-test-subj="create-case-cancel" - size="s" - onClick={handleSetIsCancel} - iconType="cross" - > - {i18n.CANCEL} - </EuiButtonEmpty> - </EuiFlexItem> - <EuiFlexItem grow={false}> - <SubmitCaseButton /> - </EuiFlexItem> - </EuiFlexGroup> - </Container> - <InsertTimeline /> - </FormContext> - )} + <FormContext onSuccess={onSuccess}> + <CreateCaseForm /> + <Container> + <EuiFlexGroup + alignItems="center" + justifyContent="flexEnd" + gutterSize="xs" + responsive={false} + > + <EuiFlexItem grow={false}> + <EuiButtonEmpty + data-test-subj="create-case-cancel" + size="s" + onClick={handleSetIsCancel} + iconType="cross" + > + {i18n.CANCEL} + </EuiButtonEmpty> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <SubmitCaseButton /> + </EuiFlexItem> + </EuiFlexGroup> + </Container> + <InsertTimeline /> + </FormContext> </EuiPanel> ); }); diff --git a/x-pack/plugins/security_solution/public/cases/components/create/mock.ts b/x-pack/plugins/security_solution/public/cases/components/create/mock.ts index a983add030a1e..6e17be8d53e5a 100644 --- a/x-pack/plugins/security_solution/public/cases/components/create/mock.ts +++ b/x-pack/plugins/security_solution/public/cases/components/create/mock.ts @@ -5,7 +5,8 @@ * 2.0. */ -import { CasePostRequest, CaseType, ConnectorTypes } from '../../../../../cases/common'; +import { CasePostRequest, CaseType } from '../../../../../cases/common/api'; +import { ConnectorTypes } from '../../../../../cases/common/api/connectors'; import { choices } from '../connectors/mock'; export const sampleTags = ['coke', 'pepsi']; diff --git a/x-pack/plugins/security_solution/public/cases/components/create/schema.tsx b/x-pack/plugins/security_solution/public/cases/components/create/schema.tsx index 38321cdbeab50..b069a484d314c 100644 --- a/x-pack/plugins/security_solution/public/cases/components/create/schema.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/create/schema.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { CasePostRequest, ConnectorTypeFields } from '../../../../../cases/common'; +import { CasePostRequest, ConnectorTypeFields } from '../../../../../cases/common/api'; import { FIELD_TYPES, fieldValidators, FormSchema } from '../../../shared_imports'; import * as i18n from './translations'; diff --git a/x-pack/plugins/security_solution/public/cases/components/edit_connector/index.tsx b/x-pack/plugins/security_solution/public/cases/components/edit_connector/index.tsx index 0ecb66d542334..f76adfd2a840f 100644 --- a/x-pack/plugins/security_solution/public/cases/components/edit_connector/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/edit_connector/index.tsx @@ -21,8 +21,9 @@ import styled from 'styled-components'; import { noop } from 'lodash/fp'; import { Form, UseField, useForm } from '../../../shared_imports'; -import { ActionConnector, ConnectorTypeFields } from '../../../../../cases/common'; +import { ConnectorTypeFields } from '../../../../../cases/common/api/connectors'; import { ConnectorSelector } from '../connector_selector/form'; +import { ActionConnector } from '../../../../../cases/common/api'; import { ConnectorFieldsForm } from '../connectors/fields_form'; import { getConnectorById } from '../configure_cases/utils'; import { CaseUserActions } from '../../containers/types'; diff --git a/x-pack/plugins/security_solution/public/cases/components/status/button.test.tsx b/x-pack/plugins/security_solution/public/cases/components/status/button.test.tsx index 3c019369fa08b..6bf4eb95bc049 100644 --- a/x-pack/plugins/security_solution/public/cases/components/status/button.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/status/button.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { mount } from 'enzyme'; -import { CaseStatuses } from '../../../../../cases/common'; +import { CaseStatuses } from '../../../../../cases/common/api'; import { StatusActionButton } from './button'; describe('StatusActionButton', () => { diff --git a/x-pack/plugins/security_solution/public/cases/components/status/button.tsx b/x-pack/plugins/security_solution/public/cases/components/status/button.tsx index 6aa8f540e2e95..5a0d98fc8a11a 100644 --- a/x-pack/plugins/security_solution/public/cases/components/status/button.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/status/button.tsx @@ -8,7 +8,7 @@ import React, { memo, useCallback, useMemo } from 'react'; import { EuiButton } from '@elastic/eui'; -import { CaseStatuses, caseStatuses } from '../../../../../cases/common'; +import { CaseStatuses, caseStatuses } from '../../../../../cases/common/api'; import { statuses } from './config'; interface Props { diff --git a/x-pack/plugins/security_solution/public/cases/components/status/config.ts b/x-pack/plugins/security_solution/public/cases/components/status/config.ts index b7bc7dfa36110..47a74549f03cc 100644 --- a/x-pack/plugins/security_solution/public/cases/components/status/config.ts +++ b/x-pack/plugins/security_solution/public/cases/components/status/config.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { CaseStatuses } from '../../../../../cases/common'; +import { CaseStatuses } from '../../../../../cases/common/api'; import * as i18n from './translations'; import { AllCaseStatus, Statuses, StatusAll } from './types'; diff --git a/x-pack/plugins/security_solution/public/cases/components/status/stats.test.tsx b/x-pack/plugins/security_solution/public/cases/components/status/stats.test.tsx index 0bf3297361446..266ceb04e4335 100644 --- a/x-pack/plugins/security_solution/public/cases/components/status/stats.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/status/stats.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { mount } from 'enzyme'; -import { CaseStatuses } from '../../../../../cases/common'; +import { CaseStatuses } from '../../../../../cases/common/api'; import { Stats } from './stats'; describe('Stats', () => { diff --git a/x-pack/plugins/security_solution/public/cases/components/status/stats.tsx b/x-pack/plugins/security_solution/public/cases/components/status/stats.tsx index 93b8479a55d71..43001c2cf5947 100644 --- a/x-pack/plugins/security_solution/public/cases/components/status/stats.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/status/stats.tsx @@ -7,7 +7,7 @@ import React, { memo, useMemo } from 'react'; import { EuiDescriptionList, EuiLoadingSpinner } from '@elastic/eui'; -import { CaseStatuses } from '../../../../../cases/common'; +import { CaseStatuses } from '../../../../../cases/common/api'; import { statuses } from './config'; export interface Props { diff --git a/x-pack/plugins/security_solution/public/cases/components/status/status.test.tsx b/x-pack/plugins/security_solution/public/cases/components/status/status.test.tsx index 05c3b95e163e6..eff9d73c2adf9 100644 --- a/x-pack/plugins/security_solution/public/cases/components/status/status.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/status/status.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { mount } from 'enzyme'; -import { CaseStatuses } from '../../../../../cases/common'; +import { CaseStatuses } from '../../../../../cases/common/api'; import { Status } from './status'; describe('Stats', () => { diff --git a/x-pack/plugins/security_solution/public/cases/components/status/types.ts b/x-pack/plugins/security_solution/public/cases/components/status/types.ts index bbe44bce55515..5618e7802579d 100644 --- a/x-pack/plugins/security_solution/public/cases/components/status/types.ts +++ b/x-pack/plugins/security_solution/public/cases/components/status/types.ts @@ -6,7 +6,7 @@ */ import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; -import { CaseStatuses } from '../../../../../cases/common'; +import { CaseStatuses } from '../../../../../cases/common/api'; export const StatusAll = 'all' as const; type StatusAllType = typeof StatusAll; diff --git a/x-pack/plugins/security_solution/public/cases/components/timeline_actions/add_to_case_action.tsx b/x-pack/plugins/security_solution/public/cases/components/timeline_actions/add_to_case_action.tsx index ec5a3825ff652..decd37a7646e7 100644 --- a/x-pack/plugins/security_solution/public/cases/components/timeline_actions/add_to_case_action.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/timeline_actions/add_to_case_action.tsx @@ -16,7 +16,7 @@ import { EuiToolTip, } from '@elastic/eui'; -import { CommentType, CaseStatuses } from '../../../../../cases/common'; +import { CommentType, CaseStatuses } from '../../../../../cases/common/api'; import { Ecs } from '../../../../common/ecs'; import { ActionIconItem } from '../../../timelines/components/timeline/body/actions/action_icon_item'; import { usePostComment } from '../../containers/use_post_comment'; diff --git a/x-pack/plugins/security_solution/public/cases/components/use_all_cases_modal/all_cases_modal.tsx b/x-pack/plugins/security_solution/public/cases/components/use_all_cases_modal/all_cases_modal.tsx index 54a5dd1263961..10ad3d35004ba 100644 --- a/x-pack/plugins/security_solution/public/cases/components/use_all_cases_modal/all_cases_modal.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/use_all_cases_modal/all_cases_modal.tsx @@ -10,7 +10,7 @@ import styled from 'styled-components'; import { EuiModal, EuiModalBody, EuiModalHeader, EuiModalHeaderTitle } from '@elastic/eui'; import { useGetUserSavedObjectPermissions } from '../../../common/lib/kibana'; -import { CaseStatuses } from '../../../../../cases/common'; +import { CaseStatuses } from '../../../../../cases/common/api'; import { Case, SubCase } from '../../containers/types'; import { AllCases } from '../all_cases'; import * as i18n from './translations'; diff --git a/x-pack/plugins/security_solution/public/cases/components/use_all_cases_modal/index.tsx b/x-pack/plugins/security_solution/public/cases/components/use_all_cases_modal/index.tsx index 23cc11ef2ef28..0b30f6ac94e03 100644 --- a/x-pack/plugins/security_solution/public/cases/components/use_all_cases_modal/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/use_all_cases_modal/index.tsx @@ -6,7 +6,7 @@ */ import React, { useState, useCallback, useMemo } from 'react'; -import { CaseStatuses } from '../../../../../cases/common'; +import { CaseStatuses } from '../../../../../cases/common/api'; import { Case, SubCase } from '../../containers/types'; import { AllCasesModal } from './all_cases_modal'; diff --git a/x-pack/plugins/security_solution/public/cases/components/use_create_case_modal/create_case_modal.tsx b/x-pack/plugins/security_solution/public/cases/components/use_create_case_modal/create_case_modal.tsx index 627dc61c36b0c..4b5eb00d95a80 100644 --- a/x-pack/plugins/security_solution/public/cases/components/use_create_case_modal/create_case_modal.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/use_create_case_modal/create_case_modal.tsx @@ -14,7 +14,7 @@ import { CreateCaseForm } from '../create/form'; import { SubmitCaseButton } from '../create/submit_button'; import { Case } from '../../containers/types'; import * as i18n from '../../translations'; -import { CaseType } from '../../../../../cases/common'; +import { CaseType } from '../../../../../cases/common/api'; export interface CreateCaseModalProps { isModalOpen: boolean; diff --git a/x-pack/plugins/security_solution/public/cases/components/use_create_case_modal/index.tsx b/x-pack/plugins/security_solution/public/cases/components/use_create_case_modal/index.tsx index e29ee3f8712da..5d2f54bd1f142 100644 --- a/x-pack/plugins/security_solution/public/cases/components/use_create_case_modal/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/use_create_case_modal/index.tsx @@ -6,7 +6,7 @@ */ import React, { useState, useCallback, useMemo } from 'react'; -import { CaseType } from '../../../../../cases/common'; +import { CaseType } from '../../../../../cases/common/api'; import { Case } from '../../containers/types'; import { CreateCaseModal } from './create_case_modal'; diff --git a/x-pack/plugins/security_solution/public/cases/components/use_push_to_service/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/use_push_to_service/index.test.tsx index 928d0167bbe85..c058473bbfe3f 100644 --- a/x-pack/plugins/security_solution/public/cases/components/use_push_to_service/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/use_push_to_service/index.test.tsx @@ -13,11 +13,12 @@ import '../../../common/mock/match_media'; import { usePushToService, ReturnUsePushToService, UsePushToService } from '.'; import { TestProviders } from '../../../common/mock'; -import { CaseStatuses, ConnectorTypes } from '../../../../../cases/common'; +import { CaseStatuses } from '../../../../../cases/common/api'; import { usePostPushToService } from '../../containers/use_post_push_to_service'; import { basicPush, actionLicenses } from '../../containers/mock'; import { useGetActionLicense } from '../../containers/use_get_action_license'; import { connectorsMock } from '../../containers/configure/mock'; +import { ConnectorTypes } from '../../../../../cases/common/api/connectors'; jest.mock('react-router-dom', () => { const original = jest.requireActual('react-router-dom'); diff --git a/x-pack/plugins/security_solution/public/cases/components/use_push_to_service/index.tsx b/x-pack/plugins/security_solution/public/cases/components/use_push_to_service/index.tsx index 42284cfa7da49..d83ddb08b51d2 100644 --- a/x-pack/plugins/security_solution/public/cases/components/use_push_to_service/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/use_push_to_service/index.tsx @@ -17,7 +17,7 @@ import { getConfigureCasesUrl, useFormatUrl } from '../../../common/components/l import { CaseCallOut } from '../callout'; import { getLicenseError, getKibanaConfigError } from './helpers'; import * as i18n from './translations'; -import { CaseConnector, ActionConnector, CaseStatuses } from '../../../../../cases/common'; +import { CaseConnector, ActionConnector, CaseStatuses } from '../../../../../cases/common/api'; import { CaseServices } from '../../containers/use_get_case_user_actions'; import { LinkAnchor } from '../../../common/components/links'; import { SecurityPageName } from '../../../app/types'; diff --git a/x-pack/plugins/security_solution/public/cases/components/user_action_tree/helpers.test.tsx b/x-pack/plugins/security_solution/public/cases/components/user_action_tree/helpers.test.tsx index 84408557eb5ae..a62c6c0ef682d 100644 --- a/x-pack/plugins/security_solution/public/cases/components/user_action_tree/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/user_action_tree/helpers.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { mount } from 'enzyme'; -import { CaseStatuses } from '../../../../../cases/common'; +import { CaseStatuses } from '../../../../../cases/common/api'; import { basicPush, getUserAction } from '../../containers/mock'; import { getLabelTitle, getPushedServiceLabelTitle, getConnectorLabelTitle } from './helpers'; import { connectorsMock } from '../../containers/configure/mock'; diff --git a/x-pack/plugins/security_solution/public/cases/components/user_action_tree/helpers.tsx b/x-pack/plugins/security_solution/public/cases/components/user_action_tree/helpers.tsx index a97e2e98cb9af..cc8d560f91b1f 100644 --- a/x-pack/plugins/security_solution/public/cases/components/user_action_tree/helpers.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/user_action_tree/helpers.tsx @@ -15,7 +15,7 @@ import { ActionConnector, CaseStatuses, CommentType, -} from '../../../../../cases/common'; +} from '../../../../../cases/common/api'; import { CaseUserActions } from '../../containers/types'; import { CaseServices } from '../../containers/use_get_case_user_actions'; import { parseString } from '../../containers/utils'; diff --git a/x-pack/plugins/security_solution/public/cases/components/user_action_tree/index.tsx b/x-pack/plugins/security_solution/public/cases/components/user_action_tree/index.tsx index d372d62ab16bb..f8d6872a4b740 100644 --- a/x-pack/plugins/security_solution/public/cases/components/user_action_tree/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/user_action_tree/index.tsx @@ -30,7 +30,7 @@ import { AlertCommentRequestRt, CommentType, ContextTypeUserRt, -} from '../../../../../cases/common'; +} from '../../../../../cases/common/api'; import { CaseServices } from '../../containers/use_get_case_user_actions'; import { parseString } from '../../containers/utils'; import { OnUpdateFields } from '../case_view'; diff --git a/x-pack/plugins/security_solution/public/cases/components/user_action_tree/user_action_alert_comment_event.test.tsx b/x-pack/plugins/security_solution/public/cases/components/user_action_tree/user_action_alert_comment_event.test.tsx index 25080d61a951b..3bfdf2d2c5e62 100644 --- a/x-pack/plugins/security_solution/public/cases/components/user_action_tree/user_action_alert_comment_event.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/user_action_tree/user_action_alert_comment_event.test.tsx @@ -11,7 +11,7 @@ import { mount } from 'enzyme'; import { TestProviders } from '../../../common/mock'; import { useKibana } from '../../../common/lib/kibana'; import { AlertCommentEvent } from './user_action_alert_comment_event'; -import { CommentType } from '../../../../../cases/common'; +import { CommentType } from '../../../../../cases/common/api'; const props = { alertId: 'alert-id-1', diff --git a/x-pack/plugins/security_solution/public/cases/components/user_action_tree/user_action_alert_comment_event.tsx b/x-pack/plugins/security_solution/public/cases/components/user_action_tree/user_action_alert_comment_event.tsx index a1b6587cfeecb..a72bebbaf0999 100644 --- a/x-pack/plugins/security_solution/public/cases/components/user_action_tree/user_action_alert_comment_event.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/user_action_tree/user_action_alert_comment_event.tsx @@ -15,7 +15,7 @@ import { getRuleDetailsUrl, useFormatUrl } from '../../../common/components/link import { SecurityPageName } from '../../../app/types'; import * as i18n from './translations'; -import { CommentType } from '../../../../../cases/common'; +import { CommentType } from '../../../../../cases/common/api'; import { LinkAnchor } from '../../../common/components/links'; interface Props { diff --git a/x-pack/plugins/security_solution/public/cases/containers/api.ts b/x-pack/plugins/security_solution/public/cases/containers/api.ts index ca7ab5eb9d7dd..644c7dbf716bf 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/api.ts +++ b/x-pack/plugins/security_solution/public/cases/containers/api.ts @@ -8,14 +8,9 @@ import { assign, omit } from 'lodash'; import { - ACTION_TYPES_URL, - CASE_REPORTERS_URL, - CASE_STATUS_URL, - CASE_TAGS_URL, CasePatchRequest, CasePostRequest, CaseResponse, - CASES_URL, CasesFindResponse, CasesResponse, CasesStatusResponse, @@ -23,19 +18,30 @@ import { CaseUserActionsResponse, CommentRequest, CommentType, + SubCasePatchRequest, + SubCaseResponse, + SubCasesResponse, + User, +} from '../../../../cases/common/api'; + +import { + ACTION_TYPES_URL, + CASE_REPORTERS_URL, + CASE_STATUS_URL, + CASE_TAGS_URL, + CASES_URL, + SUB_CASE_DETAILS_URL, + SUB_CASES_PATCH_DEL_URL, +} from '../../../../cases/common/constants'; + +import { getCaseCommentsUrl, - getCaseDetailsUrl, getCasePushUrl, + getCaseDetailsUrl, getCaseUserActionUrl, getSubCaseDetailsUrl, getSubCaseUserActionUrl, - SUB_CASE_DETAILS_URL, - SUB_CASES_PATCH_DEL_URL, - SubCasePatchRequest, - SubCaseResponse, - SubCasesResponse, - User, -} from '../../../../cases/common'; +} from '../../../../cases/common/api/helpers'; import { KibanaServices } from '../../common/lib/kibana'; import { StatusAll } from '../components/status'; diff --git a/x-pack/plugins/security_solution/public/cases/containers/configure/api.ts b/x-pack/plugins/security_solution/public/cases/containers/configure/api.ts index c165c493c16d9..943724ef08398 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/configure/api.ts +++ b/x-pack/plugins/security_solution/public/cases/containers/configure/api.ts @@ -7,16 +7,20 @@ import { isEmpty } from 'lodash/fp'; import { - ACTION_TYPES_URL, ActionConnector, ActionTypeConnector, - CASE_CONFIGURE_CONNECTORS_URL, - CASE_CONFIGURE_URL, CasesConfigurePatch, - CasesConfigureRequest, CasesConfigureResponse, -} from '../../../../../cases/common'; + CasesConfigureRequest, +} from '../../../../../cases/common/api'; import { KibanaServices } from '../../../common/lib/kibana'; + +import { + CASE_CONFIGURE_CONNECTORS_URL, + CASE_CONFIGURE_URL, + ACTION_TYPES_URL, +} from '../../../../../cases/common/constants'; + import { ApiProps } from '../types'; import { convertToCamelCase, decodeCaseConfigureResponse } from '../utils'; import { CaseConfigure } from './types'; diff --git a/x-pack/plugins/security_solution/public/cases/containers/configure/use_configure.tsx b/x-pack/plugins/security_solution/public/cases/containers/configure/use_configure.tsx index 2087753b26039..2ec2a73363bfe 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/configure/use_configure.tsx +++ b/x-pack/plugins/security_solution/public/cases/containers/configure/use_configure.tsx @@ -15,7 +15,7 @@ import { } from '../../../common/components/toasters'; import * as i18n from './translations'; import { ClosureType, CaseConfigure, CaseConnector, CaseConnectorMapping } from './types'; -import { ConnectorTypes } from '../../../../../cases/common'; +import { ConnectorTypes } from '../../../../../cases/common/api/connectors'; export type ConnectorConfiguration = { connector: CaseConnector } & { closureType: CaseConfigure['closureType']; diff --git a/x-pack/plugins/security_solution/public/cases/containers/types.ts b/x-pack/plugins/security_solution/public/cases/containers/types.ts index d1c17ea56df65..6feb5a1501a76 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/types.ts +++ b/x-pack/plugins/security_solution/public/cases/containers/types.ts @@ -6,20 +6,20 @@ */ import { - AssociationType, - CaseAttributes, + User, + UserActionField, + UserAction, CaseConnector, - CasePatchRequest, + CommentRequest, CaseStatuses, + CaseAttributes, + CasePatchRequest, CaseType, - CommentRequest, - User, - UserAction, - UserActionField, -} from '../../../../cases/common'; + AssociationType, +} from '../../../../cases/common/api'; import { CaseStatusWithAllStatus } from '../components/status'; -export { CaseConnector, ActionConnector, CaseStatuses } from '../../../../cases/common'; +export { CaseConnector, ActionConnector, CaseStatuses } from '../../../../cases/common/api'; export type Comment = CommentRequest & { associationType: AssociationType; diff --git a/x-pack/plugins/security_solution/public/cases/containers/use_bulk_update_case.tsx b/x-pack/plugins/security_solution/public/cases/containers/use_bulk_update_case.tsx index ffb964982d302..d39da93a06a48 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/use_bulk_update_case.tsx +++ b/x-pack/plugins/security_solution/public/cases/containers/use_bulk_update_case.tsx @@ -6,7 +6,7 @@ */ import { useCallback, useReducer, useRef, useEffect } from 'react'; -import { CaseStatuses } from '../../../../cases/common'; +import { CaseStatuses } from '../../../../cases/common/api'; import { displaySuccessToast, errorToToaster, diff --git a/x-pack/plugins/security_solution/public/cases/containers/utils.ts b/x-pack/plugins/security_solution/public/cases/containers/utils.ts index e447476d02282..7c33e4481b2aa 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/utils.ts +++ b/x-pack/plugins/security_solution/public/cases/containers/utils.ts @@ -13,22 +13,22 @@ import { identity } from 'fp-ts/lib/function'; import { pipe } from 'fp-ts/lib/pipeable'; import { - CaseConfigureResponseRt, - CasePatchRequest, - CaseResponse, - CaseResponseRt, - CasesConfigureResponse, CasesFindResponse, CasesFindResponseRt, + CaseResponse, + CaseResponseRt, CasesResponse, CasesResponseRt, - CasesStatusResponse, CasesStatusResponseRt, + CasesStatusResponse, + throwErrors, + CasesConfigureResponse, + CaseConfigureResponseRt, CaseUserActionsResponse, CaseUserActionsResponseRt, CommentType, - throwErrors, -} from '../../../../cases/common'; + CasePatchRequest, +} from '../../../../cases/common/api'; import { AppToast, ToasterError } from '../../common/components/toasters'; import { AllCases, Case, UpdateByKey } from './types'; import * as i18n from './translations'; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/use_manage_case_action.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/use_manage_case_action.tsx index c19e5c26bdc94..875bc5e647077 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/use_manage_case_action.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/use_manage_case_action.tsx @@ -6,7 +6,7 @@ */ import { useEffect, useRef, useState } from 'react'; -import { ACTION_URL } from '../../../../../../cases/common'; +import { ACTION_URL } from '../../../../../../cases/common/constants'; import { KibanaServices } from '../../../../common/lib/kibana'; interface CaseAction { diff --git a/x-pack/plugins/security_solution/public/index.ts b/x-pack/plugins/security_solution/public/index.ts index 55262fe039b4e..f1d1bc3e6280b 100644 --- a/x-pack/plugins/security_solution/public/index.ts +++ b/x-pack/plugins/security_solution/public/index.ts @@ -7,8 +7,8 @@ import { PluginInitializerContext } from '../../../../src/core/public'; import { Plugin } from './plugin'; -import { PluginSetup } from './types'; +import { PluginSetup, PluginStart } from './types'; export const plugin = (context: PluginInitializerContext): Plugin => new Plugin(context); -export { Plugin, PluginSetup }; +export { Plugin, PluginSetup, PluginStart }; diff --git a/x-pack/plugins/security_solution/public/timelines/containers/api.ts b/x-pack/plugins/security_solution/public/timelines/containers/api.ts index 4443688fd249d..01a85f6309c3f 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/api.ts +++ b/x-pack/plugins/security_solution/public/timelines/containers/api.ts @@ -12,7 +12,7 @@ import { pipe } from 'fp-ts/lib/pipeable'; // eslint-disable-next-line no-restricted-imports import isEmpty from 'lodash/isEmpty'; -import { throwErrors } from '../../../../cases/common'; +import { throwErrors } from '../../../../cases/common/api'; import { TimelineResponse, TimelineResponseType, diff --git a/x-pack/plugins/security_solution/public/types.ts b/x-pack/plugins/security_solution/public/types.ts index e3d2c345a2a66..e88077679e1b6 100644 --- a/x-pack/plugins/security_solution/public/types.ts +++ b/x-pack/plugins/security_solution/public/types.ts @@ -22,7 +22,6 @@ import { TriggersAndActionsUIPublicPluginSetup as TriggersActionsSetup, TriggersAndActionsUIPublicPluginStart as TriggersActionsStart, } from '../../triggers_actions_ui/public'; -import { CasesUiStart } from '../../cases/public'; import { SecurityPluginSetup } from '../../security/public'; import { ResolverPluginSetup } from './resolver/types'; import { Inspect } from '../common/search_strategy'; @@ -48,7 +47,6 @@ export interface SetupPlugins { } export interface StartPlugins { - cases: CasesUiStart; data: DataPublicPluginStart; embeddable: EmbeddableStart; inspector: InspectorStart; From 5f92987bb392aeeab2aab963d2058550c0ab1f7a Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet <pierre.gayvallet@gmail.com> Date: Fri, 19 Mar 2021 08:14:55 +0100 Subject: [PATCH 02/13] add banners settings doc (#94672) * add banners settings doc * address review comments * add link to the correct file * use singular for banner --- docs/settings/banners-settings.asciidoc | 35 +++++++++++++++++++++++++ docs/settings/settings-xkb.asciidoc | 1 + docs/setup/settings.asciidoc | 1 + 3 files changed, 37 insertions(+) create mode 100644 docs/settings/banners-settings.asciidoc diff --git a/docs/settings/banners-settings.asciidoc b/docs/settings/banners-settings.asciidoc new file mode 100644 index 0000000000000..2a68cbe82f9f2 --- /dev/null +++ b/docs/settings/banners-settings.asciidoc @@ -0,0 +1,35 @@ +[role="xpack"] +[[banners-settings-kb]] +=== Banner settings in {kib} +++++ +<titleabbrev>Banners settings</titleabbrev> +++++ + +Banners are disabled by default. You need to manually configure them in order to use the feature. + +You can configure the `xpack.banners` settings in your `kibana.yml` file. + +[[general-banners-settings-kb]] +==== General banner settings + +[cols="2*<"] +|=== + +| `xpack.banners.placement` +| Set to `header` to enable the header banner. Defaults to `disabled`. + +| `xpack.banners.textContent` +| The text to display inside the banner, either plain text or Markdown. + +| `xpack.banners.textColor` +| The color for the banner text. Defaults to `#8A6A0A`. + +| `xpack.banners.backgroundColor` +| The color of the banner background. Defaults to `#FFF9E8`. + +|=== + +[NOTE] +==== +The `banners` plugin is a https://www.elastic.co/subscriptions[subscription feature] +==== \ No newline at end of file diff --git a/docs/settings/settings-xkb.asciidoc b/docs/settings/settings-xkb.asciidoc index 4a211976be8cf..1bd38578750d7 100644 --- a/docs/settings/settings-xkb.asciidoc +++ b/docs/settings/settings-xkb.asciidoc @@ -12,6 +12,7 @@ For more {kib} configuration settings, see <<settings>>. include::alert-action-settings.asciidoc[] include::apm-settings.asciidoc[] +include::banners-settings.asciidoc[] include::dev-settings.asciidoc[] include::graph-settings.asciidoc[] include::infrastructure-ui-settings.asciidoc[] diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 7db88a8b7ad84..9934f8508707c 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -685,6 +685,7 @@ Valid locales are: `en`, `zh-CN`, `ja-JP`. *Default: `en`* include::{kib-repo-dir}/settings/alert-action-settings.asciidoc[] include::{kib-repo-dir}/settings/apm-settings.asciidoc[] +include::{kib-repo-dir}/settings/banners-settings.asciidoc[] include::{kib-repo-dir}/settings/dev-settings.asciidoc[] include::{kib-repo-dir}/settings/graph-settings.asciidoc[] include::{kib-repo-dir}/settings/fleet-settings.asciidoc[] From fc3125af08cfd1f18a41f2daba63d0b6d07dadee Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski <jon@budzenski.me> Date: Fri, 19 Mar 2021 12:11:34 -0500 Subject: [PATCH 03/13] skip "doc table should show popover" #94889 --- test/functional/apps/discover/_data_grid_doc_table.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/functional/apps/discover/_data_grid_doc_table.ts b/test/functional/apps/discover/_data_grid_doc_table.ts index fb19111d92c68..85aa96555f20b 100644 --- a/test/functional/apps/discover/_data_grid_doc_table.ts +++ b/test/functional/apps/discover/_data_grid_doc_table.ts @@ -58,7 +58,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.timePicker.setDefaultAbsoluteRange(); }); - it('should show popover with expanded cell content by click on expand button', async () => { + // flaky https://github.com/elastic/kibana/issues/94889 + it.skip('should show popover with expanded cell content by click on expand button', async () => { log.debug('open popover with expanded cell content to get json from the editor'); const documentCell = await dataGrid.getCellElement(1, 3); await documentCell.click(); From 778bb69cba065ef2988cc875eb273d91c4483177 Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger <walter@elastic.co> Date: Fri, 19 Mar 2021 21:06:59 +0100 Subject: [PATCH 04/13] [ML] Functional Tests: Extend canvas assertion options. (#91473) Updates the canvas element assertion service and stabilizes tests. --- .../plugins/ml/public/application/_hacks.scss | 11 +- .../scatterplot_matrix/scatterplot_matrix.tsx | 2 +- .../configuration_step_form.tsx | 54 +++--- .../get_roc_curve_chart_vega_lite_spec.tsx | 1 + .../classification_creation.ts | 51 +++--- .../outlier_detection_creation.ts | 44 +++-- .../regression_creation.ts | 19 +- .../functional/services/canvas_element.ts | 165 ++++++++++++++---- .../test/functional/services/ml/common_ui.ts | 45 +++++ .../ml/data_frame_analytics_canvas_element.ts | 41 ----- .../ml/data_frame_analytics_creation.ts | 19 +- .../ml/data_frame_analytics_results.ts | 19 +- x-pack/test/functional/services/ml/index.ts | 10 +- .../functional/services/transform/wizard.ts | 12 +- 14 files changed, 321 insertions(+), 172 deletions(-) delete mode 100644 x-pack/test/functional/services/ml/data_frame_analytics_canvas_element.ts diff --git a/x-pack/plugins/ml/public/application/_hacks.scss b/x-pack/plugins/ml/public/application/_hacks.scss index 63fec4e74b796..13fabcb8045aa 100644 --- a/x-pack/plugins/ml/public/application/_hacks.scss +++ b/x-pack/plugins/ml/public/application/_hacks.scss @@ -1,6 +1,6 @@ .tab-datavisualizer_index_select, .tab-timeseriesexplorer, -.tab-explorer, { +.tab-explorer { // Make all page background white until More of the pages use EuiPage to wrap in panel-like components background-color: $euiColorEmptyShade; } @@ -22,3 +22,12 @@ .clear, .clearfix { clear: both; } + +// Helper class for functional tests to disable anti-aliasing for canvas elements +.mlDisableAntiAliasing { + -webkit-font-smoothing : none; + + * canvas { + image-rendering: pixelated; + } +} diff --git a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx index 344464bfe9590..4e9fd3baebe7b 100644 --- a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx +++ b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx @@ -267,7 +267,7 @@ export const ScatterplotMatrix: FC<ScatterplotMatrixProps> = ({ {splom === undefined || vegaSpec === undefined ? ( <VegaChartLoading /> ) : ( - <div data-test-subj="mlScatterplotMatrix"> + <div data-test-subj={`mlScatterplotMatrix ${isLoading ? 'loading' : 'loaded'}`}> <EuiFlexGroup> <EuiFlexItem> <EuiFormRow diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx index 206fb511a4d25..36d3de1376373 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx @@ -120,14 +120,6 @@ export const ConfigurationStepForm: FC<CreateAnalyticsStepProps> = ({ language: SEARCH_QUERY_LANGUAGE.KUERY, }); - const scatterplotFieldOptions = useMemo( - () => - includesTableItems - .filter((d) => d.feature_type === 'numerical' && d.is_included) - .map((d) => d.name), - [includesTableItems] - ); - const toastNotifications = getToastNotifications(); const setJobConfigQuery: ExplorationQueryBarProps['setSearchQuery'] = (update) => { @@ -341,16 +333,37 @@ export const ConfigurationStepForm: FC<CreateAnalyticsStepProps> = ({ [currentIndexPattern.fields] ); + const scatterplotMatrixProps = useMemo( + () => ({ + color: isJobTypeWithDepVar ? dependentVariable : undefined, + fields: includesTableItems + .filter((d) => d.feature_type === 'numerical' && d.is_included) + .map((d) => d.name), + index: currentIndexPattern.title, + legendType: getScatterplotMatrixLegendType(jobType), + searchQuery: jobConfigQuery, + }), + [ + currentIndexPattern.title, + dependentVariable, + includesTableItems, + isJobTypeWithDepVar, + jobConfigQuery, + jobType, + ] + ); + // Show the Scatterplot Matrix only if // - There's more than one suitable field available // - The job type is outlier detection, or // - The job type is regression or classification and the dependent variable has been set - const showScatterplotMatrix = - (jobType === ANALYSIS_CONFIG_TYPE.OUTLIER_DETECTION || - ((jobType === ANALYSIS_CONFIG_TYPE.REGRESSION || - jobType === ANALYSIS_CONFIG_TYPE.CLASSIFICATION) && - !dependentVariableEmpty)) && - scatterplotFieldOptions.length > 1; + const showScatterplotMatrix = useMemo( + () => + (jobType === ANALYSIS_CONFIG_TYPE.OUTLIER_DETECTION || + (isJobTypeWithDepVar && !dependentVariableEmpty)) && + scatterplotMatrixProps.fields.length > 1, + [dependentVariableEmpty, jobType, scatterplotMatrixProps.fields.length] + ); // Don't render until `savedSearchQuery` has been initialized. // `undefined` means uninitialized, `null` means initialized but not used. @@ -550,18 +563,7 @@ export const ConfigurationStepForm: FC<CreateAnalyticsStepProps> = ({ paddingSize="m" data-test-subj="mlAnalyticsCreateJobWizardScatterplotMatrixPanel" > - <ScatterplotMatrix - fields={scatterplotFieldOptions} - index={currentIndexPattern.title} - color={ - jobType === ANALYSIS_CONFIG_TYPE.REGRESSION || - jobType === ANALYSIS_CONFIG_TYPE.CLASSIFICATION - ? dependentVariable - : undefined - } - legendType={getScatterplotMatrixLegendType(jobType)} - searchQuery={jobConfigQuery} - /> + <ScatterplotMatrix {...scatterplotMatrixProps} /> </EuiPanel> <EuiSpacer /> </> diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/get_roc_curve_chart_vega_lite_spec.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/get_roc_curve_chart_vega_lite_spec.tsx index b9e9c5720e5aa..7e279308c6879 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/get_roc_curve_chart_vega_lite_spec.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/get_roc_curve_chart_vega_lite_spec.tsx @@ -53,6 +53,7 @@ export const getRocCurveChartVegaLiteSpec = ( return { $schema: 'https://vega.github.io/schema/vega-lite/v4.8.1.json', + background: 'transparent', // Left padding of 45px to align the left axis of the chart with the confusion matrix above. padding: { left: 45, top: 0, right: 0, bottom: 0 }, config: { diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts index 50aa3d1498997..90705a73e3839 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts @@ -12,8 +12,7 @@ export default function ({ getService }: FtrProviderContext) { const ml = getService('ml'); const editedDescription = 'Edited description'; - // Failing: See https://github.com/elastic/kibana/issues/91450 - describe.skip('classification creation', function () { + describe('classification creation', function () { before(async () => { await esArchiver.loadIfNeeded('ml/bm_classification'); await ml.testResources.createIndexPatternIfNeeded('ft_bank_marketing', '@timestamp'); @@ -43,24 +42,21 @@ export default function ({ getService }: FtrProviderContext) { createIndexPattern: true, expected: { rocCurveColorState: [ - // background - { key: '#FFFFFF', value: 93 }, // tick/grid/axis - { key: '#98A2B3', value: 1 }, - { key: '#DDDDDD', value: 3 }, - // line - { key: '#6092C0', value: 1 }, + { key: '#DDDDDD', value: 50 }, + // lines + { key: '#98A2B3', value: 30 }, + { key: '#6092C0', value: 10 }, + { key: '#5F92C0', value: 6 }, ], scatterplotMatrixColorStats: [ - // background - { key: '#000000', value: 94 }, + // marker colors + { key: '#7FC6B3', value: 1 }, + { key: '#88ADD0', value: 0.03 }, // tick/grid/axis - { key: '#DDDDDD', value: 1 }, - { key: '#D3DAE6', value: 1 }, - { key: '#F5F7FA', value: 1 }, - // scatterplot circles - { key: '#6A717D', value: 1 }, - { key: '#54B39A', value: 1 }, + { key: '#DDDDDD', value: 8 }, + { key: '#D3DAE6', value: 8 }, + { key: '#F5F7FA', value: 20 }, ], row: { type: 'classification', @@ -112,8 +108,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.dataFrameAnalyticsCreation.assertIncludeFieldsSelectionExists(); await ml.testExecution.logTestStep('displays the scatterplot matrix'); - await ml.dataFrameAnalyticsCanvasElement.assertCanvasElement( - 'mlAnalyticsCreateJobWizardScatterplotMatrixFormRow', + await ml.dataFrameAnalyticsCreation.assertScatterplotMatrix( testData.expected.scatterplotMatrixColorStats ); @@ -157,7 +152,12 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep('checks validation callouts exist'); await ml.dataFrameAnalyticsCreation.assertValidationCalloutsExists(); - await ml.dataFrameAnalyticsCreation.assertAllValidationCalloutsPresent(3); + // Expect the follow callouts: + // - ✓ Dependent variable + // - ✓ Training percent + // - ✓ Top classes + // - ⚠ Analysis fields + await ml.dataFrameAnalyticsCreation.assertAllValidationCalloutsPresent(4); await ml.testExecution.logTestStep('continues to the create step'); await ml.dataFrameAnalyticsCreation.continueToCreateStep(); @@ -238,16 +238,21 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep('displays the results view for created job'); await ml.dataFrameAnalyticsTable.openResultsView(testData.jobId); await ml.dataFrameAnalyticsResults.assertClassificationEvaluatePanelElementsExists(); - await ml.dataFrameAnalyticsCanvasElement.assertCanvasElement( + await ml.commonUI.assertColorsInCanvasElement( 'mlDFAnalyticsClassificationExplorationRocCurveChart', - testData.expected.rocCurveColorState + testData.expected.rocCurveColorState, + ['#000000'], + undefined, + undefined, + // increased tolerance for ROC curve chart up from 10 to 20 + // since the returned colors vary quite a bit on each run. + 20 ); await ml.dataFrameAnalyticsResults.assertClassificationTablePanelExists(); await ml.dataFrameAnalyticsResults.assertResultsTableExists(); await ml.dataFrameAnalyticsResults.assertResultsTableTrainingFiltersExist(); await ml.dataFrameAnalyticsResults.assertResultsTableNotEmpty(); - await ml.dataFrameAnalyticsCanvasElement.assertCanvasElement( - 'mlDFExpandableSection-splom', + await ml.dataFrameAnalyticsResults.assertScatterplotMatrix( testData.expected.scatterplotMatrixColorStats ); }); diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts index 0a00bb3cd757f..d8b52130d307c 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts @@ -12,8 +12,7 @@ export default function ({ getService }: FtrProviderContext) { const ml = getService('ml'); const editedDescription = 'Edited description'; - // FAILING: https://github.com/elastic/kibana/issues/94854 - describe.skip('outlier detection creation', function () { + describe('outlier detection creation', function () { before(async () => { await esArchiver.loadIfNeeded('ml/ihp_outlier'); await ml.testResources.createIndexPatternIfNeeded('ft_ihp_outlier', '@timestamp'); @@ -51,26 +50,21 @@ export default function ({ getService }: FtrProviderContext) { { chartAvailable: true, id: 'Exterior2nd', legend: '3 categories' }, { chartAvailable: true, id: 'Fireplaces', legend: '0 - 3' }, ], - scatterplotMatrixColorStatsWizard: [ - // background - { key: '#000000', value: 91 }, - // tick/grid/axis - { key: '#6A717D', value: 2 }, - { key: '#F5F7FA', value: 2 }, - { key: '#D3DAE6', value: 1 }, - // scatterplot circles - { key: '#69707D', value: 1 }, - { key: '#98A1B3', value: 1 }, + scatterplotMatrixColorsWizard: [ + // markers + { key: '#52B398', value: 25 }, + // grey boilerplate + { key: '#6A717D', value: 30 }, ], scatterplotMatrixColorStatsResults: [ - // background - { key: '#000000', value: 91 }, + // red markers + { key: '#D98071', value: 1 }, // tick/grid/axis, grey markers - // the red outlier color is not above the 1% threshold. - { key: '#6A717D', value: 2 }, - { key: '#98A2B3', value: 1 }, - { key: '#F5F7FA', value: 2 }, - { key: '#D3DAE6', value: 1 }, + { key: '#6A717D', value: 30 }, + { key: '#D3DAE6', value: 8 }, + { key: '#98A1B3', value: 25 }, + // anti-aliasing + { key: '#F5F7FA', value: 27 }, ], row: { type: 'outlier_detection', @@ -94,6 +88,10 @@ export default function ({ getService }: FtrProviderContext) { await ml.navigation.navigateToDataFrameAnalytics(); await ml.testExecution.logTestStep('loads the source selection modal'); + + // Disable anti-aliasing to stabilize canvas image rendering assertions + await ml.commonUI.disableAntiAliasing(); + await ml.dataFrameAnalytics.startAnalyticsCreation(); await ml.testExecution.logTestStep( @@ -129,9 +127,8 @@ export default function ({ getService }: FtrProviderContext) { await ml.dataFrameAnalyticsCreation.assertIncludeFieldsSelectionExists(); await ml.testExecution.logTestStep('displays the scatterplot matrix'); - await ml.dataFrameAnalyticsCanvasElement.assertCanvasElement( - 'mlAnalyticsCreateJobWizardScatterplotMatrixFormRow', - testData.expected.scatterplotMatrixColorStatsWizard + await ml.dataFrameAnalyticsCreation.assertScatterplotMatrix( + testData.expected.scatterplotMatrixColorsWizard ); await ml.testExecution.logTestStep('continues to the additional options step'); @@ -258,8 +255,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.dataFrameAnalyticsResults.assertResultsTableExists(); await ml.dataFrameAnalyticsResults.assertResultsTableNotEmpty(); await ml.dataFrameAnalyticsResults.assertFeatureInfluenceCellNotEmpty(); - await ml.dataFrameAnalyticsCanvasElement.assertCanvasElement( - 'mlDFExpandableSection-splom', + await ml.dataFrameAnalyticsResults.assertScatterplotMatrix( testData.expected.scatterplotMatrixColorStatsResults ); }); diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts index c88867aa95de1..43dd5ad78a072 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts @@ -41,14 +41,13 @@ export default function ({ getService }: FtrProviderContext) { createIndexPattern: true, expected: { scatterplotMatrixColorStats: [ - // background - { key: '#000000', value: 80 }, + // some marker colors of the continuous color scale + { key: '#61AFA3', value: 2 }, + { key: '#D1E5E0', value: 2 }, // tick/grid/axis - { key: '#6A717D', value: 1 }, - { key: '#F5F7FA', value: 2 }, - { key: '#D3DAE6', value: 1 }, - // because a continuous color scale is used for the scatterplot circles, - // none of the generated colors is above the 1% threshold. + { key: '#6A717D', value: 10 }, + { key: '#F5F7FA', value: 12 }, + { key: '#D3DAE6', value: 3 }, ], row: { type: 'regression', @@ -101,8 +100,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.dataFrameAnalyticsCreation.assertIncludeFieldsSelectionExists(); await ml.testExecution.logTestStep('displays the scatterplot matrix'); - await ml.dataFrameAnalyticsCanvasElement.assertCanvasElement( - 'mlAnalyticsCreateJobWizardScatterplotMatrixFormRow', + await ml.dataFrameAnalyticsCreation.assertScatterplotMatrix( testData.expected.scatterplotMatrixColorStats ); @@ -231,8 +229,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.dataFrameAnalyticsResults.assertResultsTableExists(); await ml.dataFrameAnalyticsResults.assertResultsTableTrainingFiltersExist(); await ml.dataFrameAnalyticsResults.assertResultsTableNotEmpty(); - await ml.dataFrameAnalyticsCanvasElement.assertCanvasElement( - 'mlDFExpandableSection-splom', + await ml.dataFrameAnalyticsResults.assertScatterplotMatrix( testData.expected.scatterplotMatrixColorStats ); }); diff --git a/x-pack/test/functional/services/canvas_element.ts b/x-pack/test/functional/services/canvas_element.ts index 300874d1e1a21..b6d1239b34169 100644 --- a/x-pack/test/functional/services/canvas_element.ts +++ b/x-pack/test/functional/services/canvas_element.ts @@ -15,26 +15,34 @@ interface ColorStat { type ColorStats = ColorStat[]; -/** - * Returns if a given value is within the tolerated range of an expected value - * - * @param actualValue - * @param expectedValue - * @param toleranceRange - * @returns if actualValue is within the tolerance of expectedValue - */ -function isValueWithinTolerance(actualValue: number, expectedValue: number, toleranceRange = 10) { - const lower = expectedValue - toleranceRange / 2; - const upper = expectedValue + toleranceRange / 2; - return lower <= actualValue && upper >= actualValue; -} - import { FtrProviderContext } from '../ftr_provider_context'; export async function CanvasElementProvider({ getService }: FtrProviderContext) { const { driver } = await getService('__webdriver__').init(); return new (class CanvasElementService { + // disable font anti-aliasing to be more resilient + // against OS rendering differences + public async disableAntiAliasing() { + await driver.executeScript( + ` + document.body.style["font-smooth"] = "never"; + document.body.style["-webkit-font-smoothing"] = "none"; + document.body.classList.add("mlDisableAntiAliasing"); + ` + ); + } + + public async resetAntiAliasing() { + await driver.executeScript( + ` + document.body.style["font-smooth"] = ""; + document.body.style["-webkit-font-smoothing"] = ""; + document.body.classList.remove("mlDisableAntiAliasing"); + ` + ); + } + /** * Gets the image data of a canvas element * @param selector querySelector to access the canvas element. @@ -60,36 +68,33 @@ export async function CanvasElementProvider({ getService }: FtrProviderContext) * * @param selector querySelector to access the canvas element. * @param expectedColorStats - optional stats to compare against and check if the percentage is within the tolerance. - * @param threshold - colors below this percentage threshold will be filtered from the returned list of colors + * @param percentageThreshold - colors below this percentage threshold will be filtered from the returned list of colors + * @param channelTolerance - tolerance for each RGB channel value + * @param exclude - colors to exclude, useful for e.g. known background color values * @returns an array of colors and their percentage of appearance in the given image data */ public async getColorStats( selector: string, expectedColorStats?: ColorStats, - threshold = 5 + exclude?: string[], + percentageThreshold = 5, + channelTolerance = 10, + valueTolerance = 10 ): Promise<ColorStats> { const imageData = await this.getImageData(selector); // transform the array of RGBA numbers to an array of hex values const colors: string[] = []; for (let i = 0; i < imageData.length; i += 4) { // uses d3's `rgb` method create a color object, `toString()` returns the hex value - colors.push( - rgb(imageData[i], imageData[i + 1], imageData[i + 2]) - .toString() - .toUpperCase() - ); + const r = imageData[i]; + const g = imageData[i + 1]; + const b = imageData[i + 2]; + const color = rgb(r, g, b).toString().toUpperCase(); + if (exclude === undefined || !exclude.includes(color)) colors.push(color); } - const expectedColorStatsMap = - expectedColorStats !== undefined - ? expectedColorStats.reduce((p, c) => { - p[c.key] = c.value; - return p; - }, {} as Record<string, number>) - : {}; - function getPixelPercentage(pixelsNum: number): number { - return Math.round((pixelsNum / colors.length) * 100); + return (pixelsNum / colors.length) * 100; } // - d3's nest/key/entries methods will group the array of hex values so we can count @@ -101,17 +106,111 @@ export async function CanvasElementProvider({ getService }: FtrProviderContext) return nest<string>() .key((d) => d) .entries(colors) - .filter((s) => getPixelPercentage(s.values.length) >= threshold) - .map((s) => { + .filter((s) => getPixelPercentage(s.values.length) >= percentageThreshold) + .sort((a, b) => a.key.localeCompare(b.key)) + .map((s, i) => { const value = getPixelPercentage(s.values.length); return { key: s.key, value, ...(expectedColorStats !== undefined - ? { withinTolerance: isValueWithinTolerance(value, expectedColorStatsMap[s.key]) } + ? { + withinTolerance: + this.isValueWithinTolerance( + value, + expectedColorStats[i]?.value, + valueTolerance + ) && + this.isColorWithinTolerance( + s.key, + expectedColorStats[i]?.key, + channelTolerance + ), + } : {}), }; }); } + + /** + * Same as getColorStats() but also checks if each supplied + * expected color lies within channelTolerance. + */ + public async getColorStatsWithColorTolerance( + selector: string, + expectedColorStats: ColorStats, + exclude?: string[], + percentageThreshold = 0, + channelTolerance = 10, + valueTolerance = 10 + ) { + const actualColorStats = await this.getColorStats( + selector, + undefined, + exclude, + percentageThreshold, + channelTolerance, + valueTolerance + ); + + return expectedColorStats.map((expectedColor) => { + const colorPercentageWithinTolerance = actualColorStats + .filter((d) => this.isColorWithinTolerance(d.key, expectedColor.key, channelTolerance)) + .reduce((sum, x) => sum + x.value, 0); + + return { + key: expectedColor.key, + value: colorPercentageWithinTolerance, + withinTolerance: this.isValueWithinTolerance( + colorPercentageWithinTolerance, + expectedColor.value, + valueTolerance + ), + }; + }); + } + + /** + * Returns if a given color is within the tolerated range of an expected color + * + * @param actualColor + * @param expectedColor + * @param toleranceRange + * @returns if actualColor is within the tolerance of expectedColor + */ + public isColorWithinTolerance(actualColor: string, expectedColor: string, toleranceRange = 10) { + const actualRGB = rgb(actualColor); + const expectedRGB = rgb(expectedColor); + + const lowerR = expectedRGB.r - toleranceRange / 2; + const upperR = expectedRGB.r + toleranceRange / 2; + const lowerG = expectedRGB.g - toleranceRange / 2; + const upperG = expectedRGB.g + toleranceRange / 2; + const lowerB = expectedRGB.b - toleranceRange / 2; + const upperB = expectedRGB.b + toleranceRange / 2; + + return ( + lowerR <= actualRGB.r && + upperR >= actualRGB.r && + lowerG <= actualRGB.g && + upperG >= actualRGB.g && + lowerB <= actualRGB.b && + upperB >= actualRGB.b + ); + } + + /** + * Returns if a given value is within the tolerated range of an expected value + * + * @param actualValue + * @param expectedValue + * @param toleranceRange + * @returns if actualValue is within the tolerance of expectedValue + */ + public isValueWithinTolerance(actualValue: number, expectedValue: number, toleranceRange = 10) { + const lower = expectedValue - toleranceRange / 2; + const upper = expectedValue + toleranceRange / 2; + return actualValue > 0 && lower <= actualValue && upper >= actualValue; + } })(); } diff --git a/x-pack/test/functional/services/ml/common_ui.ts b/x-pack/test/functional/services/ml/common_ui.ts index 70e3d7c1b9b15..e7aa7ad5d3c89 100644 --- a/x-pack/test/functional/services/ml/common_ui.ts +++ b/x-pack/test/functional/services/ml/common_ui.ts @@ -15,9 +15,17 @@ interface SetValueOptions { typeCharByChar?: boolean; } +// key: color hex code, e.g. #FF3344 +// value: the expected percentage of the color to be present in the canvas element +export type CanvasElementColorStats = Array<{ + key: string; + value: number; +}>; + export type MlCommonUI = ProvidedType<typeof MachineLearningCommonUIProvider>; export function MachineLearningCommonUIProvider({ getService }: FtrProviderContext) { + const canvasElement = getService('canvasElement'); const log = getService('log'); const retry = getService('retry'); const testSubjects = getService('testSubjects'); @@ -205,5 +213,42 @@ export function MachineLearningCommonUIProvider({ getService }: FtrProviderConte `${testDataSubj} slider value should be '${expectedValue}' (got '${actualValue}')` ); }, + + async disableAntiAliasing() { + await canvasElement.disableAntiAliasing(); + }, + + async resetAntiAliasing() { + await canvasElement.resetAntiAliasing(); + }, + + async assertColorsInCanvasElement( + dataTestSubj: string, + expectedColorStats: CanvasElementColorStats, + exclude?: string[], + percentageThreshold = 0, + channelTolerance = 10, + valueTolerance = 10 + ) { + await retry.tryForTime(30 * 1000, async () => { + await testSubjects.existOrFail(dataTestSubj); + + const actualColorStatsWithTolerance = await canvasElement.getColorStatsWithColorTolerance( + `[data-test-subj="${dataTestSubj}"] canvas`, + expectedColorStats, + exclude, + percentageThreshold, + channelTolerance, + valueTolerance + ); + + expect(actualColorStatsWithTolerance.every((d) => d.withinTolerance)).to.eql( + true, + `Color stats for '${dataTestSubj}' should be within tolerance. Expected: '${JSON.stringify( + expectedColorStats + )}' (got '${JSON.stringify(actualColorStatsWithTolerance)}')` + ); + }); + }, }; } diff --git a/x-pack/test/functional/services/ml/data_frame_analytics_canvas_element.ts b/x-pack/test/functional/services/ml/data_frame_analytics_canvas_element.ts deleted file mode 100644 index a354e0723d377..0000000000000 --- a/x-pack/test/functional/services/ml/data_frame_analytics_canvas_element.ts +++ /dev/null @@ -1,41 +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 expect from '@kbn/expect'; - -import { FtrProviderContext } from '../../ftr_provider_context'; - -export function MachineLearningDataFrameAnalyticsCanvasElementProvider({ - getService, -}: FtrProviderContext) { - const canvasElement = getService('canvasElement'); - const testSubjects = getService('testSubjects'); - - return new (class AnalyticsCanvasElement { - public async assertCanvasElement( - dataTestSubj: string, - expectedColorStats: Array<{ - key: string; - value: number; - }> - ) { - await testSubjects.existOrFail(dataTestSubj); - - const actualColorStats = await canvasElement.getColorStats( - `[data-test-subj="${dataTestSubj}"] canvas`, - expectedColorStats, - 1 - ); - expect(actualColorStats.every((d) => d.withinTolerance)).to.eql( - true, - `Color stats for canvas element should be within tolerance. Expected: '${JSON.stringify( - expectedColorStats - )}' (got '${JSON.stringify(actualColorStats)}')` - ); - } - })(); -} diff --git a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts index 89a19016c9abd..d2c1291e69af8 100644 --- a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts +++ b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts @@ -9,7 +9,7 @@ import expect from '@kbn/expect'; import { DataFrameAnalyticsConfig } from '../../../../plugins/ml/public/application/data_frame_analytics/common'; import { FtrProviderContext } from '../../ftr_provider_context'; -import { MlCommonUI } from './common_ui'; +import type { CanvasElementColorStats, MlCommonUI } from './common_ui'; import { MlApi } from './api'; import { isRegressionAnalysis, @@ -256,6 +256,23 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( await this.assertDependentVariableSelection([dependentVariable]); }, + async assertScatterplotMatrix(expectedValue: CanvasElementColorStats) { + await testSubjects.existOrFail( + 'mlAnalyticsCreateJobWizardScatterplotMatrixPanel > mlScatterplotMatrix loaded', + { + timeout: 5000, + } + ); + await testSubjects.scrollIntoView( + 'mlAnalyticsCreateJobWizardScatterplotMatrixPanel > mlScatterplotMatrix loaded' + ); + await mlCommonUI.assertColorsInCanvasElement( + 'mlAnalyticsCreateJobWizardScatterplotMatrixPanel', + expectedValue, + ['#000000'] + ); + }, + async assertTrainingPercentInputExists() { await testSubjects.existOrFail('mlAnalyticsCreateJobWizardTrainingPercentSlider'); }, diff --git a/x-pack/test/functional/services/ml/data_frame_analytics_results.ts b/x-pack/test/functional/services/ml/data_frame_analytics_results.ts index bf4e4778d1441..c3fb75799fe64 100644 --- a/x-pack/test/functional/services/ml/data_frame_analytics_results.ts +++ b/x-pack/test/functional/services/ml/data_frame_analytics_results.ts @@ -10,9 +10,12 @@ import { WebElementWrapper } from 'test/functional/services/lib/web_element_wrap import { FtrProviderContext } from '../../ftr_provider_context'; -export function MachineLearningDataFrameAnalyticsResultsProvider({ - getService, -}: FtrProviderContext) { +import type { CanvasElementColorStats, MlCommonUI } from './common_ui'; + +export function MachineLearningDataFrameAnalyticsResultsProvider( + { getService }: FtrProviderContext, + mlCommonUI: MlCommonUI +) { const retry = getService('retry'); const testSubjects = getService('testSubjects'); @@ -81,6 +84,16 @@ export function MachineLearningDataFrameAnalyticsResultsProvider({ }); }, + async assertScatterplotMatrix(expectedValue: CanvasElementColorStats) { + await testSubjects.existOrFail('mlDFExpandableSection-splom > mlScatterplotMatrix loaded', { + timeout: 5000, + }); + await testSubjects.scrollIntoView('mlDFExpandableSection-splom > mlScatterplotMatrix loaded'); + await mlCommonUI.assertColorsInCanvasElement('mlDFExpandableSection-splom', expectedValue, [ + '#000000', + ]); + }, + async assertFeatureImportanceDecisionPathChartElementsExists() { await testSubjects.existOrFail('mlDFADecisionPathChart', { timeout: 5000, diff --git a/x-pack/test/functional/services/ml/index.ts b/x-pack/test/functional/services/ml/index.ts index ceee1ba7dc1ac..894ba3d6ef07d 100644 --- a/x-pack/test/functional/services/ml/index.ts +++ b/x-pack/test/functional/services/ml/index.ts @@ -18,7 +18,6 @@ import { MachineLearningDataFrameAnalyticsProvider } from './data_frame_analytic import { MachineLearningDataFrameAnalyticsCreationProvider } from './data_frame_analytics_creation'; import { MachineLearningDataFrameAnalyticsEditProvider } from './data_frame_analytics_edit'; import { MachineLearningDataFrameAnalyticsResultsProvider } from './data_frame_analytics_results'; -import { MachineLearningDataFrameAnalyticsCanvasElementProvider } from './data_frame_analytics_canvas_element'; import { MachineLearningDataFrameAnalyticsMapProvider } from './data_frame_analytics_map'; import { MachineLearningDataFrameAnalyticsTableProvider } from './data_frame_analytics_table'; import { MachineLearningDataVisualizerProvider } from './data_visualizer'; @@ -63,12 +62,12 @@ export function MachineLearningProvider(context: FtrProviderContext) { api ); const dataFrameAnalyticsEdit = MachineLearningDataFrameAnalyticsEditProvider(context, commonUI); - const dataFrameAnalyticsResults = MachineLearningDataFrameAnalyticsResultsProvider(context); + const dataFrameAnalyticsResults = MachineLearningDataFrameAnalyticsResultsProvider( + context, + commonUI + ); const dataFrameAnalyticsMap = MachineLearningDataFrameAnalyticsMapProvider(context); const dataFrameAnalyticsTable = MachineLearningDataFrameAnalyticsTableProvider(context); - const dataFrameAnalyticsCanvasElement = MachineLearningDataFrameAnalyticsCanvasElementProvider( - context - ); const dataVisualizer = MachineLearningDataVisualizerProvider(context); const dataVisualizerTable = MachineLearningDataVisualizerTableProvider(context, commonUI); @@ -113,7 +112,6 @@ export function MachineLearningProvider(context: FtrProviderContext) { dataFrameAnalyticsResults, dataFrameAnalyticsMap, dataFrameAnalyticsTable, - dataFrameAnalyticsCanvasElement, dataVisualizer, dataVisualizerFileBased, dataVisualizerIndexBased, diff --git a/x-pack/test/functional/services/transform/wizard.ts b/x-pack/test/functional/services/transform/wizard.ts index 40cc8644625eb..d9ba127bc16ce 100644 --- a/x-pack/test/functional/services/transform/wizard.ts +++ b/x-pack/test/functional/services/transform/wizard.ts @@ -243,17 +243,25 @@ export function TransformWizardProvider({ getService, getPageObjects }: FtrProvi await testSubjects.existOrFail(`mlDataGridChart-${id}-histogram`); if (expected.colorStats !== undefined) { + const sortedExpectedColorStats = [...expected.colorStats].sort((a, b) => + a.key.localeCompare(b.key) + ); + const actualColorStats = await canvasElement.getColorStats( `[data-test-subj="mlDataGridChart-${id}-histogram"] .echCanvasRenderer`, - expected.colorStats + sortedExpectedColorStats ); + expect(actualColorStats.length).to.eql( + sortedExpectedColorStats.length, + `Expected and actual color stats for column '${expected.id}' should have the same amount of elements. Expected: ${sortedExpectedColorStats.length} (got ${actualColorStats.length})` + ); expect(actualColorStats.every((d) => d.withinTolerance)).to.eql( true, `Color stats for column '${ expected.id }' should be within tolerance. Expected: '${JSON.stringify( - expected.colorStats + sortedExpectedColorStats )}' (got '${JSON.stringify(actualColorStats)}')` ); } From 7e5dbdf5b9f3998ed704baee68fe9a894f275649 Mon Sep 17 00:00:00 2001 From: Tiago Costa <tiagoffcc@hotmail.com> Date: Fri, 19 Mar 2021 23:21:31 +0000 Subject: [PATCH 05/13] skip flaky suite (#84440) --- x-pack/test/functional/apps/grok_debugger/grok_debugger.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/grok_debugger/grok_debugger.js b/x-pack/test/functional/apps/grok_debugger/grok_debugger.js index 0cd00c55ea1bb..77753641916f6 100644 --- a/x-pack/test/functional/apps/grok_debugger/grok_debugger.js +++ b/x-pack/test/functional/apps/grok_debugger/grok_debugger.js @@ -11,7 +11,8 @@ export default function ({ getService, getPageObjects }) { const esArchiver = getService('esArchiver'); const PageObjects = getPageObjects(['grokDebugger']); - describe('grok debugger app', function () { + // FLAKY: https://github.com/elastic/kibana/issues/84440 + describe.skip('grok debugger app', function () { this.tags('includeFirefox'); before(async () => { await esArchiver.load('empty_kibana'); From dd7ea1d4b2aab7589c1e9ade276e44217fb3fde9 Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger <walter@elastic.co> Date: Sat, 20 Mar 2021 18:41:25 +0100 Subject: [PATCH 06/13] [ML] Data Frame Analytics: Fix ROC Curve Chart for binary classification. (#94791) - Updates the ROC Curve Chart to show only one line for binary classification. - Improves type specs for the evaluate panel's data. --- .../components/data_grid/common.ts | 2 +- .../application/components/data_grid/types.ts | 9 +++- .../column_data.tsx | 31 +++++++++---- .../evaluate_panel.tsx | 38 ++++++++++------ .../get_roc_curve_chart_vega_lite_spec.tsx | 6 ++- .../use_roc_curve.ts | 43 ++++++++++++++++--- .../public/app/hooks/use_pivot_data.ts | 10 +---- .../classification_creation.ts | 4 +- 8 files changed, 98 insertions(+), 45 deletions(-) diff --git a/x-pack/plugins/ml/public/application/components/data_grid/common.ts b/x-pack/plugins/ml/public/application/components/data_grid/common.ts index 4b3eda0b0d651..69750b0ab1aaa 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/common.ts +++ b/x-pack/plugins/ml/public/application/components/data_grid/common.ts @@ -338,7 +338,7 @@ export const useRenderCellValue = ( return null; } - let format: any; + let format: ReturnType<typeof mlFieldFormatService.getFieldFormatFromIndexPattern>; if (indexPattern !== undefined) { format = mlFieldFormatService.getFieldFormatFromIndexPattern(indexPattern, columnId, ''); diff --git a/x-pack/plugins/ml/public/application/components/data_grid/types.ts b/x-pack/plugins/ml/public/application/components/data_grid/types.ts index 2fb47a59284a3..649968f176e18 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/types.ts +++ b/x-pack/plugins/ml/public/application/components/data_grid/types.ts @@ -7,7 +7,12 @@ import { Dispatch, SetStateAction } from 'react'; -import { EuiDataGridPaginationProps, EuiDataGridSorting, EuiDataGridColumn } from '@elastic/eui'; +import { + EuiDataGridCellValueElementProps, + EuiDataGridPaginationProps, + EuiDataGridSorting, + EuiDataGridColumn, +} from '@elastic/eui'; import { Dictionary } from '../../../../common/types/common'; import { HitsTotalRelation } from '../../../../common/types/es_client'; @@ -42,7 +47,7 @@ export type RenderCellValue = ({ }: { rowIndex: number; columnId: string; - setCellProps: any; + setCellProps: EuiDataGridCellValueElementProps['setCellProps']; }) => any; export type EsSorting = Dictionary<{ diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/column_data.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/column_data.tsx index 97aa7632fcdf3..d824a28bd9135 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/column_data.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/column_data.tsx @@ -18,19 +18,31 @@ import { ConfusionMatrix } from '../../../../common/analytics'; const COL_INITIAL_WIDTH = 165; // in pixels -interface ColumnData { +export interface ConfusionMatrixColumn { + id: string; + display?: JSX.Element; + initialWidth?: number; +} + +export interface ConfusionMatrixColumnData { actual_class: string; actual_class_doc_count: number; - [key: string]: string | number; + other: number; + predicted_classes_count: Record<string, number>; } export const ACTUAL_CLASS_ID = 'actual_class'; export const OTHER_CLASS_ID = 'other'; export const MAX_COLUMNS = 6; -export function getColumnData(confusionMatrixData: ConfusionMatrix[]) { - const colData: Partial<ColumnData[]> = []; - const columns: Array<{ id: string; display?: any; initialWidth?: number }> = [ +export function getColumnData( + confusionMatrixData: ConfusionMatrix[] +): { + columns: ConfusionMatrixColumn[]; + columnData: ConfusionMatrixColumnData[]; +} { + const colData: ConfusionMatrixColumnData[] = []; + const columns: ConfusionMatrixColumn[] = [ { id: ACTUAL_CLASS_ID, display: <span />, @@ -40,17 +52,18 @@ export function getColumnData(confusionMatrixData: ConfusionMatrix[]) { let showOther = false; - confusionMatrixData.forEach((classData) => { + for (const classData of confusionMatrixData) { const otherCount = classData.other_predicted_class_doc_count; if (otherCount > 0) { showOther = true; } - const col: any = { + const col: ConfusionMatrixColumnData = { actual_class: classData.actual_class, actual_class_doc_count: classData.actual_class_doc_count, other: otherCount, + predicted_classes_count: {}, }; const predictedClasses = classData.predicted_classes || []; @@ -60,11 +73,11 @@ export function getColumnData(confusionMatrixData: ConfusionMatrix[]) { for (let i = 0; i < predictedClasses.length; i++) { const predictedClass = predictedClasses[i].predicted_class; const predictedClassCount = predictedClasses[i].count; - col[predictedClass] = predictedClassCount; + col.predicted_classes_count[predictedClass] = predictedClassCount; } colData.push(col); - }); + } if (showOther) { columns.push({ id: OTHER_CLASS_ID, initialWidth: COL_INITIAL_WIDTH }); 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 20866bf43a2f4..6bc838d87d630 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 @@ -13,6 +13,8 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { EuiButtonEmpty, EuiDataGrid, + EuiDataGridCellValueElementProps, + EuiDataGridPopoverContents, EuiFlexGroup, EuiFlexItem, EuiIconTip, @@ -37,9 +39,11 @@ import { getRocCurveChartVegaLiteSpec } from './get_roc_curve_chart_vega_lite_sp import { getColumnData, + getTrailingControlColumns, + ConfusionMatrixColumn, + ConfusionMatrixColumnData, ACTUAL_CLASS_ID, MAX_COLUMNS, - getTrailingControlColumns, } from './column_data'; import { isTrainingFilter } from './is_training_filter'; @@ -94,10 +98,10 @@ export const EvaluatePanel: FC<EvaluatePanelProps> = ({ jobConfig, jobStatus, se services: { docLinks }, } = useMlKibana(); - const [columns, setColumns] = useState<any>([]); - const [columnsData, setColumnsData] = useState<any>([]); + const [columns, setColumns] = useState<ConfusionMatrixColumn[]>([]); + const [columnsData, setColumnsData] = useState<ConfusionMatrixColumnData[]>([]); const [showFullColumns, setShowFullColumns] = useState<boolean>(false); - const [popoverContents, setPopoverContents] = useState<any>([]); + const [popoverContents, setPopoverContents] = useState<EuiDataGridPopoverContents>({}); const [dataSubsetTitle, setDataSubsetTitle] = useState<SUBSET_TITLE>(SUBSET_TITLE.ENTIRE); // Column visibility const [visibleColumns, setVisibleColumns] = useState<string[]>(() => @@ -144,8 +148,7 @@ export const EvaluatePanel: FC<EvaluatePanelProps> = ({ jobConfig, jobStatus, se const gridItem = columnData[rowIndex]; if (gridItem !== undefined && colId !== ACTUAL_CLASS_ID) { - // @ts-ignore - const count = gridItem[colId]; + const count = gridItem.predicted_classes_count[colId]; return `${count} / ${gridItem.actual_class_doc_count} * 100 = ${cellContentsElement.textContent}`; } @@ -160,7 +163,11 @@ export const EvaluatePanel: FC<EvaluatePanelProps> = ({ jobConfig, jobStatus, se classificationClasses, error: errorRocCurve, isLoading: isLoadingRocCurve, - } = useRocCurve(jobConfig, searchQuery, visibleColumns); + } = useRocCurve( + jobConfig, + searchQuery, + columns.map((d) => d.id) + ); const renderCellValue = ({ rowIndex, @@ -169,17 +176,20 @@ export const EvaluatePanel: FC<EvaluatePanelProps> = ({ jobConfig, jobStatus, se }: { rowIndex: number; columnId: string; - setCellProps: any; + setCellProps: EuiDataGridCellValueElementProps['setCellProps']; }) => { - const cellValue = columnsData[rowIndex][columnId]; + const cellValue = + columnId === ACTUAL_CLASS_ID + ? columnsData[rowIndex][columnId] + : columnsData[rowIndex].predicted_classes_count[columnId]; const actualCount = columnsData[rowIndex] && columnsData[rowIndex].actual_class_doc_count; - let accuracy: number | string = '0%'; + let accuracy: string = '0%'; - if (columnId !== ACTUAL_CLASS_ID && actualCount) { - accuracy = cellValue / actualCount; + if (columnId !== ACTUAL_CLASS_ID && actualCount && typeof cellValue === 'number') { + let accuracyNumber: number = cellValue / actualCount; // round to 2 decimal places without converting to string; - accuracy = Math.round(accuracy * 100) / 100; - accuracy = `${Math.round(accuracy * 100)}%`; + accuracyNumber = Math.round(accuracyNumber * 100) / 100; + accuracy = `${Math.round(accuracyNumber * 100)}%`; } // eslint-disable-next-line react-hooks/rules-of-hooks useEffect(() => { diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/get_roc_curve_chart_vega_lite_spec.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/get_roc_curve_chart_vega_lite_spec.tsx index 7e279308c6879..e482c89a96dc0 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/get_roc_curve_chart_vega_lite_spec.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/get_roc_curve_chart_vega_lite_spec.tsx @@ -127,6 +127,10 @@ export const getRocCurveChartVegaLiteSpec = ( }, height: SIZE, width: SIZE, - mark: 'line', + mark: { + type: 'line', + strokeCap: 'round', + strokeJoin: 'round', + }, }; }; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/use_roc_curve.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/use_roc_curve.ts index 8cdb6f86ebdda..20521258cd374 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/use_roc_curve.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/use_roc_curve.ts @@ -26,6 +26,11 @@ import { ACTUAL_CLASS_ID, OTHER_CLASS_ID } from './column_data'; import { isTrainingFilter } from './is_training_filter'; +const AUC_VALUE_LABEL = 'AUC'; +const AUC_ROUNDING_VALUE = 100000; +const ROC_CLASS_NAME = 'ROC'; +const BINARY_CLASSIFICATION_THRESHOLD = 2; + interface RocCurveDataRow extends RocCurveItem { class_name: string; } @@ -33,12 +38,17 @@ interface RocCurveDataRow extends RocCurveItem { export const useRocCurve = ( jobConfig: DataFrameAnalyticsConfig, searchQuery: ResultsSearchQuery, - visibleColumns: string[] + columns: string[] ) => { - const classificationClasses = visibleColumns.filter( + const classificationClasses = columns.filter( (d) => d !== ACTUAL_CLASS_ID && d !== OTHER_CLASS_ID ); + // For binary classification jobs we only need to get the data for one class. + if (classificationClasses.length <= BINARY_CLASSIFICATION_THRESHOLD) { + classificationClasses.splice(1); + } + const [rocCurveData, setRocCurveData] = useState<RocCurveDataRow[]>([]); const [isLoading, setIsLoading] = useState<boolean>(false); const [error, setError] = useState<null | string[]>(null); @@ -83,9 +93,19 @@ export const useRocCurve = ( isClassificationEvaluateResponse(evalData.eval) ) { const auc = evalData.eval?.classification?.auc_roc?.value || 0; + + // For binary classification jobs we use the 'ROC' label, + // for multi-class classification the original class name. + const rocCurveClassLabel = + classificationClasses.length > BINARY_CLASSIFICATION_THRESHOLD + ? classificationClasses[i] + : ROC_CLASS_NAME; + const rocCurveDataForClass = (evalData.eval?.classification?.auc_roc?.curve || []).map( (d) => ({ - class_name: `${rocCurveClassName} (AUC: ${Math.round(auc * 100000) / 100000})`, + class_name: `${rocCurveClassLabel} (${AUC_VALUE_LABEL}: ${ + Math.round(auc * AUC_ROUNDING_VALUE) / AUC_ROUNDING_VALUE + })`, ...d, }) ); @@ -101,7 +121,18 @@ export const useRocCurve = ( } loadRocCurveData(); - }, [JSON.stringify([jobConfig, searchQuery, visibleColumns])]); - - return { rocCurveData, classificationClasses, error, isLoading }; + }, [JSON.stringify([jobConfig, searchQuery, columns])]); + + return { + rocCurveData, + // To match the data that was generated for the class, + // for multi-class classification jobs this returns all class names, + // for binary classification it returns just ['ROC']. + classificationClasses: + classificationClasses.length > BINARY_CLASSIFICATION_THRESHOLD + ? classificationClasses + : [ROC_CLASS_NAME], + error, + isLoading, + }; }; diff --git a/x-pack/plugins/transform/public/app/hooks/use_pivot_data.ts b/x-pack/plugins/transform/public/app/hooks/use_pivot_data.ts index 32d8ed16a643b..2477c005c936d 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_pivot_data.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_pivot_data.ts @@ -193,15 +193,7 @@ export const usePivotData = ( ); const renderCellValue: RenderCellValue = useMemo(() => { - return ({ - rowIndex, - columnId, - setCellProps, - }: { - rowIndex: number; - columnId: string; - setCellProps: any; - }) => { + return ({ rowIndex, columnId }: { rowIndex: number; columnId: string }) => { const adjustedRowIndex = rowIndex - pagination.pageIndex * pagination.pageSize; const cellValue = pageData.hasOwnProperty(adjustedRowIndex) diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts index 90705a73e3839..0c4f1b33948bd 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts @@ -44,10 +44,8 @@ export default function ({ getService }: FtrProviderContext) { rocCurveColorState: [ // tick/grid/axis { key: '#DDDDDD', value: 50 }, - // lines + // line { key: '#98A2B3', value: 30 }, - { key: '#6092C0', value: 10 }, - { key: '#5F92C0', value: 6 }, ], scatterplotMatrixColorStats: [ // marker colors From a6c0ff6e40cd234192ef18c74bfd727b12ef5abc Mon Sep 17 00:00:00 2001 From: Nathan Reese <reese.nathan@gmail.com> Date: Sat, 20 Mar 2021 14:59:52 -0600 Subject: [PATCH 07/13] [ml] migrate file_data_visualizer/analyze_file to file_upload plugin (#94259) * [ml] migrate file_data_visualizer/analyze_file to file_upload plugin * tslint * give analyze route access to ml user Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/file_upload/common/types.ts | 67 +++++++++++++++++ .../server/analyze_file.tsx} | 45 ++++++------ .../plugins/file_upload/server/import_data.ts | 3 +- x-pack/plugins/file_upload/server/routes.ts | 52 +++++++++++++- x-pack/plugins/file_upload/server/schemas.ts | 17 +++++ .../plugins/ml/common/types/capabilities.ts | 8 ++- .../ml/common/types/file_datavisualizer.ts | 71 ------------------- .../analysis_summary/analysis_summary.tsx | 2 +- .../combined_fields/combined_fields_form.tsx | 2 +- .../components/combined_fields/geo_point.tsx | 2 +- .../components/combined_fields/utils.ts | 7 +- .../explanation_flyout/explanation_flyout.tsx | 2 +- .../fields_stats_grid/create_fields.ts | 2 +- .../fields_stats_grid/fields_stats_grid.tsx | 2 +- .../fields_stats_grid/get_field_names.ts | 2 +- .../filebeat_config_flyout/filebeat_config.ts | 2 +- .../filebeat_config_flyout.tsx | 2 +- .../components/import_settings/advanced.tsx | 2 +- .../import_settings/import_settings.tsx | 2 +- .../results_links/results_links.tsx | 2 +- .../components/results_view/results_view.tsx | 2 +- .../file_based/components/utils/utils.ts | 2 +- .../services/ml_api_service/datavisualizer.ts | 4 +- .../models/file_data_visualizer/index.ts | 8 --- x-pack/plugins/ml/server/plugin.ts | 2 - .../ml/server/routes/file_data_visualizer.ts | 60 ---------------- .../schemas/file_data_visualizer_schema.ts | 27 ------- 27 files changed, 181 insertions(+), 218 deletions(-) rename x-pack/plugins/{ml/server/models/file_data_visualizer/file_data_visualizer.ts => file_upload/server/analyze_file.tsx} (58%) delete mode 100644 x-pack/plugins/ml/common/types/file_datavisualizer.ts delete mode 100644 x-pack/plugins/ml/server/models/file_data_visualizer/index.ts delete mode 100644 x-pack/plugins/ml/server/routes/file_data_visualizer.ts delete mode 100644 x-pack/plugins/ml/server/routes/schemas/file_data_visualizer_schema.ts diff --git a/x-pack/plugins/file_upload/common/types.ts b/x-pack/plugins/file_upload/common/types.ts index c01e514f0f720..3c385bdf8f3df 100644 --- a/x-pack/plugins/file_upload/common/types.ts +++ b/x-pack/plugins/file_upload/common/types.ts @@ -5,6 +5,73 @@ * 2.0. */ +import { ES_FIELD_TYPES } from '../../../../src/plugins/data/common'; + +export interface InputOverrides { + [key: string]: string | undefined; +} + +export type FormattedOverrides = InputOverrides & { + column_names: string[]; + has_header_row: boolean; + should_trim_fields: boolean; +}; + +export interface AnalysisResult { + results: FindFileStructureResponse; + overrides?: FormattedOverrides; +} + +export interface FindFileStructureResponse { + charset: string; + has_header_row: boolean; + has_byte_order_marker: boolean; + format: string; + field_stats: { + [fieldName: string]: { + count: number; + cardinality: number; + top_hits: Array<{ count: number; value: any }>; + mean_value?: number; + median_value?: number; + max_value?: number; + min_value?: number; + earliest?: string; + latest?: string; + }; + }; + sample_start: string; + num_messages_analyzed: number; + mappings: { + properties: { + [fieldName: string]: { + // including all possible Elasticsearch types + // since find_file_structure API can be enhanced to include new fields in the future + type: Exclude< + ES_FIELD_TYPES, + ES_FIELD_TYPES._ID | ES_FIELD_TYPES._INDEX | ES_FIELD_TYPES._SOURCE | ES_FIELD_TYPES._TYPE + >; + format?: string; + }; + }; + }; + quote: string; + delimiter: string; + need_client_timezone: boolean; + num_lines_analyzed: number; + column_names?: string[]; + explanation?: string[]; + grok_pattern?: string; + multiline_start_pattern?: string; + exclude_lines_pattern?: string; + java_timestamp_formats?: string[]; + joda_timestamp_formats?: string[]; + timestamp_field?: string; + should_trim_fields?: boolean; +} + +export type InputData = any[]; + export interface ImportResponse { success: boolean; id: string; diff --git a/x-pack/plugins/ml/server/models/file_data_visualizer/file_data_visualizer.ts b/x-pack/plugins/file_upload/server/analyze_file.tsx similarity index 58% rename from x-pack/plugins/ml/server/models/file_data_visualizer/file_data_visualizer.ts rename to x-pack/plugins/file_upload/server/analyze_file.tsx index 6e57e997e5f00..394573eb0cca5 100644 --- a/x-pack/plugins/ml/server/models/file_data_visualizer/file_data_visualizer.ts +++ b/x-pack/plugins/file_upload/server/analyze_file.tsx @@ -9,32 +9,29 @@ import { IScopedClusterClient } from 'kibana/server'; import { AnalysisResult, FormattedOverrides, + InputData, InputOverrides, FindFileStructureResponse, -} from '../../../common/types/file_datavisualizer'; - -export type InputData = any[]; - -export function fileDataVisualizerProvider(client: IScopedClusterClient) { - async function analyzeFile(data: InputData, overrides: InputOverrides): Promise<AnalysisResult> { - overrides.explain = overrides.explain === undefined ? 'true' : overrides.explain; - const { - body, - } = await client.asInternalUser.textStructure.findStructure<FindFileStructureResponse>({ - body: data, - ...overrides, - }); - - const { hasOverrides, reducedOverrides } = formatOverrides(overrides); - - return { - ...(hasOverrides && { overrides: reducedOverrides }), - results: body, - }; - } +} from '../common'; + +export async function analyzeFile( + client: IScopedClusterClient, + data: InputData, + overrides: InputOverrides +): Promise<AnalysisResult> { + overrides.explain = overrides.explain === undefined ? 'true' : overrides.explain; + const { + body, + } = await client.asInternalUser.textStructure.findStructure<FindFileStructureResponse>({ + body: data, + ...overrides, + }); + + const { hasOverrides, reducedOverrides } = formatOverrides(overrides); return { - analyzeFile, + ...(hasOverrides && { overrides: reducedOverrides }), + results: body, }; } @@ -42,8 +39,8 @@ function formatOverrides(overrides: InputOverrides) { let hasOverrides = false; const reducedOverrides: FormattedOverrides = Object.keys(overrides).reduce((acc, overrideKey) => { - const overrideValue: string = overrides[overrideKey]; - if (overrideValue !== '') { + const overrideValue: string | undefined = overrides[overrideKey]; + if (overrideValue !== undefined && overrideValue !== '') { if (overrideKey === 'column_names') { acc.column_names = overrideValue.split(','); } else if (overrideKey === 'has_header_row') { diff --git a/x-pack/plugins/file_upload/server/import_data.ts b/x-pack/plugins/file_upload/server/import_data.ts index 510302beaf6dd..f93d73647ed0e 100644 --- a/x-pack/plugins/file_upload/server/import_data.ts +++ b/x-pack/plugins/file_upload/server/import_data.ts @@ -10,13 +10,12 @@ import { INDEX_META_DATA_CREATED_BY } from '../common/constants'; import { ImportResponse, ImportFailure, + InputData, Settings, Mappings, IngestPipelineWrapper, } from '../common'; -export type InputData = any[]; - export function importDataProvider({ asCurrentUser }: IScopedClusterClient) { async function importData( id: string | undefined, diff --git a/x-pack/plugins/file_upload/server/routes.ts b/x-pack/plugins/file_upload/server/routes.ts index d7b7b8f99edd9..4f4adb29f6b0b 100644 --- a/x-pack/plugins/file_upload/server/routes.ts +++ b/x-pack/plugins/file_upload/server/routes.ts @@ -5,13 +5,21 @@ * 2.0. */ +import { schema } from '@kbn/config-schema'; import { IRouter, IScopedClusterClient } from 'kibana/server'; -import { MAX_FILE_SIZE_BYTES, IngestPipelineWrapper, Mappings, Settings } from '../common'; +import { + MAX_FILE_SIZE_BYTES, + IngestPipelineWrapper, + InputData, + Mappings, + Settings, +} from '../common'; import { wrapError } from './error_wrapper'; -import { InputData, importDataProvider } from './import_data'; +import { analyzeFile } from './analyze_file'; +import { importDataProvider } from './import_data'; import { updateTelemetry } from './telemetry'; -import { importFileBodySchema, importFileQuerySchema } from './schemas'; +import { analyzeFileQuerySchema, importFileBodySchema, importFileQuerySchema } from './schemas'; function importData( client: IScopedClusterClient, @@ -30,6 +38,44 @@ function importData( * Routes for the file upload. */ export function fileUploadRoutes(router: IRouter) { + /** + * @apiGroup FileDataVisualizer + * + * @api {post} /api/file_upload/analyze_file Analyze file data + * @apiName AnalyzeFile + * @apiDescription Performs analysis of the file data. + * + * @apiSchema (query) analyzeFileQuerySchema + */ + router.post( + { + path: '/api/file_upload/analyze_file', + validate: { + body: schema.any(), + query: analyzeFileQuerySchema, + }, + options: { + body: { + accepts: ['text/*', 'application/json'], + maxBytes: MAX_FILE_SIZE_BYTES, + }, + tags: ['access:fileUpload:analyzeFile'], + }, + }, + async (context, request, response) => { + try { + const result = await analyzeFile( + context.core.elasticsearch.client, + request.body, + request.query + ); + return response.ok({ body: result }); + } catch (e) { + return response.customError(wrapError(e)); + } + } + ); + /** * @apiGroup FileDataVisualizer * diff --git a/x-pack/plugins/file_upload/server/schemas.ts b/x-pack/plugins/file_upload/server/schemas.ts index bfef9885b3216..a0d54cf9ec553 100644 --- a/x-pack/plugins/file_upload/server/schemas.ts +++ b/x-pack/plugins/file_upload/server/schemas.ts @@ -7,6 +7,23 @@ import { schema } from '@kbn/config-schema'; +export const analyzeFileQuerySchema = schema.object({ + charset: schema.maybe(schema.string()), + column_names: schema.maybe(schema.string()), + delimiter: schema.maybe(schema.string()), + explain: schema.maybe(schema.string()), + format: schema.maybe(schema.string()), + grok_pattern: schema.maybe(schema.string()), + has_header_row: schema.maybe(schema.string()), + line_merge_size_limit: schema.maybe(schema.string()), + lines_to_sample: schema.maybe(schema.string()), + quote: schema.maybe(schema.string()), + should_trim_fields: schema.maybe(schema.string()), + timeout: schema.maybe(schema.string()), + timestamp_field: schema.maybe(schema.string()), + timestamp_format: schema.maybe(schema.string()), +}); + export const importFileQuerySchema = schema.object({ id: schema.maybe(schema.string()), }); diff --git a/x-pack/plugins/ml/common/types/capabilities.ts b/x-pack/plugins/ml/common/types/capabilities.ts index 61a5013642cd7..129b496c00149 100644 --- a/x-pack/plugins/ml/common/types/capabilities.ts +++ b/x-pack/plugins/ml/common/types/capabilities.ts @@ -102,7 +102,11 @@ export function getPluginPrivileges() { return { admin: { ...privilege, - api: ['fileUpload:import', ...allMlCapabilitiesKeys.map((k) => `ml:${k}`)], + api: [ + 'fileUpload:import', + 'fileUpload:analyzeFile', + ...allMlCapabilitiesKeys.map((k) => `ml:${k}`), + ], catalogue: [PLUGIN_ID, `${PLUGIN_ID}_file_data_visualizer`], ui: allMlCapabilitiesKeys, savedObject: { @@ -116,7 +120,7 @@ export function getPluginPrivileges() { }, user: { ...privilege, - api: userMlCapabilitiesKeys.map((k) => `ml:${k}`), + api: ['fileUpload:analyzeFile', ...userMlCapabilitiesKeys.map((k) => `ml:${k}`)], catalogue: [PLUGIN_ID], management: { insightsAndAlerting: [] }, ui: userMlCapabilitiesKeys, diff --git a/x-pack/plugins/ml/common/types/file_datavisualizer.ts b/x-pack/plugins/ml/common/types/file_datavisualizer.ts deleted file mode 100644 index 500ee98823485..0000000000000 --- a/x-pack/plugins/ml/common/types/file_datavisualizer.ts +++ /dev/null @@ -1,71 +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 { ES_FIELD_TYPES } from '../../../../../src/plugins/data/common'; - -export interface InputOverrides { - [key: string]: string; -} - -export type FormattedOverrides = InputOverrides & { - column_names: string[]; - has_header_row: boolean; - should_trim_fields: boolean; -}; - -export interface AnalysisResult { - results: FindFileStructureResponse; - overrides?: FormattedOverrides; -} - -export interface FindFileStructureResponse { - charset: string; - has_header_row: boolean; - has_byte_order_marker: boolean; - format: string; - field_stats: { - [fieldName: string]: { - count: number; - cardinality: number; - top_hits: Array<{ count: number; value: any }>; - mean_value?: number; - median_value?: number; - max_value?: number; - min_value?: number; - earliest?: string; - latest?: string; - }; - }; - sample_start: string; - num_messages_analyzed: number; - mappings: { - properties: { - [fieldName: string]: { - // including all possible Elasticsearch types - // since find_file_structure API can be enhanced to include new fields in the future - type: Exclude< - ES_FIELD_TYPES, - ES_FIELD_TYPES._ID | ES_FIELD_TYPES._INDEX | ES_FIELD_TYPES._SOURCE | ES_FIELD_TYPES._TYPE - >; - format?: string; - }; - }; - }; - quote: string; - delimiter: string; - need_client_timezone: boolean; - num_lines_analyzed: number; - column_names?: string[]; - explanation?: string[]; - grok_pattern?: string; - multiline_start_pattern?: string; - exclude_lines_pattern?: string; - java_timestamp_formats?: string[]; - joda_timestamp_formats?: string[]; - timestamp_field?: string; - should_trim_fields?: boolean; -} diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/analysis_summary/analysis_summary.tsx b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/analysis_summary/analysis_summary.tsx index 0691ed6fdd441..d787ee5cb844e 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/analysis_summary/analysis_summary.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/analysis_summary/analysis_summary.tsx @@ -9,7 +9,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import React, { FC } from 'react'; import { EuiTitle, EuiSpacer, EuiDescriptionList } from '@elastic/eui'; -import { FindFileStructureResponse } from '../../../../../../common/types/file_datavisualizer'; +import { FindFileStructureResponse } from '../../../../../../../file_upload/common'; export const AnalysisSummary: FC<{ results: FindFileStructureResponse }> = ({ results }) => { const items = createDisplayItems(results); diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/combined_fields/combined_fields_form.tsx b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/combined_fields/combined_fields_form.tsx index 0b67b24b65c5e..02ead5c26f959 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/combined_fields/combined_fields_form.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/combined_fields/combined_fields_form.tsx @@ -29,7 +29,7 @@ import { removeCombinedFieldsFromMappings, removeCombinedFieldsFromPipeline, } from './utils'; -import { FindFileStructureResponse } from '../../../../../../common/types/file_datavisualizer'; +import { FindFileStructureResponse } from '../../../../../../../file_upload/common'; interface Props { mappingsString: string; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/combined_fields/geo_point.tsx b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/combined_fields/geo_point.tsx index d12de6c5c91a5..5ae2e5de681c3 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/combined_fields/geo_point.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/combined_fields/geo_point.tsx @@ -29,7 +29,7 @@ import { getFieldNames, getNameCollisionMsg, } from './utils'; -import { FindFileStructureResponse } from '../../../../../../common/types/file_datavisualizer'; +import { FindFileStructureResponse } from '../../../../../../../file_upload/common'; interface Props { addCombinedField: (combinedField: CombinedField) => void; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/combined_fields/utils.ts b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/combined_fields/utils.ts index da1efe034bd54..ab08398fcda02 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/combined_fields/utils.ts +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/combined_fields/utils.ts @@ -9,8 +9,11 @@ import { i18n } from '@kbn/i18n'; import { cloneDeep } from 'lodash'; import uuid from 'uuid/v4'; import { CombinedField } from './types'; -import { FindFileStructureResponse } from '../../../../../../common/types/file_datavisualizer'; -import { IngestPipeline, Mappings } from '../../../../../../../file_upload/common'; +import { + FindFileStructureResponse, + IngestPipeline, + Mappings, +} from '../../../../../../../file_upload/common'; const COMMON_LAT_NAMES = ['latitude', 'lat']; const COMMON_LON_NAMES = ['longitude', 'long', 'lon']; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/explanation_flyout/explanation_flyout.tsx b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/explanation_flyout/explanation_flyout.tsx index 199b03b728a96..579f2e3340954 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/explanation_flyout/explanation_flyout.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/explanation_flyout/explanation_flyout.tsx @@ -20,7 +20,7 @@ import { EuiText, EuiSubSteps, } from '@elastic/eui'; -import { FindFileStructureResponse } from '../../../../../../common/types/file_datavisualizer'; +import { FindFileStructureResponse } from '../../../../../../../file_upload/common'; interface Props { results: FindFileStructureResponse; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats_grid/create_fields.ts b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats_grid/create_fields.ts index 86ca1fbbe6f82..fdbb35d27c531 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats_grid/create_fields.ts +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats_grid/create_fields.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FindFileStructureResponse } from '../../../../../../common/types/file_datavisualizer'; +import { FindFileStructureResponse } from '../../../../../../../file_upload/common'; import { getFieldNames, getSupportedFieldType } from './get_field_names'; import { FileBasedFieldVisConfig } from '../../../stats_table/types'; import { ML_JOB_FIELD_TYPES } from '../../../../../../common/constants/field_types'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats_grid/fields_stats_grid.tsx b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats_grid/fields_stats_grid.tsx index 364242e9bf325..1029d58b4c639 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats_grid/fields_stats_grid.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats_grid/fields_stats_grid.tsx @@ -7,7 +7,7 @@ import React, { useMemo, FC } from 'react'; import { EuiFlexGroup, EuiSpacer } from '@elastic/eui'; -import type { FindFileStructureResponse } from '../../../../../../common/types/file_datavisualizer'; +import type { FindFileStructureResponse } from '../../../../../../../file_upload/common'; import { DataVisualizerTable, ItemIdToExpandedRowMap } from '../../../stats_table'; import type { FileBasedFieldVisConfig } from '../../../stats_table/types/field_vis_config'; import { FileBasedDataVisualizerExpandedRow } from '../expanded_row'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats_grid/get_field_names.ts b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats_grid/get_field_names.ts index 2e0c3feeee4f1..d1cb361a84a72 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats_grid/get_field_names.ts +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats_grid/get_field_names.ts @@ -6,7 +6,7 @@ */ import { difference } from 'lodash'; -import type { FindFileStructureResponse } from '../../../../../../common/types/file_datavisualizer'; +import type { FindFileStructureResponse } from '../../../../../../../file_upload/common'; import { MlJobFieldType } from '../../../../../../common/types/field_types'; import { ML_JOB_FIELD_TYPES } from '../../../../../../common/constants/field_types'; import { ES_FIELD_TYPES } from '../../../../../../../../../src/plugins/data/common'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/filebeat_config.ts b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/filebeat_config.ts index da22b807f4332..2254110432bdb 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/filebeat_config.ts +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/filebeat_config.ts @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import { FindFileStructureResponse } from '../../../../../../common/types/file_datavisualizer'; +import { FindFileStructureResponse } from '../../../../../../../file_upload/common'; export function createFilebeatConfig( index: string, diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/filebeat_config_flyout.tsx b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/filebeat_config_flyout.tsx index a288579f91b58..c3b53d4430087 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/filebeat_config_flyout.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/filebeat_config_flyout.tsx @@ -23,7 +23,7 @@ import { } from '@elastic/eui'; import { createFilebeatConfig } from './filebeat_config'; import { useMlKibana } from '../../../../contexts/kibana'; -import { FindFileStructureResponse } from '../../../../../../common/types/file_datavisualizer'; +import { FindFileStructureResponse } from '../../../../../../../file_upload/common'; export enum EDITOR_MODE { HIDDEN, diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/advanced.tsx b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/advanced.tsx index 94f4aaa1c4bab..eb0e09973f0e3 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/advanced.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/advanced.tsx @@ -20,7 +20,7 @@ import { import { CombinedField, CombinedFieldsForm } from '../combined_fields'; import { MLJobEditor, ML_EDITOR_MODE } from '../../../../jobs/jobs_list/components/ml_job_editor'; -import { FindFileStructureResponse } from '../../../../../../common/types/file_datavisualizer'; +import { FindFileStructureResponse } from '../../../../../../../file_upload/common'; const EDITOR_HEIGHT = '300px'; interface Props { diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/import_settings.tsx b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/import_settings.tsx index c75d2074e60a3..5a9597723a0b5 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/import_settings.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/import_settings.tsx @@ -13,7 +13,7 @@ import { EuiTabbedContent, EuiSpacer } from '@elastic/eui'; import { SimpleSettings } from './simple'; import { AdvancedSettings } from './advanced'; import { CombinedField } from '../combined_fields'; -import { FindFileStructureResponse } from '../../../../../../common/types/file_datavisualizer'; +import { FindFileStructureResponse } from '../../../../../../../file_upload/common'; interface Props { index: string; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_links/results_links.tsx b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_links/results_links.tsx index 6ce75a4be840e..90b8fb4ac0cbb 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_links/results_links.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_links/results_links.tsx @@ -20,7 +20,7 @@ import { DISCOVER_APP_URL_GENERATOR, DiscoverUrlGeneratorState, } from '../../../../../../../../../src/plugins/discover/public'; -import { FindFileStructureResponse } from '../../../../../../common/types/file_datavisualizer'; +import { FindFileStructureResponse } from '../../../../../../../file_upload/common'; const RECHECK_DELAY_MS = 3000; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_view/results_view.tsx b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_view/results_view.tsx index cfcdd96dc3c8a..7431bfd4295e4 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_view/results_view.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_view/results_view.tsx @@ -20,7 +20,7 @@ import { EuiFlexGroup, EuiFlexItem, } from '@elastic/eui'; -import { FindFileStructureResponse } from '../../../../../../common/types/file_datavisualizer'; +import { FindFileStructureResponse } from '../../../../../../../file_upload/common'; import { FileContents } from '../file_contents'; import { AnalysisSummary } from '../analysis_summary'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/utils.ts b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/utils.ts index ebde771603fcf..85ca27cdf90ff 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/utils.ts +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/utils.ts @@ -7,7 +7,7 @@ import { isEqual } from 'lodash'; import { ml } from '../../../../services/ml_api_service'; -import { AnalysisResult, InputOverrides } from '../../../../../../common/types/file_datavisualizer'; +import { AnalysisResult, InputOverrides } from '../../../../../../../file_upload/common'; import { MB } from '../../../../../../../file_upload/public'; export const DEFAULT_LINES_TO_SAMPLE = 1000; diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/datavisualizer.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/datavisualizer.ts index 98a0d7b9b0a94..ccea697de4b9c 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/datavisualizer.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/datavisualizer.ts @@ -7,13 +7,11 @@ import { http } from '../http_service'; -import { basePath } from './index'; - export const fileDatavisualizer = { analyzeFile(file: string, params: Record<string, string> = {}) { const body = JSON.stringify(file); return http<any>({ - path: `${basePath()}/file_data_visualizer/analyze_file`, + path: '/api/file_upload/analyze_file', method: 'POST', body, query: params, diff --git a/x-pack/plugins/ml/server/models/file_data_visualizer/index.ts b/x-pack/plugins/ml/server/models/file_data_visualizer/index.ts deleted file mode 100644 index 8e4c9935f5537..0000000000000 --- a/x-pack/plugins/ml/server/models/file_data_visualizer/index.ts +++ /dev/null @@ -1,8 +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. - */ - -export { fileDataVisualizerProvider, InputData } from './file_data_visualizer'; diff --git a/x-pack/plugins/ml/server/plugin.ts b/x-pack/plugins/ml/server/plugin.ts index c4ee1fd76530e..173b30716c6b6 100644 --- a/x-pack/plugins/ml/server/plugin.ts +++ b/x-pack/plugins/ml/server/plugin.ts @@ -34,7 +34,6 @@ import { dataFrameAnalyticsRoutes } from './routes/data_frame_analytics'; import { dataRecognizer } from './routes/modules'; import { dataVisualizerRoutes } from './routes/data_visualizer'; import { fieldsService } from './routes/fields_service'; -import { fileDataVisualizerRoutes } from './routes/file_data_visualizer'; import { filtersRoutes } from './routes/filters'; import { indicesRoutes } from './routes/indices'; import { jobAuditMessagesRoutes } from './routes/job_audit_messages'; @@ -172,7 +171,6 @@ export class MlServerPlugin dataRecognizer(routeInit); dataVisualizerRoutes(routeInit); fieldsService(routeInit); - fileDataVisualizerRoutes(routeInit); filtersRoutes(routeInit); indicesRoutes(routeInit); jobAuditMessagesRoutes(routeInit); diff --git a/x-pack/plugins/ml/server/routes/file_data_visualizer.ts b/x-pack/plugins/ml/server/routes/file_data_visualizer.ts deleted file mode 100644 index 6b200c59f57d5..0000000000000 --- a/x-pack/plugins/ml/server/routes/file_data_visualizer.ts +++ /dev/null @@ -1,60 +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 { IScopedClusterClient } from 'kibana/server'; -import { schema } from '@kbn/config-schema'; -import { MAX_FILE_SIZE_BYTES } from '../../../file_upload/common'; -import { InputOverrides } from '../../common/types/file_datavisualizer'; -import { wrapError } from '../client/error_wrapper'; -import { InputData, fileDataVisualizerProvider } from '../models/file_data_visualizer'; - -import { RouteInitialization } from '../types'; -import { analyzeFileQuerySchema } from './schemas/file_data_visualizer_schema'; - -function analyzeFiles(client: IScopedClusterClient, data: InputData, overrides: InputOverrides) { - const { analyzeFile } = fileDataVisualizerProvider(client); - return analyzeFile(data, overrides); -} - -/** - * Routes for the file data visualizer. - */ -export function fileDataVisualizerRoutes({ router, routeGuard }: RouteInitialization) { - /** - * @apiGroup FileDataVisualizer - * - * @api {post} /api/ml/file_data_visualizer/analyze_file Analyze file data - * @apiName AnalyzeFile - * @apiDescription Performs analysis of the file data. - * - * @apiSchema (query) analyzeFileQuerySchema - */ - router.post( - { - path: '/api/ml/file_data_visualizer/analyze_file', - validate: { - body: schema.any(), - query: analyzeFileQuerySchema, - }, - options: { - body: { - accepts: ['text/*', 'application/json'], - maxBytes: MAX_FILE_SIZE_BYTES, - }, - tags: ['access:ml:canFindFileStructure'], - }, - }, - routeGuard.basicLicenseAPIGuard(async ({ client, request, response }) => { - try { - const result = await analyzeFiles(client, request.body, request.query); - return response.ok({ body: result }); - } catch (e) { - return response.customError(wrapError(e)); - } - }) - ); -} diff --git a/x-pack/plugins/ml/server/routes/schemas/file_data_visualizer_schema.ts b/x-pack/plugins/ml/server/routes/schemas/file_data_visualizer_schema.ts deleted file mode 100644 index 7d368ed38b2e3..0000000000000 --- a/x-pack/plugins/ml/server/routes/schemas/file_data_visualizer_schema.ts +++ /dev/null @@ -1,27 +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 { schema } from '@kbn/config-schema'; - -export const analyzeFileQuerySchema = schema.maybe( - schema.object({ - charset: schema.maybe(schema.string()), - column_names: schema.maybe(schema.string()), - delimiter: schema.maybe(schema.string()), - explain: schema.maybe(schema.string()), - format: schema.maybe(schema.string()), - grok_pattern: schema.maybe(schema.string()), - has_header_row: schema.maybe(schema.string()), - line_merge_size_limit: schema.maybe(schema.string()), - lines_to_sample: schema.maybe(schema.string()), - quote: schema.maybe(schema.string()), - should_trim_fields: schema.maybe(schema.string()), - timeout: schema.maybe(schema.string()), - timestamp_field: schema.maybe(schema.string()), - timestamp_format: schema.maybe(schema.string()), - }) -); From c0f9bfcd217f20a71818e432e470cbd01875349d Mon Sep 17 00:00:00 2001 From: Madison Caldwell <madison.rey.caldwell@gmail.com> Date: Sun, 21 Mar 2021 10:02:45 -0400 Subject: [PATCH 08/13] [Security Solution][Detections][Threshold Rules][7.12] Threshold summary view (#94345) * Add threshold summary view items * Add threshold field desgination * Add threshold fields to signal doc * Fix unit test * Handle error --- .../components/event_details/summary_view.tsx | 45 +++++++++++++++++++ .../components/alerts_table/translations.ts | 21 +++++++++ .../bulk_create_threshold_signals.test.ts | 3 ++ .../bulk_create_threshold_signals.ts | 9 ++++ 4 files changed, 78 insertions(+) diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/summary_view.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/summary_view.tsx index eb6e965975171..8e07910c1c071 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/summary_view.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/summary_view.tsx @@ -25,6 +25,9 @@ import { ALERTS_HEADERS_RISK_SCORE, ALERTS_HEADERS_RULE, ALERTS_HEADERS_SEVERITY, + ALERTS_HEADERS_THRESHOLD_COUNT, + ALERTS_HEADERS_THRESHOLD_TERMS, + ALERTS_HEADERS_THRESHOLD_CARDINALITY, } from '../../../detections/components/alerts_table/translations'; import { IP_FIELD_TYPE, @@ -61,6 +64,9 @@ const fields = [ { id: 'user.name' }, { id: SOURCE_IP_FIELD_NAME, fieldType: IP_FIELD_TYPE }, { id: DESTINATION_IP_FIELD_NAME, fieldType: IP_FIELD_TYPE }, + { id: 'signal.threshold_result.count', label: ALERTS_HEADERS_THRESHOLD_COUNT }, + { id: 'signal.threshold_result.terms', label: ALERTS_HEADERS_THRESHOLD_TERMS }, + { id: 'signal.threshold_result.cardinality', label: ALERTS_HEADERS_THRESHOLD_CARDINALITY }, ]; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -135,6 +141,45 @@ const getSummary = ({ linkValue: linkValue ?? undefined, }; + if (item.id === 'signal.threshold_result.terms') { + try { + const terms = getOr(null, 'originalValue', field); + const parsedValue = terms.map((term: string) => JSON.parse(term)); + const thresholdTerms = (parsedValue ?? []).map( + (entry: { field: string; value: string }) => { + return { + title: `${entry.field} [threshold]`, + description: { + ...description, + value: entry.value, + }, + }; + } + ); + return [...acc, ...thresholdTerms]; + } catch (err) { + return acc; + } + } + + if (item.id === 'signal.threshold_result.cardinality') { + try { + const parsedValue = JSON.parse(value); + return [ + ...acc, + { + title: ALERTS_HEADERS_THRESHOLD_CARDINALITY, + description: { + ...description, + value: `count(${parsedValue.field}) == ${parsedValue.value}`, + }, + }, + ]; + } catch (err) { + return acc; + } + } + return [ ...acc, { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/translations.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/translations.ts index 8ecf06616cec2..1829b3822e6a4 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/translations.ts @@ -88,6 +88,27 @@ export const ALERTS_HEADERS_RISK_SCORE = i18n.translate( } ); +export const ALERTS_HEADERS_THRESHOLD_COUNT = i18n.translate( + 'xpack.securitySolution.eventsViewer.alerts.defaultHeaders.thresholdCount', + { + defaultMessage: 'Threshold Count', + } +); + +export const ALERTS_HEADERS_THRESHOLD_TERMS = i18n.translate( + 'xpack.securitySolution.eventsViewer.alerts.defaultHeaders.thresholdTerms', + { + defaultMessage: 'Threshold Terms', + } +); + +export const ALERTS_HEADERS_THRESHOLD_CARDINALITY = i18n.translate( + 'xpack.securitySolution.eventsViewer.alerts.defaultHeaders.thresholdCardinality', + { + defaultMessage: 'Threshold Cardinality', + } +); + export const ACTION_OPEN_ALERT = i18n.translate( 'xpack.securitySolution.detectionEngine.alerts.actions.openAlertTitle', { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.test.ts index 3726f66cb0f82..c0fdc4eb0189d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.test.ts @@ -79,6 +79,7 @@ describe('transformThresholdNormalizedResultsToEcs', () => { _id, _index: 'test', _source: { + 'source.ip': '127.0.0.1', '@timestamp': '2020-04-20T21:27:45+0000', threshold_result: { from: new Date('2020-12-17T16:27:00.000Z'), @@ -256,6 +257,8 @@ describe('transformThresholdNormalizedResultsToEcs', () => { _index: 'test', _source: { '@timestamp': '2020-04-20T21:27:45+0000', + 'host.name': 'garden-gnomes', + 'source.ip': '127.0.0.1', threshold_result: { from: new Date('2020-12-17T16:28:00.000Z'), // from threshold signal history terms: [ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.ts index c226b63a0b9ac..43158d1e89783 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.ts @@ -163,6 +163,15 @@ const getTransformedHits = ( const source = { '@timestamp': timestamp, + ...bucket.terms.reduce<object>((termAcc, term) => { + if (!term.field.startsWith('signal.')) { + return { + ...termAcc, + [term.field]: term.value, + }; + } + return termAcc; + }, {}), threshold_result: { terms: bucket.terms, cardinality: bucket.cardinality, From 0a5ca7bb04e004d8df499251cb4e9cb27dd2dd80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20S=C3=A1nchez?= <davidsansol92@gmail.com> Date: Mon, 22 Mar 2021 12:55:14 +0100 Subject: [PATCH 09/13] [SECURITY_SOLUTION] Better BE validation message for multiple same fields (#94935) * WIP: Use schema.conditional instead of schema.oneOf to ensure the right schema validation from an specific field type * Adds some comments on new schema definition * Use validate functions to set custom messages * Fixes type issue after schema changes. An overwrite of the schema inferred type is needed to match with the NewTrustedApp custom type * Updates schema test after schema changes * Changes error key by type. Updates related unit test * WIP: Parse BE message into an user friendly one. Waiting for final texts * Updates text messages for create trusted app errors Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../endpoint/schema/trusted_apps.test.ts | 10 +- .../common/endpoint/schema/trusted_apps.ts | 139 +++++++++++------- .../common/endpoint/types/trusted_apps.ts | 9 +- .../components/create_trusted_app_flyout.tsx | 15 +- .../pages/trusted_apps/view/translations.ts | 33 +++++ 5 files changed, 147 insertions(+), 59 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts index bcd2ee0358dfe..e9ae439d0ac8c 100644 --- a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts +++ b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts @@ -250,7 +250,9 @@ describe('When invoking Trusted Apps Schema', () => { const bodyMsg = createNewTrustedApp({ entries: [createConditionEntry(), createConditionEntry()], }); - expect(() => body.validate(bodyMsg)).toThrow('[Path] field can only be used once'); + expect(() => body.validate(bodyMsg)).toThrow( + '[entries]: duplicatedEntry.process.executable.caseless' + ); }); it('should validate that `entry.field` hash field value can only be used once', () => { @@ -266,7 +268,7 @@ describe('When invoking Trusted Apps Schema', () => { }), ], }); - expect(() => body.validate(bodyMsg)).toThrow('[Hash] field can only be used once'); + expect(() => body.validate(bodyMsg)).toThrow('[entries]: duplicatedEntry.process.hash.*'); }); it('should validate that `entry.field` signer field value can only be used once', () => { @@ -282,7 +284,9 @@ describe('When invoking Trusted Apps Schema', () => { }), ], }); - expect(() => body.validate(bodyMsg)).toThrow('[Signer] field can only be used once'); + expect(() => body.validate(bodyMsg)).toThrow( + '[entries]: duplicatedEntry.process.Ext.code_signature' + ); }); it('should validate Hash field valid value', () => { diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts index e5ab1d497e762..6d40dc75fd1c1 100644 --- a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts +++ b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts @@ -5,16 +5,10 @@ * 2.0. */ -import { schema, Type } from '@kbn/config-schema'; -import { ConditionEntry, ConditionEntryField, OperatingSystem } from '../types'; +import { schema } from '@kbn/config-schema'; +import { ConditionEntryField, OperatingSystem } from '../types'; import { getDuplicateFields, isValidHash } from '../validation/trusted_apps'; -const entryFieldLabels: { [k in ConditionEntryField]: string } = { - [ConditionEntryField.HASH]: 'Hash', - [ConditionEntryField.PATH]: 'Path', - [ConditionEntryField.SIGNER]: 'Signer', -}; - export const DeleteTrustedAppsRequestSchema = { params: schema.object({ id: schema.string(), @@ -30,56 +24,99 @@ export const GetTrustedAppsRequestSchema = { const ConditionEntryTypeSchema = schema.literal('match'); const ConditionEntryOperatorSchema = schema.literal('included'); -const HashConditionEntrySchema = schema.object({ - field: schema.literal(ConditionEntryField.HASH), + +/* + * A generic Entry schema to be used for a specific entry schema depending on the OS + */ +const CommonEntrySchema = { + field: schema.oneOf([ + schema.literal(ConditionEntryField.HASH), + schema.literal(ConditionEntryField.PATH), + ]), type: ConditionEntryTypeSchema, operator: ConditionEntryOperatorSchema, - value: schema.string({ - validate: (hash) => (isValidHash(hash) ? undefined : `Invalid hash value [${hash}]`), - }), + // If field === HASH then validate hash with custom method, else validate string with minLength = 1 + value: schema.conditional( + schema.siblingRef('field'), + ConditionEntryField.HASH, + schema.string({ + validate: (hash) => + isValidHash(hash) ? undefined : `invalidField.${ConditionEntryField.HASH}`, + }), + schema.conditional( + schema.siblingRef('field'), + ConditionEntryField.PATH, + schema.string({ + validate: (field) => + field.length > 0 ? undefined : `invalidField.${ConditionEntryField.PATH}`, + }), + schema.string({ + validate: (field) => + field.length > 0 ? undefined : `invalidField.${ConditionEntryField.SIGNER}`, + }) + ) + ), +}; + +const WindowsEntrySchema = schema.object({ + ...CommonEntrySchema, + field: schema.oneOf([ + schema.literal(ConditionEntryField.HASH), + schema.literal(ConditionEntryField.PATH), + schema.literal(ConditionEntryField.SIGNER), + ]), }); -const PathConditionEntrySchema = schema.object({ - field: schema.literal(ConditionEntryField.PATH), - type: ConditionEntryTypeSchema, - operator: ConditionEntryOperatorSchema, - value: schema.string({ minLength: 1 }), + +const LinuxEntrySchema = schema.object({ + ...CommonEntrySchema, }); -const SignerConditionEntrySchema = schema.object({ - field: schema.literal(ConditionEntryField.SIGNER), - type: ConditionEntryTypeSchema, - operator: ConditionEntryOperatorSchema, - value: schema.string({ minLength: 1 }), + +const MacEntrySchema = schema.object({ + ...CommonEntrySchema, }); -const createNewTrustedAppForOsScheme = <O extends OperatingSystem, E extends ConditionEntry>( - osSchema: Type<O>, - entriesSchema: Type<E> -) => - schema.object({ - name: schema.string({ minLength: 1, maxLength: 256 }), - description: schema.maybe(schema.string({ minLength: 0, maxLength: 256, defaultValue: '' })), - os: osSchema, - entries: schema.arrayOf(entriesSchema, { - minSize: 1, - validate(entries) { - return ( - getDuplicateFields(entries) - .map((field) => `[${entryFieldLabels[field]}] field can only be used once`) - .join(', ') || undefined - ); - }, - }), - }); +/* + * Entry Schema depending on Os type using schema.conditional. + * If OS === WINDOWS then use Windows schema, + * else if OS === LINUX then use Linux schema, + * else use Mac schema + */ +const EntrySchemaDependingOnOS = schema.conditional( + schema.siblingRef('os'), + OperatingSystem.WINDOWS, + WindowsEntrySchema, + schema.conditional( + schema.siblingRef('os'), + OperatingSystem.LINUX, + LinuxEntrySchema, + MacEntrySchema + ) +); + +/* + * Entities array schema. + * The validate function checks there is no duplicated entry inside the array + */ +const EntriesSchema = schema.arrayOf(EntrySchemaDependingOnOS, { + minSize: 1, + validate(entries) { + return ( + getDuplicateFields(entries) + .map((field) => `duplicatedEntry.${field}`) + .join(', ') || undefined + ); + }, +}); export const PostTrustedAppCreateRequestSchema = { - body: schema.oneOf([ - createNewTrustedAppForOsScheme( - schema.oneOf([schema.literal(OperatingSystem.LINUX), schema.literal(OperatingSystem.MAC)]), - schema.oneOf([HashConditionEntrySchema, PathConditionEntrySchema]) - ), - createNewTrustedAppForOsScheme( + body: schema.object({ + name: schema.string({ minLength: 1, maxLength: 256 }), + description: schema.maybe(schema.string({ minLength: 0, maxLength: 256, defaultValue: '' })), + os: schema.oneOf([ schema.literal(OperatingSystem.WINDOWS), - schema.oneOf([HashConditionEntrySchema, PathConditionEntrySchema, SignerConditionEntrySchema]) - ), - ]), + schema.literal(OperatingSystem.LINUX), + schema.literal(OperatingSystem.MAC), + ]), + entries: EntriesSchema, + }), }; diff --git a/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts b/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts index 3d9482a704206..a5c3c1eab52b3 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts @@ -27,8 +27,13 @@ export interface GetTrustedListAppsResponse { data: TrustedApp[]; } -/** API Request body for creating a new Trusted App entry */ -export type PostTrustedAppCreateRequest = TypeOf<typeof PostTrustedAppCreateRequestSchema.body>; +/* + * API Request body for creating a new Trusted App entry + * As this is an inferred type and the schema type doesn't match at all with the + * NewTrustedApp type it needs and overwrite from the MacosLinux/Windows custom types + */ +export type PostTrustedAppCreateRequest = TypeOf<typeof PostTrustedAppCreateRequestSchema.body> & + (MacosLinuxConditionEntries | WindowsConditionEntries); export interface PostTrustedAppCreateResponse { data: TrustedApp; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_flyout.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_flyout.tsx index b9b9cc563303d..1c87bf4304640 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_flyout.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_flyout.tsx @@ -18,7 +18,7 @@ import { EuiText, EuiTitle, } from '@elastic/eui'; -import React, { memo, useCallback, useEffect } from 'react'; +import React, { memo, useCallback, useEffect, useMemo } from 'react'; import { EuiFlyoutProps } from '@elastic/eui/src/components/flyout/flyout'; import { FormattedMessage } from '@kbn/i18n/react'; import { useDispatch } from 'react-redux'; @@ -31,7 +31,7 @@ import { } from '../../store/selectors'; import { AppAction } from '../../../../../common/store/actions'; import { useTrustedAppsSelector } from '../hooks'; -import { ABOUT_TRUSTED_APPS } from '../translations'; +import { ABOUT_TRUSTED_APPS, CREATE_TRUSTED_APP_ERROR } from '../translations'; type CreateTrustedAppFlyoutProps = Omit<EuiFlyoutProps, 'hideCloseButton'>; export const CreateTrustedAppFlyout = memo<CreateTrustedAppFlyoutProps>( @@ -45,6 +45,15 @@ export const CreateTrustedAppFlyout = memo<CreateTrustedAppFlyoutProps>( const dataTestSubj = flyoutProps['data-test-subj']; + const creationErrorsMessage = useMemo<string | undefined>( + () => + creationErrors + ? CREATE_TRUSTED_APP_ERROR[creationErrors.message.replace(/(\[(.*)\]\: )/, '')] || + creationErrors.message + : undefined, + [creationErrors] + ); + const getTestId = useCallback( (suffix: string): string | undefined => { if (dataTestSubj) { @@ -102,7 +111,7 @@ export const CreateTrustedAppFlyout = memo<CreateTrustedAppFlyoutProps>( fullWidth onChange={handleFormOnChange} isInvalid={!!creationErrors} - error={creationErrors?.message} + error={creationErrorsMessage} data-test-subj={getTestId('createForm')} /> </EuiFlyoutBody> diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts index b594c355a6983..fb26ee8621bcb 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts @@ -137,3 +137,36 @@ export const LIST_VIEW_TOGGLE_LABEL = i18n.translate( export const NO_RESULTS_MESSAGE = i18n.translate('xpack.securitySolution.trustedapps.noResults', { defaultMessage: 'No items found', }); + +export const CREATE_TRUSTED_APP_ERROR: { [K in string]: string } = { + [`duplicatedEntry.${ConditionEntryField.HASH}`]: i18n.translate( + 'xpack.securitySolution.trustedapps.logicalConditionBuilder.entry.field.error.duplicated.hash', + { defaultMessage: 'Hash value can only be used once. Please enter a single valid hash.' } + ), + [`duplicatedEntry.${ConditionEntryField.PATH}`]: i18n.translate( + 'xpack.securitySolution.trustedapps.logicalConditionBuilder.entry.field.error.duplicated.path', + { defaultMessage: 'Path value can only be used once. Please enter a single valid path.' } + ), + [`duplicatedEntry.${ConditionEntryField.SIGNER}`]: i18n.translate( + 'xpack.securitySolution.trustedapps.logicalConditionBuilder.entry.field.error.duplicated.signature', + { + defaultMessage: + 'Signature value can only be used once. Please enter a single valid signature.', + } + ), + [`invalidField.${ConditionEntryField.HASH}`]: i18n.translate( + 'xpack.securitySolution.trustedapps.logicalConditionBuilder.entry.field.error.invalid.hash', + { + defaultMessage: + 'An invalid Hash was entered. Please enter in a valid Hash (md5, sha1, or sha256).', + } + ), + [`invalidField.${ConditionEntryField.PATH}`]: i18n.translate( + 'xpack.securitySolution.trustedapps.logicalConditionBuilder.entry.field.error.invalid.path', + { defaultMessage: 'An invalid Path was entered. Please enter in a valid Path.' } + ), + [`invalidField.${ConditionEntryField.SIGNER}`]: i18n.translate( + 'xpack.securitySolution.trustedapps.logicalConditionBuilder.entry.field.error.invalid.signature', + { defaultMessage: 'An invalid Signature was entered. Please enter in a valid Signature.' } + ), +}; From 0c6bec1dd3b98bdefcf8b5078193726711afa4ff Mon Sep 17 00:00:00 2001 From: Matthew Kime <matt@mattki.me> Date: Mon, 22 Mar 2021 07:55:14 -0500 Subject: [PATCH 10/13] fix count and custom label on runtime fields (#95019) --- .../__snapshots__/index_patterns.test.ts.snap | 38 ++++++++++++++++- .../index_patterns/index_patterns.test.ts | 42 ++++++++++++------- .../index_patterns/index_patterns.ts | 3 +- 3 files changed, 64 insertions(+), 19 deletions(-) diff --git a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap index d6da4adac81a4..af9499bd7e263 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap +++ b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap @@ -1,16 +1,50 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`IndexPatterns correctly composes runtime field 1`] = ` +FldList [ + Object { + "aggregatable": true, + "conflictDescriptions": undefined, + "count": 5, + "customLabel": "A Runtime Field", + "esTypes": Array [ + "keyword", + ], + "lang": undefined, + "name": "aRuntimeField", + "readFromDocValues": false, + "script": undefined, + "scripted": false, + "searchable": true, + "subType": undefined, + "type": "string", + }, +] +`; + exports[`IndexPatterns savedObjectToSpec 1`] = ` Object { "allowNoIndex": undefined, - "fieldAttrs": Object {}, + "fieldAttrs": Object { + "aRuntimeField": Object { + "count": 5, + "customLabel": "A Runtime Field", + }, + }, "fieldFormats": Object { "field": Object {}, }, "fields": Object {}, "id": "id", "intervalName": undefined, - "runtimeFieldMap": Object {}, + "runtimeFieldMap": Object { + "aRuntimeField": Object { + "script": Object { + "source": "emit('hello')", + }, + "type": "keyword", + }, + }, "sourceFilters": Array [ Object { "value": "item1", diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.test.ts b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.test.ts index 0fd226108683d..a4f37334c212e 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.test.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.test.ts @@ -26,6 +26,25 @@ function setDocsourcePayload(id: string | null, providedPayload: any) { object = defaults(providedPayload || {}, stubbedSavedObjectIndexPattern(id)); } +const savedObject = { + id: 'id', + version: 'version', + attributes: { + title: 'kibana-*', + timeFieldName: '@timestamp', + fields: '[]', + sourceFilters: '[{"value":"item1"},{"value":"item2"}]', + fieldFormatMap: '{"field":{}}', + typeMeta: '{}', + type: '', + runtimeFieldMap: + '{"aRuntimeField": { "type": "keyword", "script": {"source": "emit(\'hello\')"}}}', + fieldAttrs: '{"aRuntimeField": { "count": 5, "customLabel": "A Runtime Field"}}', + }, + type: 'index-pattern', + references: [], +}; + describe('IndexPatterns', () => { let indexPatterns: IndexPatternsService; let savedObjectsClient: SavedObjectsClientCommon; @@ -219,23 +238,14 @@ describe('IndexPatterns', () => { }); test('savedObjectToSpec', () => { - const savedObject = { - id: 'id', - version: 'version', - attributes: { - title: 'kibana-*', - timeFieldName: '@timestamp', - fields: '[]', - sourceFilters: '[{"value":"item1"},{"value":"item2"}]', - fieldFormatMap: '{"field":{}}', - typeMeta: '{}', - type: '', - }, - type: 'index-pattern', - references: [], - }; + const spec = indexPatterns.savedObjectToSpec(savedObject); + expect(spec).toMatchSnapshot(); + }); - expect(indexPatterns.savedObjectToSpec(savedObject)).toMatchSnapshot(); + test('correctly composes runtime field', async () => { + setDocsourcePayload('id', savedObject); + const indexPattern = await indexPatterns.get('id'); + expect(indexPattern.fields).toMatchSnapshot(); }); test('failed requests are not cached', async () => { diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts index 2779409423604..805eccd1ee31b 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts @@ -425,8 +425,9 @@ export class IndexPatternsService { runtimeField: value, aggregatable: true, searchable: true, - count: 0, readFromDocValues: false, + customLabel: spec.fieldAttrs?.[key]?.customLabel, + count: spec.fieldAttrs?.[key]?.count, }; } } From 03764796ee6bcbd63e5105ba71101e835205f213 Mon Sep 17 00:00:00 2001 From: Josh Dover <1813008+joshdover@users.noreply.github.com> Date: Mon, 22 Mar 2021 14:36:14 +0100 Subject: [PATCH 11/13] Add ESS icon to server.publicBaseUrl docs (#93949) --- docs/setup/settings.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 9934f8508707c..a28a52834db39 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -475,7 +475,7 @@ running behind a proxy. Use the <<server-rewriteBasePath, `server.rewriteBasePat if it should remove the basePath from requests it receives, and to prevent a deprecation warning at startup. This setting cannot end in a slash (`/`). -|[[server-publicBaseUrl]] `server.publicBaseUrl:` +|[[server-publicBaseUrl]] `server.publicBaseUrl:` {ess-icon} | The publicly available URL that end-users access Kibana at. Must include the protocol, hostname, port (if different than the defaults for `http` and `https`, 80 and 443 respectively), and the <<server-basePath, `server.basePath`>> (if configured). This setting cannot end in a slash (`/`). From 35af8a941a68736d82d3c72fe06bb80775a4f828 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski <jon@budzenski.me> Date: Mon, 22 Mar 2021 08:42:30 -0500 Subject: [PATCH 12/13] skip telemetry app usage test. #94513 --- test/api_integration/apis/telemetry/telemetry_local.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/api_integration/apis/telemetry/telemetry_local.ts b/test/api_integration/apis/telemetry/telemetry_local.ts index 85d94cbf46dda..211f2eb85e4e3 100644 --- a/test/api_integration/apis/telemetry/telemetry_local.ts +++ b/test/api_integration/apis/telemetry/telemetry_local.ts @@ -260,7 +260,8 @@ export default function ({ getService }: FtrProviderContext) { }); }); - it("should only use the first 10k docs for the application_usage data (they'll be rolled up in a later process)", async () => { + // flaky https://github.com/elastic/kibana/issues/94513 + it.skip("should only use the first 10k docs for the application_usage data (they'll be rolled up in a later process)", async () => { const { body } = await supertest .post('/api/telemetry/v2/clusters/_stats') .set('kbn-xsrf', 'xxx') From 04e4661cbf33b0e1f8abd39edd5e9f177ab818de Mon Sep 17 00:00:00 2001 From: Angela Chuang <6295984+angorayc@users.noreply.github.com> Date: Mon, 22 Mar 2021 14:45:45 +0000 Subject: [PATCH 13/13] [Security Solution] fix data providers cypress tests (#94933) * fix data providers cypress tests * add scrollToBottom to common tasks file Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../timelines/data_providers.spec.ts | 49 ++++++------------- .../timelines/flyout_button.spec.ts | 20 +++----- .../cypress/screens/timeline.ts | 2 + .../security_solution/cypress/tasks/common.ts | 2 + .../cypress/tasks/hosts/all_hosts.ts | 17 +++++++ 5 files changed, 43 insertions(+), 47 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/timelines/data_providers.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timelines/data_providers.spec.ts index cb93007f19c9c..d42632a66eb26 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timelines/data_providers.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timelines/data_providers.spec.ts @@ -10,6 +10,7 @@ import { TIMELINE_DATA_PROVIDERS_EMPTY, TIMELINE_DROPPED_DATA_PROVIDERS, TIMELINE_DATA_PROVIDERS_ACTION_MENU, + IS_DRAGGING_DATA_PROVIDERS, TIMELINE_FLYOUT_HEADER, } from '../../screens/timeline'; import { HOSTS_NAMES_DRAGGABLE } from '../../screens/hosts/all_hosts'; @@ -17,6 +18,7 @@ import { HOSTS_NAMES_DRAGGABLE } from '../../screens/hosts/all_hosts'; import { dragAndDropFirstHostToTimeline, dragFirstHostToEmptyTimelineDataProviders, + unDragFirstHostToEmptyTimelineDataProviders, dragFirstHostToTimeline, waitForAllHostsToBeLoaded, } from '../../tasks/hosts/all_hosts'; @@ -26,13 +28,14 @@ import { openTimelineUsingToggle } from '../../tasks/security_main'; import { addDataProvider, closeTimeline, createNewTimeline } from '../../tasks/timeline'; import { HOSTS_URL } from '../../urls/navigation'; -import { cleanKibana } from '../../tasks/common'; +import { cleanKibana, scrollToBottom } from '../../tasks/common'; describe('timeline data providers', () => { before(() => { cleanKibana(); loginAndWaitForPage(HOSTS_URL); waitForAllHostsToBeLoaded(); + scrollToBottom(); }); afterEach(() => { @@ -74,44 +77,24 @@ describe('timeline data providers', () => { }); }); - it.skip('sets the background to euiColorSuccess with a 10% alpha channel when the user starts dragging a host, but is not hovering over the data providers', () => { + it('sets correct classes when the user starts dragging a host, but is not hovering over the data providers', () => { dragFirstHostToTimeline(); - if (Cypress.browser.name === 'firefox') { - cy.get(TIMELINE_DATA_PROVIDERS) - .filter(':visible') - .should('have.css', 'background-color', 'rgba(1, 125, 115, 0.1)'); - } else { - cy.get(TIMELINE_DATA_PROVIDERS) - .filter(':visible') - .should( - 'have.css', - 'background', - 'rgba(1, 125, 115, 0.1) none repeat scroll 0% 0% / auto padding-box border-box' - ); - } + cy.get(IS_DRAGGING_DATA_PROVIDERS) + .find(TIMELINE_DATA_PROVIDERS) + .filter(':visible') + .should('have.class', 'drop-target-data-providers'); }); - // https://github.com/elastic/kibana/issues/94576 - it.skip('sets the background to euiColorSuccess with a 20% alpha channel and renders the dashed border color as euiColorSuccess when the user starts dragging a host AND is hovering over the data providers', () => { + it('render an extra highlighted area in dataProvider when the user starts dragging a host AND is hovering over the data providers', () => { dragFirstHostToEmptyTimelineDataProviders(); - if (Cypress.browser.name === 'firefox') { - cy.get(TIMELINE_DATA_PROVIDERS_EMPTY) - .filter(':visible') - .should('have.css', 'background-color', 'rgba(1, 125, 115, 0.2)'); - } else { - cy.get(TIMELINE_DATA_PROVIDERS_EMPTY) - .filter(':visible') - .should( - 'have.css', - 'background', - 'rgba(1, 125, 115, 0.2) none repeat scroll 0% 0% / auto padding-box border-box' - ); + cy.get(IS_DRAGGING_DATA_PROVIDERS) + .find(TIMELINE_DATA_PROVIDERS_EMPTY) + .children() + .should('exist'); - cy.get(TIMELINE_DATA_PROVIDERS) - .filter(':visible') - .should('have.css', 'border', '3.1875px dashed rgb(1, 125, 115)'); - } + // Release the dragging item so the cursor can peform other action + unDragFirstHostToEmptyTimelineDataProviders(); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/timelines/flyout_button.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timelines/flyout_button.spec.ts index 6b6463803ee37..c7ec17d027e80 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timelines/flyout_button.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timelines/flyout_button.spec.ts @@ -8,6 +8,7 @@ import { TIMELINE_BOTTOM_BAR_TOGGLE_BUTTON } from '../../screens/security_main'; import { CREATE_NEW_TIMELINE, + IS_DRAGGING_DATA_PROVIDERS, TIMELINE_DATA_PROVIDERS, TIMELINE_FLYOUT_HEADER, TIMELINE_SETTINGS_ICON, @@ -76,21 +77,12 @@ describe('timeline flyout button', () => { closeTimelineUsingCloseButton(); }); - it.skip('sets the data providers background to euiColorSuccess with a 10% alpha channel when the user starts dragging a host, but is not hovering over the data providers area', () => { + it('sets correct classes when the user starts dragging a host, but is not hovering over the data providers', () => { dragFirstHostToTimeline(); - if (Cypress.browser.name === 'firefox') { - cy.get(TIMELINE_DATA_PROVIDERS) - .filter(':visible') - .should('have.css', 'background-color', 'rgba(1, 125, 115, 0.1)'); - } else { - cy.get(TIMELINE_DATA_PROVIDERS) - .filter(':visible') - .should( - 'have.css', - 'background', - 'rgba(1, 125, 115, 0.1) none repeat scroll 0% 0% / auto padding-box border-box' - ); - } + cy.get(IS_DRAGGING_DATA_PROVIDERS) + .find(TIMELINE_DATA_PROVIDERS) + .filter(':visible') + .should('have.class', 'drop-target-data-providers'); }); }); diff --git a/x-pack/plugins/security_solution/cypress/screens/timeline.ts b/x-pack/plugins/security_solution/cypress/screens/timeline.ts index 10a469a90fd51..4c80f266e687c 100644 --- a/x-pack/plugins/security_solution/cypress/screens/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/screens/timeline.ts @@ -114,6 +114,8 @@ export const TIMELINE_CHANGES_IN_PROGRESS = '[data-test-subj="timeline"] .euiPro export const TIMELINE_COLUMN_SPINNER = '[data-test-subj="timeline-loading-spinner"]'; +export const IS_DRAGGING_DATA_PROVIDERS = '.is-dragging'; + export const TIMELINE_DATA_PROVIDERS = '[data-test-subj="dataProviders"]'; export const TIMELINE_DATA_PROVIDERS_ACTION_MENU = '[data-test-subj="providerActions"]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/common.ts b/x-pack/plugins/security_solution/cypress/tasks/common.ts index 293cd8fbeaa85..468b0e22838dd 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/common.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/common.ts @@ -148,3 +148,5 @@ export const cleanKibana = () => { esArchiverResetKibana(); }; + +export const scrollToBottom = () => cy.scrollTo('bottom'); diff --git a/x-pack/plugins/security_solution/cypress/tasks/hosts/all_hosts.ts b/x-pack/plugins/security_solution/cypress/tasks/hosts/all_hosts.ts index 98e3d74ad3bc4..317a35708de57 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/hosts/all_hosts.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/hosts/all_hosts.ts @@ -29,6 +29,23 @@ export const dragFirstHostToEmptyTimelineDataProviders = () => { .then((dataProvidersDropArea) => dragWithoutDrop(dataProvidersDropArea)); }; +export const unDragFirstHostToEmptyTimelineDataProviders = () => { + cy.get(HOSTS_NAMES_DRAGGABLE) + .first() + .then((host) => { + cy.wrap(host) + .trigger('mousemove', { + button: 0, + clientX: host[0].getBoundingClientRect().left, + clientY: host[0].getBoundingClientRect().top, + force: true, + }) + .wait(300) + .trigger('mouseup', { force: true }) + .wait(300); + }); +}; + export const dragFirstHostToTimeline = () => { cy.get(HOSTS_NAMES_DRAGGABLE) .first()