From f64a639bcfa1fc2bc89ca7db268f594306edfd7c Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Tue, 16 Mar 2021 18:18:33 +0000 Subject: Add latest changes from gitlab-org/gitlab@13-10-stable-ee --- .../access_tokens/components/projects_field.vue | 69 +++ .../components/projects_token_selector.vue | 156 +++++ .../graphql/queries/get_projects.query.graphql | 28 + app/assets/javascripts/access_tokens/index.js | 62 +- .../components/usage_ping_disabled.vue | 53 -- .../admin/dev_ops_report/devops_adoption.js | 2 - .../dev_ops_report/devops_score_empty_state.js | 27 - app/assets/javascripts/admin/users/tabs.js | 11 +- .../components/alert_management_table.vue | 20 +- .../components/alert_mapping_builder.vue | 43 +- .../components/alerts_integrations_list.vue | 15 +- .../components/alerts_settings_form.vue | 636 +++++++++++---------- .../components/alerts_settings_wrapper.vue | 189 +++--- .../components/mocks/parsedMapping.json | 95 --- .../javascripts/alerts_settings/constants.js | 152 +++-- app/assets/javascripts/alerts_settings/graphql.js | 18 +- .../http_integration_item.fragment.graphql | 7 + .../http_integration_payload_data.fragment.graphql | 3 + .../create_http_integration.mutation.graphql | 22 +- .../destroy_http_integration.mutation.graphql | 4 +- .../mutations/reset_http_token.mutation.graphql | 4 +- ...pdate_current_http_integration.mutation.graphql | 25 + .../update_current_intergration.mutation.graphql | 19 - ...current_prometheus_integration.mutation.graphql | 21 + .../update_http_integration.mutation.graphql | 4 +- .../queries/get_http_integrations.query.graphql | 12 + .../queries/parse_sample_payload.query.graphql | 9 + app/assets/javascripts/alerts_settings/index.js | 5 +- .../alerts_settings/utils/cache_updates.js | 35 +- .../alerts_settings/utils/error_messages.js | 2 +- .../utils/mapping_transformations.js | 28 +- .../components/usage_ping_disabled.vue | 53 ++ .../devops_report/devops_score_empty_state.js | 27 + .../instance_statistics/components/app.vue | 48 -- .../components/charts_config.js | 87 --- .../components/instance_counts.vue | 64 --- .../components/instance_statistics_count_chart.vue | 206 ------- .../components/projects_and_groups_chart.vue | 224 -------- .../instance_statistics/components/users_chart.vue | 143 ----- .../analytics/instance_statistics/constants.js | 5 - .../graphql/fragments/count.fragment.graphql | 4 - .../graphql/queries/groups.query.graphql | 13 - .../graphql/queries/instance_count.query.graphql | 13 - .../instance_statistics_count.query.graphql | 34 -- .../graphql/queries/projects.query.graphql | 13 - .../graphql/queries/users.query.graphql | 13 - .../analytics/instance_statistics/index.js | 24 - .../analytics/instance_statistics/utils.js | 68 --- .../analytics/usage_trends/components/app.vue | 41 ++ .../usage_trends/components/charts_config.js | 106 ++++ .../usage_trends/components/usage_counts.vue | 63 ++ .../components/usage_trends_count_chart.vue | 206 +++++++ .../usage_trends/components/users_chart.vue | 143 +++++ .../analytics/usage_trends/constants.js | 5 + .../graphql/fragments/count.fragment.graphql | 4 + .../graphql/queries/usage_count.query.graphql | 13 + .../queries/usage_trends_count.query.graphql | 34 ++ .../graphql/queries/users.query.graphql | 13 + .../javascripts/analytics/usage_trends/index.js | 24 + .../javascripts/analytics/usage_trends/utils.js | 68 +++ app/assets/javascripts/api.js | 24 +- .../two_factor_auth/components/recovery_codes.vue | 2 +- app/assets/javascripts/awards_handler.js | 9 +- app/assets/javascripts/badges/components/badge.vue | 2 +- .../javascripts/badges/components/badge_form.vue | 4 +- .../batch_comments/components/publish_button.vue | 3 +- .../batch_comments/components/review_bar.vue | 5 +- .../stores/modules/batch_comments/actions.js | 22 +- .../javascripts/behaviors/copy_to_clipboard.js | 38 +- .../javascripts/behaviors/markdown/render_math.js | 99 ++-- .../javascripts/behaviors/shortcuts/keybindings.js | 135 ++--- .../javascripts/behaviors/toggler_behavior.js | 2 +- app/assets/javascripts/blob/blob_file_dropzone.js | 1 + app/assets/javascripts/blob/viewer/index.js | 9 +- app/assets/javascripts/boards/boards_util.js | 28 +- .../boards/components/board_add_new_column.vue | 143 +++++ .../components/board_add_new_column_form.vue | 131 +++++ .../components/board_add_new_column_trigger.vue | 2 +- .../javascripts/boards/components/board_card.vue | 83 +-- .../boards/components/board_card_deprecated.vue | 61 ++ .../boards/components/board_card_inner.vue | 268 +++++++++ .../boards/components/board_card_layout.vue | 98 ---- .../components/board_card_layout_deprecated.vue | 3 +- .../javascripts/boards/components/board_column.vue | 21 +- .../boards/components/board_content.vue | 31 +- .../javascripts/boards/components/board_form.vue | 75 ++- .../javascripts/boards/components/board_list.vue | 91 +-- .../boards/components/board_list_deprecated.vue | 2 +- .../boards/components/board_list_header.vue | 51 +- .../components/board_list_header_deprecated.vue | 4 +- .../boards/components/board_new_issue.vue | 7 +- .../components/board_new_issue_deprecated.vue | 6 +- .../javascripts/boards/components/board_sidebar.js | 6 +- .../boards/components/boards_selector.vue | 43 +- .../components/boards_selector_deprecated.vue | 7 +- .../boards/components/config_toggle.vue | 64 +++ .../boards/components/filtered_search.vue | 54 ++ .../boards/components/issue_card_inner.vue | 262 --------- .../components/issue_card_inner_deprecated.vue | 6 +- .../javascripts/boards/components/issue_count.vue | 36 -- .../boards/components/issue_due_date.vue | 6 +- .../boards/components/issue_time_estimate.vue | 2 +- .../javascripts/boards/components/item_count.vue | 36 ++ .../javascripts/boards/components/modal/list.vue | 6 +- .../boards/components/project_select.vue | 11 +- .../components/project_select_deprecated.vue | 1 + .../sidebar/board_sidebar_subscription.vue | 2 + .../boards/components/sidebar/remove_issue.vue | 88 --- app/assets/javascripts/boards/config_toggle.js | 25 +- app/assets/javascripts/boards/constants.js | 5 + app/assets/javascripts/boards/filtered_search.js | 25 + .../boards/graphql/board_labels.query.graphql | 4 +- .../graphql/board_list_create.mutation.graphql | 20 +- .../boards/graphql/group_projects.query.graphql | 1 + .../boards/graphql/users_search.query.graphql | 11 - app/assets/javascripts/boards/index.js | 29 +- .../javascripts/boards/mixins/board_card_inner.js | 8 + .../javascripts/boards/mixins/issue_card_inner.js | 10 - .../boards/mount_multiple_boards_switcher.js | 6 +- app/assets/javascripts/boards/stores/actions.js | 107 +++- .../javascripts/boards/stores/boards_store.js | 4 +- app/assets/javascripts/boards/stores/getters.js | 24 +- .../javascripts/boards/stores/mutation_types.js | 10 +- app/assets/javascripts/boards/stores/mutations.js | 113 ++-- app/assets/javascripts/boards/stores/state.js | 14 +- app/assets/javascripts/captcha/captcha_modal.vue | 10 +- .../captcha/captcha_modal_axios_interceptor.js | 37 ++ .../javascripts/captcha/unsolved_captcha_error.js | 10 + .../captcha/wait_for_captcha_to_be_solved.js | 53 ++ .../components/ci_environments_dropdown.vue | 12 +- .../components/ci_variable_modal.vue | 1 - .../components/ci_variable_table.vue | 2 +- .../components/fluentd_output_settings.vue | 20 +- .../components/ingress_modsecurity_settings.vue | 22 +- .../clusters/forms/components/integration_form.vue | 14 +- .../javascripts/clusters_list/store/actions.js | 2 +- .../commit/pipelines/pipelines_bundle.js | 2 - .../commit/pipelines/pipelines_table.vue | 18 +- app/assets/javascripts/commons/bootstrap.js | 67 --- app/assets/javascripts/commons/vue.js | 2 + .../create_cluster/eks_cluster/constants.js | 5 +- .../cycle_analytics/components/base.vue | 288 ++++++++++ .../cycle_analytics/cycle_analytics_bundle.js | 156 ----- app/assets/javascripts/cycle_analytics/index.js | 32 ++ .../deprecated_jquery_dropdown/gl_dropdown.js | 43 +- .../design_management/components/delete_button.vue | 16 +- .../components/design_destroyer.vue | 1 + .../components/design_notes/design_discussion.vue | 6 +- .../design_management/components/list/item.vue | 1 + .../design_management/components/toolbar/index.vue | 2 +- .../design_management/components/upload/button.vue | 2 +- .../javascripts/design_management/graphql.js | 1 - .../javascripts/design_management/pages/index.vue | 5 +- .../design_management/utils/cache_update.js | 3 - app/assets/javascripts/diffs/components/app.vue | 14 +- .../diffs/components/diff_discussion_reply.vue | 5 +- .../javascripts/diffs/components/diff_file.vue | 14 +- .../diffs/components/diff_file_header.vue | 6 +- .../diffs/components/inline_diff_table_row.vue | 6 +- .../diffs/components/settings_dropdown.vue | 46 +- app/assets/javascripts/diffs/constants.js | 1 - app/assets/javascripts/diffs/i18n.js | 1 + app/assets/javascripts/diffs/index.js | 2 +- app/assets/javascripts/diffs/store/actions.js | 5 - app/assets/javascripts/diffs/store/utils.js | 2 +- app/assets/javascripts/diffs/utils/file_reviews.js | 7 +- app/assets/javascripts/diffs/utils/preferences.js | 13 +- app/assets/javascripts/editor/constants.js | 3 + .../editor/extensions/editor_lite_webide_ext.js | 164 ++++++ .../javascripts/emoji/components/category.vue | 61 ++ .../javascripts/emoji/components/emoji_group.vue | 35 ++ .../javascripts/emoji/components/emoji_list.vue | 44 ++ app/assets/javascripts/emoji/components/picker.vue | 121 ++++ app/assets/javascripts/emoji/components/utils.js | 27 + app/assets/javascripts/emoji/constants.js | 14 + app/assets/javascripts/emoji/index.js | 16 +- .../environments/components/environments_app.vue | 6 +- .../mixins/environments_pagination_api_mixin.js | 2 + .../environments/services/environments_service.js | 4 +- .../javascripts/experimentation/constants.js | 1 + .../experimentation/experiment_tracking.js | 24 + app/assets/javascripts/experimentation/utils.js | 10 + .../components/configure_feature_flags_modal.vue | 13 +- .../feature_flags/components/edit_feature_flag.vue | 2 + .../feature_flags/components/feature_flags.vue | 8 +- .../components/feature_flags_table.vue | 7 +- app/assets/javascripts/flash.js | 2 +- app/assets/javascripts/gfm_auto_complete.js | 78 ++- .../components/grafana_integration.vue | 26 +- .../fragments/alert.fragment.graphql | 6 +- .../graphql_shared/fragments/epic.fragment.graphql | 1 + .../project_user_members_search.query.graphql | 14 + .../queries/users_search.query.graphql | 12 +- app/assets/javascripts/graphql_shared/utils.js | 34 ++ .../javascripts/groups/components/group_item.vue | 43 +- .../groups/components/invite_members_banner.vue | 4 +- .../javascripts/groups/components/item_caret.vue | 4 +- app/assets/javascripts/groups_select.js | 30 +- .../javascripts/helpers/cve_id_request_helper.js | 50 ++ .../javascripts/ide/components/branches/item.vue | 2 +- .../ide/components/branches/search_list.vue | 7 +- .../ide/components/commit_sidebar/form.vue | 18 +- .../commit_sidebar/new_merge_request_option.vue | 1 - .../ide/components/file_templates/bar.vue | 4 +- app/assets/javascripts/ide/components/ide.vue | 13 +- .../ide/components/merge_requests/item.vue | 2 +- .../ide/components/merge_requests/list.vue | 9 +- app/assets/javascripts/ide/components/nav_form.vue | 25 +- .../javascripts/ide/components/pipelines/list.vue | 3 +- .../ide/components/preview/navigator.vue | 2 +- .../javascripts/ide/components/repo_editor.vue | 81 ++- app/assets/javascripts/ide/components/repo_tab.vue | 46 +- .../javascripts/ide/components/repo_tabs.vue | 6 +- .../ide/components/shared/tokened_input.vue | 4 +- app/assets/javascripts/ide/constants.js | 1 + app/assets/javascripts/ide/index.js | 1 - app/assets/javascripts/ide/messages.js | 17 + .../ide/queries/getUserPermissions.query.graphql | 9 - .../ide/queries/get_ide_project.query.graphql | 7 + .../ide/queries/ide_project.fragment.graphql | 7 + app/assets/javascripts/ide/services/index.js | 4 +- app/assets/javascripts/ide/stores/getters.js | 44 +- .../import_entities/components/import_status.vue | 2 +- .../javascripts/import_entities/constants.js | 6 + .../import_groups/components/import_table.vue | 92 ++- .../import_groups/components/import_table_row.vue | 147 +++-- .../import_groups/graphql/client_factory.js | 78 ++- .../graphql/queries/group.query.graphql | 5 + .../graphql/services/source_groups_manager.js | 83 ++- .../graphql/services/status_poller.js | 12 +- .../import_entities/import_groups/index.js | 3 + .../components/provider_repo_table_row.vue | 10 +- .../javascripts/incidents_settings/constants.js | 4 +- .../edit/components/active_checkbox.vue | 2 +- .../edit/components/confirmation_modal.vue | 2 +- .../edit/components/integration_form.vue | 17 +- .../invite_members/components/group_select.vue | 103 ++++ .../components/invite_group_trigger.vue | 34 ++ .../components/invite_members_modal.vue | 148 +++-- .../components/invite_members_trigger.vue | 29 +- .../components/members_token_select.vue | 4 +- app/assets/javascripts/invite_members/constants.js | 2 +- .../invite_members/init_invite_group_trigger.js | 20 + .../invite_members/init_invite_members_form.js | 7 + .../invite_members/init_invite_members_modal.js | 1 + .../issuable/components/csv_export_modal.vue | 100 ++++ .../components/csv_import_export_buttons.vue | 107 ++++ .../issuable/components/csv_import_modal.vue | 86 +++ app/assets/javascripts/issuable/constants.js | 6 + .../issuable/init_csv_import_export_buttons.js | 45 ++ app/assets/javascripts/issuable_form.js | 6 +- .../issuable_list/components/issuable_item.vue | 65 ++- .../components/issuable_list_root.vue | 11 +- .../issuable_show/components/issuable_body.vue | 70 ++- .../components/issuable_description.vue | 23 +- .../components/issuable_discussion.vue | 15 + .../issuable_show/components/issuable_header.vue | 32 +- .../components/issuable_show_root.vue | 36 ++ app/assets/javascripts/issue.js | 9 +- .../javascripts/issue_show/components/app.vue | 51 +- .../issue_show/components/description.vue | 15 +- .../issue_show/components/edit_actions.vue | 16 +- .../components/fields/description_template.vue | 4 +- .../javascripts/issue_show/components/form.vue | 6 +- .../issue_show/components/header_actions.vue | 3 +- .../javascripts/issue_show/services/index.js | 2 + app/assets/javascripts/issue_show/stores/index.js | 2 +- .../javascripts/issue_show/utils/parse_data.js | 2 +- .../issues_list/components/issuable.vue | 31 +- .../components/issue_card_time_info.vue | 124 ++++ .../issues_list/components/issues_list_app.vue | 143 +++++ .../components/jira_issues_import_status_app.vue | 112 ++++ .../components/jira_issues_list_root.vue | 112 ---- app/assets/javascripts/issues_list/index.js | 42 +- .../javascripts/issues_list/service_desk_helper.js | 30 +- .../javascripts/jira_connect/components/app.vue | 48 +- .../jira_connect/components/groups_list_item.vue | 11 + app/assets/javascripts/jira_connect/constants.js | 1 + app/assets/javascripts/jira_connect/index.js | 5 +- .../jira_connect/store/mutation_types.js | 2 +- .../javascripts/jira_connect/store/mutations.js | 6 +- app/assets/javascripts/jira_connect/store/state.js | 2 +- app/assets/javascripts/jira_connect/utils.js | 33 ++ .../javascripts/jira_import/utils/cache_update.js | 3 +- .../jobs/components/artifacts_block.vue | 2 +- .../javascripts/jobs/components/commit_block.vue | 11 +- .../jobs/components/job_container_item.vue | 2 +- .../jobs/components/job_log_controllers.vue | 15 +- .../jobs/components/job_sidebar_retry_button.vue | 4 +- app/assets/javascripts/jobs/components/sidebar.vue | 74 ++- .../components/sidebar_job_details_container.vue | 2 +- .../jobs/components/stages_dropdown.vue | 2 +- .../javascripts/jobs/components/trigger_block.vue | 2 +- app/assets/javascripts/jobs/store/getters.js | 2 +- app/assets/javascripts/jobs/svg/scroll_down.svg | 4 - app/assets/javascripts/lib/chrome_84_icon_fix.js | 78 --- app/assets/javascripts/lib/graphql.js | 3 +- .../javascripts/lib/utils/datetime_utility.js | 107 ++++ .../javascripts/lib/utils/experimentation.js | 3 - app/assets/javascripts/lib/utils/http_status.js | 1 + app/assets/javascripts/lib/utils/number_utils.js | 21 + app/assets/javascripts/lib/utils/select2_utils.js | 25 + app/assets/javascripts/lib/utils/text_markdown.js | 6 +- .../lib/utils/unit_format/formatter_factory.js | 70 +-- .../javascripts/lib/utils/unit_format/index.js | 342 ++++++----- app/assets/javascripts/lib/utils/url_utility.js | 5 +- app/assets/javascripts/locale/index.js | 20 + app/assets/javascripts/main.js | 2 +- app/assets/javascripts/members.js | 111 ---- .../members/components/avatars/user_avatar.vue | 8 +- app/assets/javascripts/members/utils.js | 4 +- .../merge_conflicts/components/diff_file_editor.js | 115 ---- .../components/diff_file_editor.vue | 128 +++++ .../components/inline_conflict_lines.js | 22 - .../components/inline_conflict_lines.vue | 47 ++ .../components/parallel_conflict_lines.js | 37 -- .../components/parallel_conflict_lines.vue | 47 ++ .../javascripts/merge_conflicts/constants.js | 20 + .../merge_conflict_resolver_app.vue | 217 +++++++ .../merge_conflicts/merge_conflicts_bundle.js | 26 +- .../javascripts/merge_conflicts/store/actions.js | 120 ++++ .../javascripts/merge_conflicts/store/getters.js | 117 ++++ .../javascripts/merge_conflicts/store/index.js | 16 + .../merge_conflicts/store/mutation_types.js | 8 + .../javascripts/merge_conflicts/store/mutations.js | 40 ++ .../javascripts/merge_conflicts/store/state.js | 13 + app/assets/javascripts/merge_conflicts/utils.js | 228 ++++++++ app/assets/javascripts/merge_request_tabs.js | 2 - app/assets/javascripts/milestone.js | 27 - app/assets/javascripts/milestone_select.js | 2 +- .../monitoring/components/charts/anomaly.vue | 1 - .../javascripts/monitoring/stores/actions.js | 2 +- .../monitoring/stores/embed_group/index.js | 2 - app/assets/javascripts/network/branch_graph.js | 8 +- .../javascripts/notes/components/comment_form.vue | 208 ++++--- .../notes/components/discussion_actions.vue | 4 +- .../discussion_notes_replies_wrapper.vue | 2 +- .../components/discussion_reply_placeholder.vue | 29 +- .../javascripts/notes/components/note_actions.vue | 92 ++- .../javascripts/notes/components/note_form.vue | 4 +- .../javascripts/notes/components/noteable_note.vue | 9 +- .../javascripts/notes/components/notes_app.vue | 3 + .../notes/components/sidebar_subscription.vue | 58 ++ app/assets/javascripts/notes/i18n.js | 26 + app/assets/javascripts/notes/stores/actions.js | 44 +- app/assets/javascripts/notes/stores/utils.js | 4 +- .../components/custom_notifications_modal.vue | 17 +- .../components/notifications_dropdown.vue | 71 +-- .../components/notifications_dropdown_item.vue | 8 +- app/assets/javascripts/notifications/constants.js | 5 +- app/assets/javascripts/notifications_dropdown.js | 35 -- app/assets/javascripts/notifications_form.js | 48 -- .../details/components/composer_installation.vue | 5 +- .../details/components/conan_installation.vue | 5 +- .../details/components/installation_title.vue | 38 ++ .../details/components/maven_installation.vue | 128 +++-- .../details/components/npm_installation.vue | 5 +- .../details/components/nuget_installation.vue | 6 +- .../details/components/pypi_installation.vue | 5 +- .../javascripts/packages/details/constants.js | 3 + .../javascripts/packages/details/store/getters.js | 16 + app/assets/javascripts/packages/list/constants.js | 2 +- .../shared/components/package_list_row.vue | 2 +- app/assets/javascripts/packages/shared/utils.js | 2 +- .../group/components/group_settings_app.vue | 5 +- .../settings/group/components/maven_settings.vue | 3 +- .../settings/group/graphql/utils/cache_update.js | 1 - .../javascripts/pages/admin/abuse_reports/index.js | 7 +- app/assets/javascripts/pages/admin/admin.js | 32 -- .../admin/application_settings/general/index.js | 26 +- .../pages/admin/application_settings/index.js | 18 +- .../pages/admin/dev_ops_report/index.js | 4 +- .../pages/admin/impersonation_tokens/index.js | 4 +- app/assets/javascripts/pages/admin/index.js | 10 +- .../pages/admin/instance_statistics/index.js | 3 - .../javascripts/pages/admin/projects/index.js | 12 +- .../pages/admin/projects/index/index.js | 4 +- .../javascripts/pages/admin/runners/index.js | 14 +- .../pages/admin/services/index/index.js | 6 +- .../javascripts/pages/admin/usage_trends/index.js | 3 + .../javascripts/pages/dashboard/issues/index.js | 16 +- .../pages/dashboard/milestones/show/index.js | 2 - .../pages/dashboard/projects/index/index.js | 7 +- .../pages/dashboard/todos/index/index.js | 2 +- .../javascripts/pages/groups/activity/index.js | 3 +- app/assets/javascripts/pages/groups/edit/index.js | 3 + .../pages/groups/group_members/index.js | 9 +- .../pages/groups/milestones/show/index.js | 2 - app/assets/javascripts/pages/groups/new/index.js | 14 + .../pages/groups/settings/ci_cd/show/index.js | 27 +- .../groups/settings/integrations/edit/index.js | 16 +- .../settings/packages_and_registries/index.js | 3 + .../pages/groups/settings/repository/show/index.js | 11 +- .../pages/groups/shared/group_details.js | 13 +- app/assets/javascripts/pages/groups/show/index.js | 6 +- app/assets/javascripts/pages/profiles/index.js | 28 +- .../javascripts/pages/profiles/index/index.js | 7 - .../pages/profiles/notifications/show/index.js | 4 - .../pages/profiles/personal_access_tokens/index.js | 5 +- .../javascripts/pages/projects/blob/edit/index.js | 2 +- .../javascripts/pages/projects/blob/new/index.js | 2 +- .../javascripts/pages/projects/blob/show/index.js | 98 ++-- .../javascripts/pages/projects/boards/index.js | 8 +- .../pages/projects/branches/index/index.js | 8 +- .../pages/projects/branches/new/index.js | 11 +- .../pages/projects/compare/show/index.js | 3 + .../pages/projects/cycle_analytics/show/index.js | 2 +- .../feature_flags_user_lists/edit/index.js | 22 +- .../projects/feature_flags_user_lists/new/index.js | 28 +- .../feature_flags_user_lists/show/index.js | 19 +- .../pages/projects/forks/new/components/app.vue | 72 +++ .../projects/forks/new/components/fork_form.vue | 304 ++++++++++ .../forks/new/components/fork_groups_list_item.vue | 20 +- .../javascripts/pages/projects/forks/new/index.js | 48 +- .../pages/projects/imports/show/index.js | 4 +- .../pages/projects/issues/index/index.js | 5 +- .../javascripts/pages/projects/jobs/index/index.js | 30 +- .../pages/projects/labels/edit/index.js | 2 +- .../learn_gitlab/components/learn_gitlab_a.vue | 8 +- .../learn_gitlab/components/learn_gitlab_b.vue | 109 +++- .../components/learn_gitlab_info_card.vue | 70 +++ .../pages/projects/learn_gitlab/constants/index.js | 56 +- .../javascripts/pages/projects/logs/index.js | 2 +- .../pages/projects/merge_requests/edit/index.js | 6 +- .../pages/projects/merge_requests/index/index.js | 2 + .../pages/projects/milestones/edit/index.js | 4 +- .../pages/projects/milestones/index/index.js | 2 +- .../pages/projects/milestones/new/index.js | 2 +- .../pages/projects/milestones/show/index.js | 6 +- app/assets/javascripts/pages/projects/new/index.js | 42 +- .../pages/projects/project_members/index.js | 110 ++-- .../projects/prometheus/metrics/edit/index.js | 4 +- .../pages/projects/prometheus/metrics/new/index.js | 4 +- .../pages/projects/settings/access_tokens/index.js | 2 +- .../pages/projects/settings/ci_cd/show/index.js | 3 + .../projects/settings/operations/show/index.js | 5 + .../projects/settings/repository/show/index.js | 3 + .../permissions/components/settings_panel.vue | 42 +- .../pages/projects/shared/permissions/constants.js | 6 +- .../javascripts/pages/projects/show/index.js | 22 +- .../javascripts/pages/projects/tags/new/index.js | 8 +- .../pages/projects/tags/releases/index.js | 6 +- app/assets/javascripts/pages/search/show/index.js | 4 +- .../pages/shared/wikis/components/wiki_alert.vue | 31 + app/assets/javascripts/pages/shared/wikis/index.js | 28 +- app/assets/javascripts/pages/users/index.js | 10 +- app/assets/javascripts/performance/constants.js | 21 + .../performance_bar/components/detailed_metric.vue | 2 +- .../components/performance_bar_app.vue | 9 +- app/assets/javascripts/performance_bar/index.js | 2 + .../components/commit/commit_form.vue | 16 +- .../components/commit/commit_section.vue | 34 +- .../components/editor/ci_config_merged_preview.vue | 2 +- .../components/header/pipeline_editor_header.vue | 47 +- .../components/header/pipeline_status.vue | 120 ++++ .../components/header/validation_segment.vue | 18 +- .../components/ui/pipeline_editor_empty_state.vue | 56 ++ .../javascripts/pipeline_editor/constants.js | 4 +- .../mutations/commit_ci_file.mutation.graphql | 3 +- .../graphql/queries/client/current_branch.graphql | 3 + .../queries/client/is_new_ci_config_file.graphql | 3 + .../graphql/queries/client/pipeline.graphql | 17 + .../pipeline_editor/graphql/resolvers.js | 23 + app/assets/javascripts/pipeline_editor/index.js | 4 + .../pipeline_editor/pipeline_editor_app.vue | 91 +-- .../pipeline_editor/pipeline_editor_home.vue | 1 + .../pipeline_new/components/pipeline_new_form.vue | 189 +++--- .../pipeline_new/components/refs_dropdown.vue | 113 ++++ app/assets/javascripts/pipeline_new/constants.js | 1 + app/assets/javascripts/pipeline_new/index.js | 14 +- .../pipelines/components/graph/graph_component.vue | 29 +- .../components/graph/graph_component_wrapper.vue | 71 ++- .../components/graph/linked_pipelines_column.vue | 35 +- .../pipelines/components/graph/utils.js | 97 +++- .../pipelines/components/graph_shared/api.js | 8 + .../components/graph_shared/links_inner.vue | 74 ++- .../components/graph_shared/links_layer.vue | 115 +++- .../components/pipeline_graph/pipeline_graph.vue | 15 +- .../components/pipelines_list/empty_state.vue | 71 +-- .../pipelines_list/pipeline_mini_graph.vue | 54 ++ .../pipelines_list/pipeline_operations.vue | 119 ++++ .../components/pipelines_list/pipeline_stage.vue | 152 +++++ .../pipelines_list/pipeline_triggerer.vue | 12 +- .../components/pipelines_list/pipeline_url.vue | 92 +-- .../components/pipelines_list/pipelines.vue | 10 - .../pipelines_list/pipelines_actions.vue | 102 ---- .../pipelines_list/pipelines_artifacts.vue | 2 + .../components/pipelines_list/pipelines_commit.vue | 85 +++ .../pipelines_list/pipelines_manual_actions.vue | 103 ++++ .../pipelines_list/pipelines_status_badge.vue | 37 ++ .../components/pipelines_list/pipelines_table.vue | 212 +++++-- .../pipelines_list/pipelines_table_row.vue | 61 +- .../pipelines/components/pipelines_list/stage.vue | 234 -------- .../components/pipelines_list/time_ago.vue | 49 +- app/assets/javascripts/pipelines/constants.js | 3 +- .../pipelines/pipeline_details_bundle.js | 3 +- .../pipelines/pipeline_details_graph.js | 9 +- .../javascripts/pipelines/pipelines_index.js | 4 - .../profile/account/components/update_username.vue | 8 +- .../preferences/components/profile_preferences.vue | 26 +- app/assets/javascripts/profile/profile.js | 1 + .../commit/components/branches_dropdown.vue | 23 +- .../projects/commit/components/form_modal.vue | 30 +- .../commit/components/projects_dropdown.vue | 87 +++ .../javascripts/projects/commit/constants.js | 13 +- .../commit/init_cherry_pick_commit_modal.js | 8 +- .../javascripts/projects/commit/store/actions.js | 20 +- .../javascripts/projects/commit/store/getters.js | 2 + .../projects/commit/store/mutation_types.js | 3 + .../javascripts/projects/commit/store/mutations.js | 9 + .../javascripts/projects/commit/store/state.js | 3 + .../javascripts/projects/commit_box/info/index.js | 14 +- .../info/init_commit_pipeline_mini_graph.js | 26 + .../javascripts/projects/commits/store/actions.js | 2 +- .../projects/compare/components/app.vue | 79 +-- .../projects/compare/components/app_legacy.vue | 89 +++ .../projects/compare/components/repo_dropdown.vue | 93 +++ .../projects/compare/components/revision_card.vue | 65 +++ .../compare/components/revision_dropdown.vue | 115 ++-- .../components/revision_dropdown_legacy.vue | 145 +++++ app/assets/javascripts/projects/compare/index.js | 42 +- .../javascripts/projects/details/upload_button.vue | 59 ++ .../components/new_project_push_tip_popover.vue | 66 +++ .../components/welcome.vue | 14 +- .../experiment_new_project_creation/index.js | 6 + .../feature_flags_user_lists/show/index.js | 23 + .../charts/components/pipeline_charts.vue | 6 +- app/assets/javascripts/projects/project_new.js | 35 -- .../components/service_desk_root.vue | 2 +- .../components/service_desk_setting.vue | 12 +- .../javascripts/projects/upload_file_experiment.js | 33 ++ .../projects/upload_file_experiment_tracking.js | 9 + .../protected_branches/protected_branch_create.js | 7 + .../protected_branches/protected_branch_edit.js | 21 +- .../ref/components/ref_results_section.vue | 8 +- .../javascripts/ref/components/ref_selector.vue | 239 +++++--- app/assets/javascripts/ref/constants.js | 5 + app/assets/javascripts/ref/stores/actions.js | 17 +- .../javascripts/ref/stores/mutation_types.js | 2 + app/assets/javascripts/ref/stores/mutations.js | 3 + app/assets/javascripts/ref/stores/state.js | 25 +- .../registry/explorer/components/delete_button.vue | 1 + .../registry/explorer/components/delete_image.vue | 1 - .../components/details_page/details_header.vue | 32 +- .../components/details_page/tags_list_row.vue | 1 + .../explorer/components/list_page/image_list.vue | 1 - .../components/list_page/image_list_row.vue | 7 +- .../components/list_page/registry_header.vue | 19 +- .../registry/explorer/constants/common.js | 3 + .../registry/explorer/constants/details.js | 8 +- .../explorer/constants/expiration_policies.js | 3 - .../registry/explorer/constants/index.js | 1 + .../registry/explorer/pages/details.vue | 7 +- .../javascripts/registry/explorer/pages/list.vue | 1 - .../settings/components/expiration_toggle.vue | 12 +- .../settings/graphql/utils/cache_update.js | 1 - .../components/related_issuable_input.vue | 1 + .../releases/components/app_edit_new.vue | 11 +- .../releases/components/asset_links_form.vue | 3 + .../releases/components/tag_field_new.vue | 70 ++- .../reports/components/grouped_issues_list.vue | 13 + .../components/grouped_test_reports_app.vue | 183 ------ .../javascripts/reports/components/issue_body.js | 2 +- .../javascripts/reports/components/issues_list.vue | 13 + .../javascripts/reports/components/modal.vue | 71 --- .../reports/components/report_section.vue | 5 +- .../javascripts/reports/components/summary_row.vue | 32 +- .../reports/components/test_issue_body.vue | 64 --- .../grouped_test_report/components/modal.vue | 71 +++ .../components/test_issue_body.vue | 64 +++ .../grouped_test_reports_app.vue | 201 +++++++ .../reports/grouped_test_report/store/actions.js | 82 +++ .../reports/grouped_test_report/store/getters.js | 13 + .../reports/grouped_test_report/store/index.js | 17 + .../grouped_test_report/store/mutation_types.js | 7 + .../reports/grouped_test_report/store/mutations.js | 70 +++ .../reports/grouped_test_report/store/state.js | 71 +++ .../reports/grouped_test_report/store/utils.js | 102 ++++ app/assets/javascripts/reports/store/actions.js | 82 --- app/assets/javascripts/reports/store/getters.js | 13 - app/assets/javascripts/reports/store/index.js | 17 - .../javascripts/reports/store/mutation_types.js | 7 - app/assets/javascripts/reports/store/mutations.js | 70 --- app/assets/javascripts/reports/store/state.js | 71 --- app/assets/javascripts/reports/store/utils.js | 102 ---- .../repository/components/upload_blob_modal.vue | 220 +++++++ app/assets/javascripts/repository/index.js | 19 +- app/assets/javascripts/right_sidebar.js | 3 + .../components/configuration_table.vue | 23 +- .../components/features_constants.js | 112 ---- .../components/manage_sast.vue | 8 +- .../components/scanners_constants.js | 136 +++++ .../security_configuration/components/upgrade.vue | 10 +- .../javascripts/security_configuration/index.js | 3 +- .../self_monitor/components/self_monitor_form.vue | 4 +- app/assets/javascripts/sentry/sentry_config.js | 2 +- app/assets/javascripts/sentry/wrapper.js | 26 - app/assets/javascripts/shared/popover.js | 33 -- .../components/assignees/issuable_assignees.vue | 9 +- .../assignees/sidebar_assignees_widget.vue | 50 +- .../assignees/uncollapsed_assignee_list.vue | 2 +- .../confidential/confidential_issue_sidebar.vue | 113 ---- .../sidebar/components/confidential/edit_form.vue | 64 --- .../components/confidential/edit_form_buttons.vue | 81 --- .../update_issue_confidential.mutation.graphql | 8 - .../sidebar_confidentiality_content.vue | 64 +++ .../confidential/sidebar_confidentiality_form.vue | 136 +++++ .../sidebar_confidentiality_widget.vue | 142 +++++ .../reference/sidebar_reference_widget.vue | 84 +++ .../reviewers/uncollapsed_reviewer_list.vue | 16 +- .../sidebar/components/sidebar_editable_item.vue | 37 +- .../components/subscriptions/subscriptions.vue | 2 + .../components/time_tracking/help_state.vue | 6 +- app/assets/javascripts/sidebar/constants.js | 28 + app/assets/javascripts/sidebar/graphql.js | 8 + app/assets/javascripts/sidebar/mount_sidebar.js | 110 ++-- .../queries/epic_confidential.query.graphql | 10 + .../queries/issue_confidential.query.graphql | 10 + .../sidebar/queries/issue_reference.query.graphql | 10 + .../queries/merge_request_reference.query.graphql | 10 + .../sidebar/queries/sidebarDetailsMR.query.graphql | 7 + .../update_epic_confidential.mutation.graphql | 9 + .../update_issue_confidential.mutation.graphql | 9 + .../sidebar/services/sidebar_service.js | 15 +- app/assets/javascripts/sidebar/sidebar_mediator.js | 1 + app/assets/javascripts/single_file_diff.js | 2 +- .../javascripts/snippets/components/edit.vue | 5 +- .../components/snippet_visibility_edit.vue | 7 +- .../javascripts/static_site_editor/constants.js | 2 +- .../javascripts/tooltips/components/tooltips.vue | 6 +- app/assets/javascripts/tooltips/index.js | 1 + app/assets/javascripts/tracking.js | 62 +- app/assets/javascripts/user_popovers.js | 27 +- .../components/deployment/deployment_list.vue | 92 +++ .../components/mr_collapsible_extension.vue | 25 +- .../components/mr_widget_header.vue | 4 +- .../components/mr_widget_how_to_merge_modal.vue | 12 +- .../components/mr_widget_merge_help.vue | 49 -- .../components/mr_widget_pipeline.vue | 37 +- .../components/mr_widget_pipeline_container.vue | 48 +- .../states/mr_widget_auto_merge_enabled.vue | 2 +- .../components/states/mr_widget_conflicts.vue | 82 +-- .../components/states/mr_widget_rebase.vue | 18 +- .../components/states/nothing_to_merge.vue | 36 +- .../components/states/ready_to_merge.vue | 96 ++-- .../components/states/sha_mismatch.vue | 2 +- .../components/states/squash_before_merge.vue | 2 +- .../components/states/work_in_progress.vue | 6 +- .../mixins/mr_widget_pipeline.js | 5 - .../vue_merge_request_widget/mr_widget_options.vue | 10 +- .../queries/toggle_wip.mutation.graphql | 1 + .../stores/mr_widget_store.js | 3 +- .../alert_details/components/alert_details.vue | 10 +- .../alert_details/components/alert_metrics.vue | 2 +- .../components/sidebar/sidebar_todo.vue | 1 - .../javascripts/vue_shared/alert_details/index.js | 1 - .../vue_shared/components/awards_list.vue | 22 + .../vue_shared/components/changed_file_icon.vue | 8 +- .../javascripts/vue_shared/components/ci_icon.vue | 8 +- .../vue_shared/components/clipboard_button.vue | 12 +- .../components/diff_viewer/viewers/renamed.vue | 8 +- .../vue_shared/components/file_icon.vue | 3 +- .../vue_shared/components/header_ci_component.vue | 10 +- .../components/markdown/suggestion_diff_header.vue | 24 +- .../vue_shared/components/notes/timeline_icon.vue | 3 + .../components/registry/code_instruction.vue | 40 +- .../vue_shared/components/registry/list_item.vue | 2 +- .../registry/persisted_dropdown_selection.vue | 59 ++ .../vue_shared/components/select2_select.vue | 6 + .../components/settings/settings_block.vue | 7 +- .../sidebar/labels_select_vue/dropdown_value.vue | 5 +- .../labels_select_vue/labels_select_root.vue | 6 + .../components/sidebar/multiselect_dropdown.vue | 11 +- .../queries/get_issue_participants.query.graphql | 4 +- .../queries/get_mr_participants.query.graphql | 2 +- .../update_issue_assignees.mutation.graphql | 4 +- .../javascripts/vue_shared/components/tabs/tab.vue | 47 -- .../javascripts/vue_shared/components/tabs/tabs.js | 76 --- .../vue_shared/components/tooltip_on_truncate.vue | 7 +- .../components/upload_dropzone/upload_dropzone.vue | 21 +- .../components/user_access_role_badge.vue | 22 + .../components/user_popover/user_popover.vue | 16 +- .../javascripts/vue_shared/directives/tooltip.js | 35 -- .../vue_shared/gl_feature_flags_plugin.js | 7 +- .../vue_shared/mixins/related_issuable_mixin.js | 2 +- .../security_reports/components/help_icon.vue | 2 +- .../vue_shared/security_reports/constants.js | 3 +- 687 files changed, 15637 insertions(+), 7986 deletions(-) create mode 100644 app/assets/javascripts/access_tokens/components/projects_field.vue create mode 100644 app/assets/javascripts/access_tokens/components/projects_token_selector.vue create mode 100644 app/assets/javascripts/access_tokens/graphql/queries/get_projects.query.graphql delete mode 100644 app/assets/javascripts/admin/dev_ops_report/components/usage_ping_disabled.vue delete mode 100644 app/assets/javascripts/admin/dev_ops_report/devops_adoption.js delete mode 100644 app/assets/javascripts/admin/dev_ops_report/devops_score_empty_state.js delete mode 100644 app/assets/javascripts/alerts_settings/components/mocks/parsedMapping.json create mode 100644 app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_item.fragment.graphql create mode 100644 app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_payload_data.fragment.graphql create mode 100644 app/assets/javascripts/alerts_settings/graphql/mutations/update_current_http_integration.mutation.graphql delete mode 100644 app/assets/javascripts/alerts_settings/graphql/mutations/update_current_intergration.mutation.graphql create mode 100644 app/assets/javascripts/alerts_settings/graphql/mutations/update_current_prometheus_integration.mutation.graphql create mode 100644 app/assets/javascripts/alerts_settings/graphql/queries/get_http_integrations.query.graphql create mode 100644 app/assets/javascripts/alerts_settings/graphql/queries/parse_sample_payload.query.graphql create mode 100644 app/assets/javascripts/analytics/devops_report/components/usage_ping_disabled.vue create mode 100644 app/assets/javascripts/analytics/devops_report/devops_score_empty_state.js delete mode 100644 app/assets/javascripts/analytics/instance_statistics/components/app.vue delete mode 100644 app/assets/javascripts/analytics/instance_statistics/components/charts_config.js delete mode 100644 app/assets/javascripts/analytics/instance_statistics/components/instance_counts.vue delete mode 100644 app/assets/javascripts/analytics/instance_statistics/components/instance_statistics_count_chart.vue delete mode 100644 app/assets/javascripts/analytics/instance_statistics/components/projects_and_groups_chart.vue delete mode 100644 app/assets/javascripts/analytics/instance_statistics/components/users_chart.vue delete mode 100644 app/assets/javascripts/analytics/instance_statistics/constants.js delete mode 100644 app/assets/javascripts/analytics/instance_statistics/graphql/fragments/count.fragment.graphql delete mode 100644 app/assets/javascripts/analytics/instance_statistics/graphql/queries/groups.query.graphql delete mode 100644 app/assets/javascripts/analytics/instance_statistics/graphql/queries/instance_count.query.graphql delete mode 100644 app/assets/javascripts/analytics/instance_statistics/graphql/queries/instance_statistics_count.query.graphql delete mode 100644 app/assets/javascripts/analytics/instance_statistics/graphql/queries/projects.query.graphql delete mode 100644 app/assets/javascripts/analytics/instance_statistics/graphql/queries/users.query.graphql delete mode 100644 app/assets/javascripts/analytics/instance_statistics/index.js delete mode 100644 app/assets/javascripts/analytics/instance_statistics/utils.js create mode 100644 app/assets/javascripts/analytics/usage_trends/components/app.vue create mode 100644 app/assets/javascripts/analytics/usage_trends/components/charts_config.js create mode 100644 app/assets/javascripts/analytics/usage_trends/components/usage_counts.vue create mode 100644 app/assets/javascripts/analytics/usage_trends/components/usage_trends_count_chart.vue create mode 100644 app/assets/javascripts/analytics/usage_trends/components/users_chart.vue create mode 100644 app/assets/javascripts/analytics/usage_trends/constants.js create mode 100644 app/assets/javascripts/analytics/usage_trends/graphql/fragments/count.fragment.graphql create mode 100644 app/assets/javascripts/analytics/usage_trends/graphql/queries/usage_count.query.graphql create mode 100644 app/assets/javascripts/analytics/usage_trends/graphql/queries/usage_trends_count.query.graphql create mode 100644 app/assets/javascripts/analytics/usage_trends/graphql/queries/users.query.graphql create mode 100644 app/assets/javascripts/analytics/usage_trends/index.js create mode 100644 app/assets/javascripts/analytics/usage_trends/utils.js create mode 100644 app/assets/javascripts/boards/components/board_add_new_column.vue create mode 100644 app/assets/javascripts/boards/components/board_add_new_column_form.vue create mode 100644 app/assets/javascripts/boards/components/board_card_deprecated.vue create mode 100644 app/assets/javascripts/boards/components/board_card_inner.vue delete mode 100644 app/assets/javascripts/boards/components/board_card_layout.vue create mode 100644 app/assets/javascripts/boards/components/config_toggle.vue create mode 100644 app/assets/javascripts/boards/components/filtered_search.vue delete mode 100644 app/assets/javascripts/boards/components/issue_card_inner.vue delete mode 100644 app/assets/javascripts/boards/components/issue_count.vue create mode 100644 app/assets/javascripts/boards/components/item_count.vue delete mode 100644 app/assets/javascripts/boards/components/sidebar/remove_issue.vue create mode 100644 app/assets/javascripts/boards/filtered_search.js delete mode 100644 app/assets/javascripts/boards/graphql/users_search.query.graphql create mode 100644 app/assets/javascripts/boards/mixins/board_card_inner.js delete mode 100644 app/assets/javascripts/boards/mixins/issue_card_inner.js create mode 100644 app/assets/javascripts/captcha/captcha_modal_axios_interceptor.js create mode 100644 app/assets/javascripts/captcha/unsolved_captcha_error.js create mode 100644 app/assets/javascripts/captcha/wait_for_captcha_to_be_solved.js create mode 100644 app/assets/javascripts/cycle_analytics/components/base.vue delete mode 100644 app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js create mode 100644 app/assets/javascripts/cycle_analytics/index.js create mode 100644 app/assets/javascripts/editor/extensions/editor_lite_webide_ext.js create mode 100644 app/assets/javascripts/emoji/components/category.vue create mode 100644 app/assets/javascripts/emoji/components/emoji_group.vue create mode 100644 app/assets/javascripts/emoji/components/emoji_list.vue create mode 100644 app/assets/javascripts/emoji/components/picker.vue create mode 100644 app/assets/javascripts/emoji/components/utils.js create mode 100644 app/assets/javascripts/emoji/constants.js create mode 100644 app/assets/javascripts/experimentation/constants.js create mode 100644 app/assets/javascripts/experimentation/experiment_tracking.js create mode 100644 app/assets/javascripts/experimentation/utils.js create mode 100644 app/assets/javascripts/graphql_shared/queries/project_user_members_search.query.graphql create mode 100644 app/assets/javascripts/helpers/cve_id_request_helper.js create mode 100644 app/assets/javascripts/ide/messages.js delete mode 100644 app/assets/javascripts/ide/queries/getUserPermissions.query.graphql create mode 100644 app/assets/javascripts/ide/queries/get_ide_project.query.graphql create mode 100644 app/assets/javascripts/ide/queries/ide_project.fragment.graphql create mode 100644 app/assets/javascripts/import_entities/import_groups/graphql/queries/group.query.graphql create mode 100644 app/assets/javascripts/invite_members/components/group_select.vue create mode 100644 app/assets/javascripts/invite_members/components/invite_group_trigger.vue create mode 100644 app/assets/javascripts/invite_members/init_invite_group_trigger.js create mode 100644 app/assets/javascripts/invite_members/init_invite_members_form.js create mode 100644 app/assets/javascripts/issuable/components/csv_export_modal.vue create mode 100644 app/assets/javascripts/issuable/components/csv_import_export_buttons.vue create mode 100644 app/assets/javascripts/issuable/components/csv_import_modal.vue create mode 100644 app/assets/javascripts/issuable/constants.js create mode 100644 app/assets/javascripts/issuable/init_csv_import_export_buttons.js create mode 100644 app/assets/javascripts/issuable_show/components/issuable_discussion.vue create mode 100644 app/assets/javascripts/issues_list/components/issue_card_time_info.vue create mode 100644 app/assets/javascripts/issues_list/components/issues_list_app.vue create mode 100644 app/assets/javascripts/issues_list/components/jira_issues_import_status_app.vue delete mode 100644 app/assets/javascripts/issues_list/components/jira_issues_list_root.vue create mode 100644 app/assets/javascripts/jira_connect/utils.js delete mode 100644 app/assets/javascripts/jobs/svg/scroll_down.svg delete mode 100644 app/assets/javascripts/lib/chrome_84_icon_fix.js delete mode 100644 app/assets/javascripts/lib/utils/experimentation.js create mode 100644 app/assets/javascripts/lib/utils/select2_utils.js delete mode 100644 app/assets/javascripts/members.js delete mode 100644 app/assets/javascripts/merge_conflicts/components/diff_file_editor.js create mode 100644 app/assets/javascripts/merge_conflicts/components/diff_file_editor.vue delete mode 100644 app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.js create mode 100644 app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.vue delete mode 100644 app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js create mode 100644 app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.vue create mode 100644 app/assets/javascripts/merge_conflicts/constants.js create mode 100644 app/assets/javascripts/merge_conflicts/merge_conflict_resolver_app.vue create mode 100644 app/assets/javascripts/merge_conflicts/store/actions.js create mode 100644 app/assets/javascripts/merge_conflicts/store/getters.js create mode 100644 app/assets/javascripts/merge_conflicts/store/index.js create mode 100644 app/assets/javascripts/merge_conflicts/store/mutation_types.js create mode 100644 app/assets/javascripts/merge_conflicts/store/mutations.js create mode 100644 app/assets/javascripts/merge_conflicts/store/state.js create mode 100644 app/assets/javascripts/merge_conflicts/utils.js create mode 100644 app/assets/javascripts/notes/components/sidebar_subscription.vue create mode 100644 app/assets/javascripts/notes/i18n.js delete mode 100644 app/assets/javascripts/notifications_dropdown.js delete mode 100644 app/assets/javascripts/notifications_form.js create mode 100644 app/assets/javascripts/packages/details/components/installation_title.vue delete mode 100644 app/assets/javascripts/pages/admin/instance_statistics/index.js create mode 100644 app/assets/javascripts/pages/admin/usage_trends/index.js delete mode 100644 app/assets/javascripts/pages/profiles/index/index.js create mode 100644 app/assets/javascripts/pages/projects/forks/new/components/app.vue create mode 100644 app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue create mode 100644 app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_info_card.vue create mode 100644 app/assets/javascripts/pages/shared/wikis/components/wiki_alert.vue create mode 100644 app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue create mode 100644 app/assets/javascripts/pipeline_editor/components/ui/pipeline_editor_empty_state.vue create mode 100644 app/assets/javascripts/pipeline_editor/graphql/queries/client/current_branch.graphql create mode 100644 app/assets/javascripts/pipeline_editor/graphql/queries/client/is_new_ci_config_file.graphql create mode 100644 app/assets/javascripts/pipeline_editor/graphql/queries/client/pipeline.graphql create mode 100644 app/assets/javascripts/pipeline_new/components/refs_dropdown.vue create mode 100644 app/assets/javascripts/pipelines/components/graph_shared/api.js create mode 100644 app/assets/javascripts/pipelines/components/pipelines_list/pipeline_mini_graph.vue create mode 100644 app/assets/javascripts/pipelines/components/pipelines_list/pipeline_operations.vue create mode 100644 app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue delete mode 100644 app/assets/javascripts/pipelines/components/pipelines_list/pipelines_actions.vue create mode 100644 app/assets/javascripts/pipelines/components/pipelines_list/pipelines_commit.vue create mode 100644 app/assets/javascripts/pipelines/components/pipelines_list/pipelines_manual_actions.vue create mode 100644 app/assets/javascripts/pipelines/components/pipelines_list/pipelines_status_badge.vue delete mode 100644 app/assets/javascripts/pipelines/components/pipelines_list/stage.vue create mode 100644 app/assets/javascripts/projects/commit/components/projects_dropdown.vue create mode 100644 app/assets/javascripts/projects/commit_box/info/init_commit_pipeline_mini_graph.js create mode 100644 app/assets/javascripts/projects/compare/components/app_legacy.vue create mode 100644 app/assets/javascripts/projects/compare/components/repo_dropdown.vue create mode 100644 app/assets/javascripts/projects/compare/components/revision_card.vue create mode 100644 app/assets/javascripts/projects/compare/components/revision_dropdown_legacy.vue create mode 100644 app/assets/javascripts/projects/details/upload_button.vue create mode 100644 app/assets/javascripts/projects/experiment_new_project_creation/components/new_project_push_tip_popover.vue create mode 100644 app/assets/javascripts/projects/feature_flags_user_lists/show/index.js create mode 100644 app/assets/javascripts/projects/upload_file_experiment.js create mode 100644 app/assets/javascripts/projects/upload_file_experiment_tracking.js create mode 100644 app/assets/javascripts/registry/explorer/constants/common.js delete mode 100644 app/assets/javascripts/reports/components/grouped_test_reports_app.vue delete mode 100644 app/assets/javascripts/reports/components/modal.vue delete mode 100644 app/assets/javascripts/reports/components/test_issue_body.vue create mode 100644 app/assets/javascripts/reports/grouped_test_report/components/modal.vue create mode 100644 app/assets/javascripts/reports/grouped_test_report/components/test_issue_body.vue create mode 100644 app/assets/javascripts/reports/grouped_test_report/grouped_test_reports_app.vue create mode 100644 app/assets/javascripts/reports/grouped_test_report/store/actions.js create mode 100644 app/assets/javascripts/reports/grouped_test_report/store/getters.js create mode 100644 app/assets/javascripts/reports/grouped_test_report/store/index.js create mode 100644 app/assets/javascripts/reports/grouped_test_report/store/mutation_types.js create mode 100644 app/assets/javascripts/reports/grouped_test_report/store/mutations.js create mode 100644 app/assets/javascripts/reports/grouped_test_report/store/state.js create mode 100644 app/assets/javascripts/reports/grouped_test_report/store/utils.js delete mode 100644 app/assets/javascripts/reports/store/actions.js delete mode 100644 app/assets/javascripts/reports/store/getters.js delete mode 100644 app/assets/javascripts/reports/store/index.js delete mode 100644 app/assets/javascripts/reports/store/mutation_types.js delete mode 100644 app/assets/javascripts/reports/store/mutations.js delete mode 100644 app/assets/javascripts/reports/store/state.js delete mode 100644 app/assets/javascripts/reports/store/utils.js create mode 100644 app/assets/javascripts/repository/components/upload_blob_modal.vue delete mode 100644 app/assets/javascripts/security_configuration/components/features_constants.js create mode 100644 app/assets/javascripts/security_configuration/components/scanners_constants.js delete mode 100644 app/assets/javascripts/sentry/wrapper.js delete mode 100644 app/assets/javascripts/shared/popover.js delete mode 100644 app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue delete mode 100644 app/assets/javascripts/sidebar/components/confidential/edit_form.vue delete mode 100644 app/assets/javascripts/sidebar/components/confidential/edit_form_buttons.vue delete mode 100644 app/assets/javascripts/sidebar/components/confidential/mutations/update_issue_confidential.mutation.graphql create mode 100644 app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_content.vue create mode 100644 app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_form.vue create mode 100644 app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_widget.vue create mode 100644 app/assets/javascripts/sidebar/components/reference/sidebar_reference_widget.vue create mode 100644 app/assets/javascripts/sidebar/graphql.js create mode 100644 app/assets/javascripts/sidebar/queries/epic_confidential.query.graphql create mode 100644 app/assets/javascripts/sidebar/queries/issue_confidential.query.graphql create mode 100644 app/assets/javascripts/sidebar/queries/issue_reference.query.graphql create mode 100644 app/assets/javascripts/sidebar/queries/merge_request_reference.query.graphql create mode 100644 app/assets/javascripts/sidebar/queries/sidebarDetailsMR.query.graphql create mode 100644 app/assets/javascripts/sidebar/queries/update_epic_confidential.mutation.graphql create mode 100644 app/assets/javascripts/sidebar/queries/update_issue_confidential.mutation.graphql create mode 100644 app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_list.vue delete mode 100644 app/assets/javascripts/vue_merge_request_widget/components/mr_widget_merge_help.vue create mode 100644 app/assets/javascripts/vue_shared/components/notes/timeline_icon.vue create mode 100644 app/assets/javascripts/vue_shared/components/registry/persisted_dropdown_selection.vue delete mode 100644 app/assets/javascripts/vue_shared/components/tabs/tab.vue delete mode 100644 app/assets/javascripts/vue_shared/components/tabs/tabs.js create mode 100644 app/assets/javascripts/vue_shared/components/user_access_role_badge.vue delete mode 100644 app/assets/javascripts/vue_shared/directives/tooltip.js (limited to 'app/assets/javascripts') diff --git a/app/assets/javascripts/access_tokens/components/projects_field.vue b/app/assets/javascripts/access_tokens/components/projects_field.vue new file mode 100644 index 00000000000..066cea5e90c --- /dev/null +++ b/app/assets/javascripts/access_tokens/components/projects_field.vue @@ -0,0 +1,69 @@ + + + diff --git a/app/assets/javascripts/access_tokens/components/projects_token_selector.vue b/app/assets/javascripts/access_tokens/components/projects_token_selector.vue new file mode 100644 index 00000000000..a746f62b3a1 --- /dev/null +++ b/app/assets/javascripts/access_tokens/components/projects_token_selector.vue @@ -0,0 +1,156 @@ + + + diff --git a/app/assets/javascripts/access_tokens/graphql/queries/get_projects.query.graphql b/app/assets/javascripts/access_tokens/graphql/queries/get_projects.query.graphql new file mode 100644 index 00000000000..60110437ecd --- /dev/null +++ b/app/assets/javascripts/access_tokens/graphql/queries/get_projects.query.graphql @@ -0,0 +1,28 @@ +#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" + +query getProjects( + $search: String = "" + $after: String = "" + $first: Int = null + $ids: [ID!] = null +) { + projects( + search: $search + after: $after + first: $first + ids: $ids + membership: true + searchNamespaces: true + sort: "UPDATED_ASC" + ) { + nodes { + id + name + nameWithNamespace + avatarUrl + } + pageInfo { + ...PageInfo + } + } +} diff --git a/app/assets/javascripts/access_tokens/index.js b/app/assets/javascripts/access_tokens/index.js index b4353af30d5..43d56295f78 100644 --- a/app/assets/javascripts/access_tokens/index.js +++ b/app/assets/javascripts/access_tokens/index.js @@ -1,4 +1,7 @@ import Vue from 'vue'; +import createFlash from '~/flash'; +import { __ } from '~/locale'; + import ExpiresAtField from './components/expires_at_field.vue'; const getInputAttrs = (el) => { @@ -7,11 +10,12 @@ const getInputAttrs = (el) => { return { id: input.id, name: input.name, + value: input.value, placeholder: input.placeholder, }; }; -const initExpiresAtField = () => { +export const initExpiresAtField = () => { const el = document.querySelector('.js-access-tokens-expires-at'); if (!el) { @@ -32,4 +36,58 @@ const initExpiresAtField = () => { }); }; -export default initExpiresAtField; +export const initProjectsField = () => { + const el = document.querySelector('.js-access-tokens-projects'); + + if (!el) { + return null; + } + + const inputAttrs = getInputAttrs(el); + + if (window.gon.features.personalAccessTokensScopedToProjects) { + return new Promise((resolve) => { + Promise.all([ + import('./components/projects_field.vue'), + import('vue-apollo'), + import('~/lib/graphql'), + ]) + .then( + ([ + { default: ProjectsField }, + { default: VueApollo }, + { default: createDefaultClient }, + ]) => { + const apolloProvider = new VueApollo({ + defaultClient: createDefaultClient(), + }); + + Vue.use(VueApollo); + + resolve( + new Vue({ + el, + apolloProvider, + render(h) { + return h(ProjectsField, { + props: { + inputAttrs, + }, + }); + }, + }), + ); + }, + ) + .catch(() => { + createFlash({ + message: __( + 'An error occurred while loading the access tokens form, please try again.', + ), + }); + }); + }); + } + + return null; +}; diff --git a/app/assets/javascripts/admin/dev_ops_report/components/usage_ping_disabled.vue b/app/assets/javascripts/admin/dev_ops_report/components/usage_ping_disabled.vue deleted file mode 100644 index c0ad814172d..00000000000 --- a/app/assets/javascripts/admin/dev_ops_report/components/usage_ping_disabled.vue +++ /dev/null @@ -1,53 +0,0 @@ - - diff --git a/app/assets/javascripts/admin/dev_ops_report/devops_adoption.js b/app/assets/javascripts/admin/dev_ops_report/devops_adoption.js deleted file mode 100644 index ae73033079d..00000000000 --- a/app/assets/javascripts/admin/dev_ops_report/devops_adoption.js +++ /dev/null @@ -1,2 +0,0 @@ -// EE-specific feature. Find the implementation in the `ee/`-folder -export default () => {}; diff --git a/app/assets/javascripts/admin/dev_ops_report/devops_score_empty_state.js b/app/assets/javascripts/admin/dev_ops_report/devops_score_empty_state.js deleted file mode 100644 index 0cb8d9be0e4..00000000000 --- a/app/assets/javascripts/admin/dev_ops_report/devops_score_empty_state.js +++ /dev/null @@ -1,27 +0,0 @@ -import Vue from 'vue'; -import UserCallout from '~/user_callout'; -import UsagePingDisabled from './components/usage_ping_disabled.vue'; - -export default () => { - // eslint-disable-next-line no-new - new UserCallout(); - - const emptyStateContainer = document.getElementById('js-devops-empty-state'); - - if (!emptyStateContainer) return false; - - const { emptyStateSvgPath, enableUsagePingLink, docsLink, isAdmin } = emptyStateContainer.dataset; - - return new Vue({ - el: emptyStateContainer, - provide: { - isAdmin: Boolean(isAdmin), - svgPath: emptyStateSvgPath, - primaryButtonPath: enableUsagePingLink, - docsLink, - }, - render(h) { - return h(UsagePingDisabled); - }, - }); -}; diff --git a/app/assets/javascripts/admin/users/tabs.js b/app/assets/javascripts/admin/users/tabs.js index 9ada77396c7..cbaab7df4e9 100644 --- a/app/assets/javascripts/admin/users/tabs.js +++ b/app/assets/javascripts/admin/users/tabs.js @@ -1,11 +1,20 @@ +import Api from '~/api'; import { historyPushState } from '~/lib/utils/common_utils'; import { mergeUrlParams } from '~/lib/utils/url_utility'; const COHORTS_PANE = 'cohorts'; +const COHORTS_PANE_TAB_CLICK_EVENT = 'i_analytics_cohorts'; const tabClickHandler = (e) => { const { hash } = e.currentTarget; - const tab = hash === `#${COHORTS_PANE}` ? COHORTS_PANE : null; + + let tab = null; + + if (hash === `#${COHORTS_PANE}`) { + tab = COHORTS_PANE; + Api.trackRedisHllUserEvent(COHORTS_PANE_TAB_CLICK_EVENT); + } + const newUrl = mergeUrlParams({ tab }, window.location.href); historyPushState(newUrl); }; diff --git a/app/assets/javascripts/alert_management/components/alert_management_table.vue b/app/assets/javascripts/alert_management/components/alert_management_table.vue index dd702c4a5d3..79a6bac3ba7 100644 --- a/app/assets/javascripts/alert_management/components/alert_management_table.vue +++ b/app/assets/javascripts/alert_management/components/alert_management_table.vue @@ -42,6 +42,7 @@ export default { "AlertManagement|There was an error displaying the alerts. Confirm your endpoint's configuration details to ensure alerts appear.", ), unassigned: __('Unassigned'), + closed: __('closed'), }, fields: [ { @@ -75,7 +76,7 @@ export default { { key: 'issue', label: s__('AlertManagement|Incident'), - thClass: 'gl-w-12 gl-pointer-events-none', + thClass: 'gl-w-15p gl-pointer-events-none', tdClass, }, { @@ -221,8 +222,11 @@ export default { hasAssignees(assignees) { return Boolean(assignees.nodes?.length); }, - getIssueLink(item) { - return joinPaths('/', this.projectPath, '-', 'issues', item.issueIid); + getIssueMeta({ issue: { iid, state } }) { + return { + state: state === 'closed' ? `(${this.$options.i18n.closed})` : '', + link: joinPaths('/', this.projectPath, '-', 'issues/incident', iid), + }; }, tbodyTrClass(item) { return { @@ -343,8 +347,14 @@ export default { diff --git a/app/assets/javascripts/alerts_settings/components/alert_mapping_builder.vue b/app/assets/javascripts/alerts_settings/components/alert_mapping_builder.vue index 1135562834a..07b2e59671e 100644 --- a/app/assets/javascripts/alerts_settings/components/alert_mapping_builder.vue +++ b/app/assets/javascripts/alerts_settings/components/alert_mapping_builder.vue @@ -7,15 +7,12 @@ import { GlSearchBoxByType, GlTooltipDirective as GlTooltip, } from '@gitlab/ui'; -import { cloneDeep } from 'lodash'; +import { cloneDeep, isEqual } from 'lodash'; import Vue from 'vue'; import { capitalizeFirstCharacter } from '~/lib/utils/text_utility'; import { s__, __ } from '~/locale'; -import { - getMappingData, - getPayloadFields, - transformForSave, -} from '../utils/mapping_transformations'; +import { mappingFields } from '../constants'; +import { getMappingData, transformForSave } from '../utils/mapping_transformations'; export const i18n = { columns: { @@ -33,6 +30,7 @@ export const i18n = { export default { i18n, + mappingFields, components: { GlIcon, GlFormInput, @@ -73,18 +71,15 @@ export default { }; }, computed: { - payloadFields() { - return getPayloadFields(this.parsedPayload); - }, mappingData() { - return getMappingData(this.gitlabFields, this.payloadFields, this.savedMapping); + return getMappingData(this.gitlabFields, this.parsedPayload, this.savedMapping); }, hasFallbackColumn() { return this.gitlabFields.some(({ numberOfFallbacks }) => Boolean(numberOfFallbacks)); }, }, methods: { - setMapping(gitlabKey, mappingKey, valueKey) { + setMapping(gitlabKey, mappingKey, valueKey = mappingFields.mapping) { const fieldIndex = this.gitlabFields.findIndex((field) => field.name === gitlabKey); const updatedField = { ...this.gitlabFields[fieldIndex], ...{ [valueKey]: mappingKey } }; Vue.set(this.gitlabFields, fieldIndex, updatedField); @@ -100,11 +95,11 @@ export default { return fields.filter((field) => field.label.toLowerCase().includes(search)); }, isSelected(fieldValue, mapping) { - return fieldValue === mapping; + return isEqual(fieldValue, mapping); }, - selectedValue(name) { + selectedValue(mapping) { return ( - this.payloadFields.find((item) => item.name === name)?.label || + this.parsedPayload.find((item) => isEqual(item.path, mapping))?.label || this.$options.i18n.makeSelection ); }, @@ -150,7 +145,7 @@ export default { :key="gitlabField.name" class="gl-display-table-row" > -
+
-
+
{{ mappingField.label }} @@ -188,7 +183,7 @@ export default {
-
+
{{ mappingField.label }} diff --git a/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue b/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue index 6cfb4601192..a5e17d80f86 100644 --- a/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue +++ b/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue @@ -10,6 +10,7 @@ import { GlTooltipDirective, GlSprintf, } from '@gitlab/ui'; +import { capitalize } from 'lodash'; import { s__, __ } from '~/locale'; import Tracking from '~/tracking'; import { @@ -77,6 +78,7 @@ export default { { key: 'type', label: __('Type'), + formatter: (value) => (value === typeSet.prometheus ? capitalize(value) : value), }, { key: 'actions', @@ -120,14 +122,17 @@ export default { const { category, action } = trackAlertIntegrationsViewsOptions; Tracking.event(category, action); }, - setIntegrationToDelete({ name, id }) { - this.integrationToDelete.id = id; - this.integrationToDelete.name = name; + setIntegrationToDelete(integration) { + this.integrationToDelete = integration; }, deleteIntegration() { - this.$emit('delete-integration', { id: this.integrationToDelete.id }); + const { id, type } = this.integrationToDelete; + this.$emit('delete-integration', { id, type }); this.integrationToDelete = { ...integrationToDeleteDefault }; }, + editIntegration({ id, type }) { + this.$emit('edit-integration', { id, type }); + }, }, }; @@ -169,7 +174,7 @@ export default { diff --git a/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue b/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue index 366f2209fb2..3ffb652e61b 100644 --- a/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue +++ b/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue @@ -1,22 +1,26 @@ @@ -316,7 +338,18 @@ export default { @edit-integration="editIntegration" @delete-integration="deleteIntegration" /> + + {{ $options.i18n.addNewIntegration }} +
diff --git a/app/assets/javascripts/alerts_settings/components/mocks/parsedMapping.json b/app/assets/javascripts/alerts_settings/components/mocks/parsedMapping.json deleted file mode 100644 index 80fbebf2a60..00000000000 --- a/app/assets/javascripts/alerts_settings/components/mocks/parsedMapping.json +++ /dev/null @@ -1,95 +0,0 @@ -{ - "samplePayload": { - "body": "{\n \"dashboardId\":1,\n \"evalMatches\":[\n {\n \"value\":1,\n \"metric\":\"Count\",\n \"tags\":{}\n }\n ],\n \"imageUrl\":\"https://grafana.com/static/assets/img/blog/mixed_styles.png\",\n \"message\":\"Notification Message\",\n \"orgId\":1,\n \"panelId\":2,\n \"ruleId\":1,\n \"ruleName\":\"Panel Title alert\",\n \"ruleUrl\":\"http://localhost:3000/d/hZ7BuVbWz/test-dashboard?fullscreen\\u0026edit\\u0026tab=alert\\u0026panelId=2\\u0026orgId=1\",\n \"state\":\"alerting\",\n \"tags\":{\n \"tag name\":\"tag value\"\n },\n \"title\":\"[Alerting] Panel Title alert\"\n}\n", - "payloadAlerFields": { - "nodes": [ - { - "path": ["dashboardId"], - "label": "Dashboard Id", - "type": "string" - }, - { - "path": ["evalMatches"], - "label": "Eval Matches", - "type": "array" - }, - { - "path": ["createdAt"], - "label": "Created At", - "type": "datetime" - }, - { - "path": ["imageUrl"], - "label": "Image Url", - "type": "string" - }, - { - "path": ["message"], - "label": "Message", - "type": "string" - }, - { - "path": ["orgId"], - "label": "Org Id", - "type": "string" - }, - { - "path": ["panelId"], - "label": "Panel Id", - "type": "string" - }, - { - "path": ["ruleId"], - "label": "Rule Id", - "type": "string" - }, - { - "path": ["ruleName"], - "label": "Rule Name", - "type": "string" - }, - { - "path": ["ruleUrl"], - "label": "Rule Url", - "type": "string" - }, - { - "path": ["state"], - "label": "State", - "type": "string" - }, - { - "path": ["title"], - "label": "Title", - "type": "string" - }, - { - "path": ["tags", "tag"], - "label": "Tags", - "type": "string" - } - ] - } - }, - "storedMapping": { - "nodes": [ - { - "alertFieldName": "title", - "payloadAlertPaths": "title", - "fallbackAlertPaths": "ruleUrl" - }, - { - "alertFieldName": "description", - "payloadAlertPaths": "message" - }, - { - "alertFieldName": "hosts", - "payloadAlertPaths": "evalMatches" - }, - { - "alertFieldName": "startTime", - "payloadAlertPaths": "createdAt" - } - ] - } -} diff --git a/app/assets/javascripts/alerts_settings/constants.js b/app/assets/javascripts/alerts_settings/constants.js index ecd7c921b2f..ce6cf61b5dd 100644 --- a/app/assets/javascripts/alerts_settings/constants.js +++ b/app/assets/javascripts/alerts_settings/constants.js @@ -1,50 +1,106 @@ -import { s__ } from '~/locale'; +import { s__, __ } from '~/locale'; -// TODO: Remove this as part of the form old removal export const i18n = { - usageSection: s__( - 'AlertSettings|You must provide this URL and authorization key to authorize an external service to send alerts to GitLab. You can provide this URL and key to multiple services. After configuring an external service, alerts from your service will display on the GitLab %{linkStart}Alerts%{linkEnd} page.', - ), - setupSection: s__( - "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint.", - ), - errorMsg: s__('AlertSettings|There was an error updating the alert settings.'), - errorKeyMsg: s__( - 'AlertSettings|There was an error while trying to reset the key. Please refresh the page to try again.', - ), - restKeyInfo: s__( - 'AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in.', - ), + integrationTabs: { + configureDetails: s__('AlertSettings|Configure details'), + viewCredentials: s__('AlertSettings|View credentials'), + sendTestAlert: s__('AlertSettings|Send test alert'), + }, + integrationFormSteps: { + selectType: { + label: s__('AlertSettings|Select integration type'), + enterprise: s__( + 'AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations.', + ), + }, + nameIntegration: { + label: s__('AlertSettings|Name integration'), + placeholder: s__('AlertSettings|Enter integration name'), + activeToggle: __('Active'), + }, + setupCredentials: { + help: s__( + "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint.", + ), + prometheusHelp: s__( + 'AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint.', + ), + webhookUrl: s__('AlertSettings|Webhook URL'), + authorizationKey: s__('AlertSettings|Authorization key'), + reset: s__('AlertSettings|Reset Key'), + }, + setSamplePayload: { + label: s__('AlertSettings|Sample alert payload (optional)'), + testPayloadHelpHttp: s__( + 'AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional).', + ), + testPayloadHelp: s__( + 'AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This will allow you to send an alert to an active GitLab alerting point.', + ), + placeholder: s__('AlertSettings|{ "events": [{ "application": "Name of application" }] }'), + resetHeader: s__('AlertSettings|Reset the mapping'), + resetBody: s__( + "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields.", + ), + resetOk: s__('AlertSettings|Proceed with editing'), + editPayload: s__('AlertSettings|Edit payload'), + parsePayload: s__('AlertSettings|Parse payload for custom mapping'), + payloadParsedSucessMsg: s__( + 'AlertSettings|Sample payload has been parsed. You can now map the fields.', + ), + }, + mapFields: { + label: s__('AlertSettings|Customize alert payload mapping (optional)'), + intro: s__( + 'AlertSettings|If you intend to create a custom mapping, provide an example payload from your monitoring tool and click "parse payload fields" button to continue. The sample payload is required for completing the custom mapping; if you want to skip the mapping step, progress straight to saving your integration.', + ), + }, + prometheusFormUrl: { + label: s__('AlertSettings|Prometheus API base URL'), + help: s__('AlertSettings|URL cannot be blank and must start with http or https'), + }, + restKeyInfo: { + label: s__( + 'AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in.', + ), + }, + }, + saveIntegration: s__('AlertSettings|Save integration'), changesSaved: s__('AlertSettings|Your integration was successfully updated.'), - prometheusInfo: s__('AlertSettings|Add URL and auth key to your Prometheus config file'), - integrationsInfo: s__( - 'AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}', - ), - resetKey: s__('AlertSettings|Reset key'), - copyToClipboard: s__('AlertSettings|Copy'), - apiBaseUrlLabel: s__('AlertSettings|API URL'), - authKeyLabel: s__('AlertSettings|Authorization key'), - urlLabel: s__('AlertSettings|Webhook URL'), - activeLabel: s__('AlertSettings|Active'), - apiBaseUrlHelpText: s__('AlertSettings|URL cannot be blank and must start with http or https'), - testAlertInfo: s__('AlertSettings|Test alert payload'), - alertJson: s__('AlertSettings|Alert test payload'), - alertJsonPlaceholder: s__('AlertSettings|Enter test alert JSON....'), - testAlertFailed: s__('AlertSettings|Test failed. Do you still want to save your changes anyway?'), - testAlertSuccess: s__( - 'AlertSettings|Test alert sent successfully. If you have made other changes, please save them now.', - ), - authKeyRest: s__( - 'AlertSettings|Authorization key has been successfully reset. Please save your changes now.', - ), - integration: s__('AlertSettings|Integration'), + cancelAndClose: __('Cancel and close'), + send: s__('AlertSettings|Send'), + copy: __('Copy'), }; -export const integrationTypes = [ - { value: '', text: s__('AlertSettings|Select integration type') }, - { value: 'HTTP', text: s__('AlertSettings|HTTP Endpoint') }, - { value: 'PROMETHEUS', text: s__('AlertSettings|External Prometheus') }, -]; +export const integrationSteps = { + selectType: 'SELECT_TYPE', + nameIntegration: 'NAME_INTEGRATION', + setPrometheusApiUrl: 'SET_PROMETHEUS_API_URL', + setSamplePayload: 'SET_SAMPLE_PAYLOAD', + customizeMapping: 'CUSTOMIZE_MAPPING', +}; + +export const createStepNumbers = { + [integrationSteps.selectType]: 1, + [integrationSteps.nameIntegration]: 2, + [integrationSteps.setPrometheusApiUrl]: 2, + [integrationSteps.setSamplePayload]: 3, + [integrationSteps.customizeMapping]: 4, +}; + +export const editStepNumbers = { + [integrationSteps.selectType]: 1, + [integrationSteps.nameIntegration]: 1, + [integrationSteps.setPrometheusApiUrl]: null, + [integrationSteps.setSamplePayload]: 2, + [integrationSteps.customizeMapping]: 3, +}; + +export const integrationTypes = { + none: { value: '', text: s__('AlertSettings|Select integration type') }, + http: { value: 'HTTP', text: s__('AlertSettings|HTTP Endpoint') }, + prometheus: { value: 'PROMETHEUS', text: s__('AlertSettings|External Prometheus') }, +}; export const typeSet = { http: 'HTTP', @@ -57,14 +113,18 @@ export const JSON_VALIDATE_DELAY = 250; export const targetPrometheusUrlPlaceholder = 'http://prometheus.example.com/'; -export const sectionHash = 'js-alert-management-settings'; - -/* eslint-disable @gitlab/require-i18n-strings */ - /** * Tracks snowplow event when user views alerts integration list */ export const trackAlertIntegrationsViewsOptions = { + /* eslint-disable-next-line @gitlab/require-i18n-strings */ category: 'Alert Integrations', action: 'view_alert_integrations_list', }; + +export const mappingFields = { + mapping: 'mapping', + fallback: 'fallback', +}; + +export const viewCredentialsTabIndex = 1; diff --git a/app/assets/javascripts/alerts_settings/graphql.js b/app/assets/javascripts/alerts_settings/graphql.js index 5fd05169533..72817f636ff 100644 --- a/app/assets/javascripts/alerts_settings/graphql.js +++ b/app/assets/javascripts/alerts_settings/graphql.js @@ -10,16 +10,25 @@ const resolvers = { Mutation: { updateCurrentIntegration: ( _, - { id = null, name, active, token, type, url, apiUrl }, + { + id = null, + name, + active, + token, + type, + url, + apiUrl, + payloadExample, + payloadAttributeMappings, + payloadAlertFields, + }, { cache }, ) => { const sourceData = cache.readQuery({ query: getCurrentIntegrationQuery }); const data = produce(sourceData, (draftData) => { if (id === null) { - // eslint-disable-next-line no-param-reassign draftData.currentIntegration = null; } else { - // eslint-disable-next-line no-param-reassign draftData.currentIntegration = { id, name, @@ -28,6 +37,9 @@ const resolvers = { type, url, apiUrl, + payloadExample, + payloadAttributeMappings, + payloadAlertFields, }; } }); diff --git a/app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_item.fragment.graphql b/app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_item.fragment.graphql new file mode 100644 index 00000000000..742228e2928 --- /dev/null +++ b/app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_item.fragment.graphql @@ -0,0 +1,7 @@ +#import "./integration_item.fragment.graphql" +#import "ee_else_ce/alerts_settings/graphql/fragments/http_integration_payload_data.fragment.graphql" + +fragment HttpIntegrationItem on AlertManagementHttpIntegration { + ...IntegrationItem + ...HttpIntegrationPayloadData +} diff --git a/app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_payload_data.fragment.graphql b/app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_payload_data.fragment.graphql new file mode 100644 index 00000000000..df6ad0b712d --- /dev/null +++ b/app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_payload_data.fragment.graphql @@ -0,0 +1,3 @@ +fragment HttpIntegrationPayloadData on AlertManagementHttpIntegration { + id +} diff --git a/app/assets/javascripts/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql b/app/assets/javascripts/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql index f3fc10b4bd4..babcdea935d 100644 --- a/app/assets/javascripts/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql +++ b/app/assets/javascripts/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql @@ -1,24 +1,10 @@ -#import "../fragments/integration_item.fragment.graphql" +#import "../fragments/http_integration_item.fragment.graphql" -mutation createHttpIntegration( - $projectPath: ID! - $name: String! - $active: Boolean! - $payloadExample: JsonString - $payloadAttributeMappings: [AlertManagementPayloadAlertFieldInput!] -) { - httpIntegrationCreate( - input: { - projectPath: $projectPath - name: $name - active: $active - payloadExample: $payloadExample - payloadAttributeMappings: $payloadAttributeMappings - } - ) { +mutation createHttpIntegration($projectPath: ID!, $name: String!, $active: Boolean!) { + httpIntegrationCreate(input: { projectPath: $projectPath, name: $name, active: $active }) { errors integration { - ...IntegrationItem + ...HttpIntegrationItem } } } diff --git a/app/assets/javascripts/alerts_settings/graphql/mutations/destroy_http_integration.mutation.graphql b/app/assets/javascripts/alerts_settings/graphql/mutations/destroy_http_integration.mutation.graphql index 0a49c140e6a..a3a50651fd0 100644 --- a/app/assets/javascripts/alerts_settings/graphql/mutations/destroy_http_integration.mutation.graphql +++ b/app/assets/javascripts/alerts_settings/graphql/mutations/destroy_http_integration.mutation.graphql @@ -1,10 +1,10 @@ -#import "../fragments/integration_item.fragment.graphql" +#import "../fragments/http_integration_item.fragment.graphql" mutation destroyHttpIntegration($id: ID!) { httpIntegrationDestroy(input: { id: $id }) { errors integration { - ...IntegrationItem + ...HttpIntegrationItem } } } diff --git a/app/assets/javascripts/alerts_settings/graphql/mutations/reset_http_token.mutation.graphql b/app/assets/javascripts/alerts_settings/graphql/mutations/reset_http_token.mutation.graphql index 178d1e13047..c0754d8e32b 100644 --- a/app/assets/javascripts/alerts_settings/graphql/mutations/reset_http_token.mutation.graphql +++ b/app/assets/javascripts/alerts_settings/graphql/mutations/reset_http_token.mutation.graphql @@ -1,10 +1,10 @@ -#import "../fragments/integration_item.fragment.graphql" +#import "../fragments/http_integration_item.fragment.graphql" mutation resetHttpIntegrationToken($id: ID!) { httpIntegrationResetToken(input: { id: $id }) { errors integration { - ...IntegrationItem + ...HttpIntegrationItem } } } diff --git a/app/assets/javascripts/alerts_settings/graphql/mutations/update_current_http_integration.mutation.graphql b/app/assets/javascripts/alerts_settings/graphql/mutations/update_current_http_integration.mutation.graphql new file mode 100644 index 00000000000..5f3d305993c --- /dev/null +++ b/app/assets/javascripts/alerts_settings/graphql/mutations/update_current_http_integration.mutation.graphql @@ -0,0 +1,25 @@ +mutation updateCurrentHttpIntegration( + $id: String + $name: String + $active: Boolean + $token: String + $type: String + $url: String + $apiUrl: String + $payloadExample: JsonString + $payloadAttributeMappings: [AlertManagementPayloadAlertFieldInput!] + $payloadAlertFields: [AlertManagementPayloadAlertField!] +) { + updateCurrentIntegration( + id: $id + name: $name + active: $active + token: $token + type: $type + url: $url + apiUrl: $apiUrl + payloadExample: $payloadExample + payloadAttributeMappings: $payloadAttributeMappings + payloadAlertFields: $payloadAlertFields + ) @client +} diff --git a/app/assets/javascripts/alerts_settings/graphql/mutations/update_current_intergration.mutation.graphql b/app/assets/javascripts/alerts_settings/graphql/mutations/update_current_intergration.mutation.graphql deleted file mode 100644 index 3505241309e..00000000000 --- a/app/assets/javascripts/alerts_settings/graphql/mutations/update_current_intergration.mutation.graphql +++ /dev/null @@ -1,19 +0,0 @@ -mutation updateCurrentIntegration( - $id: String - $name: String - $active: Boolean - $token: String - $type: String - $url: String - $apiUrl: String -) { - updateCurrentIntegration( - id: $id - name: $name - active: $active - token: $token - type: $type - url: $url - apiUrl: $apiUrl - ) @client -} diff --git a/app/assets/javascripts/alerts_settings/graphql/mutations/update_current_prometheus_integration.mutation.graphql b/app/assets/javascripts/alerts_settings/graphql/mutations/update_current_prometheus_integration.mutation.graphql new file mode 100644 index 00000000000..5bd63820629 --- /dev/null +++ b/app/assets/javascripts/alerts_settings/graphql/mutations/update_current_prometheus_integration.mutation.graphql @@ -0,0 +1,21 @@ +mutation updateCurrentPrometheusIntegration( + $id: String + $name: String + $active: Boolean + $token: String + $type: String + $url: String + $apiUrl: String + $samplePayload: String +) { + updateCurrentIntegration( + id: $id + name: $name + active: $active + token: $token + type: $type + url: $url + apiUrl: $apiUrl + samplePayload: $samplePayload + ) @client +} diff --git a/app/assets/javascripts/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql b/app/assets/javascripts/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql index bb5b334deeb..37df9ec25eb 100644 --- a/app/assets/javascripts/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql +++ b/app/assets/javascripts/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql @@ -1,10 +1,10 @@ -#import "../fragments/integration_item.fragment.graphql" +#import "../fragments/http_integration_item.fragment.graphql" mutation updateHttpIntegration($id: ID!, $name: String!, $active: Boolean!) { httpIntegrationUpdate(input: { id: $id, name: $name, active: $active }) { errors integration { - ...IntegrationItem + ...HttpIntegrationItem } } } diff --git a/app/assets/javascripts/alerts_settings/graphql/queries/get_http_integrations.query.graphql b/app/assets/javascripts/alerts_settings/graphql/queries/get_http_integrations.query.graphql new file mode 100644 index 00000000000..833a2d6c12f --- /dev/null +++ b/app/assets/javascripts/alerts_settings/graphql/queries/get_http_integrations.query.graphql @@ -0,0 +1,12 @@ +#import "ee_else_ce/alerts_settings/graphql/fragments/http_integration_payload_data.fragment.graphql" + +# TODO: this query need to accept http integration id to request a sepcific integration +query getHttpIntegrations($projectPath: ID!) { + project(fullPath: $projectPath) { + alertManagementHttpIntegrations { + nodes { + ...HttpIntegrationPayloadData + } + } + } +} diff --git a/app/assets/javascripts/alerts_settings/graphql/queries/parse_sample_payload.query.graphql b/app/assets/javascripts/alerts_settings/graphql/queries/parse_sample_payload.query.graphql new file mode 100644 index 00000000000..159b2661f0b --- /dev/null +++ b/app/assets/javascripts/alerts_settings/graphql/queries/parse_sample_payload.query.graphql @@ -0,0 +1,9 @@ +query parsePayloadFields($projectPath: ID!, $payload: String!) { + project(fullPath: $projectPath) { + alertManagementPayloadFields(payloadExample: $payload) { + path + label + type + } + } +} diff --git a/app/assets/javascripts/alerts_settings/index.js b/app/assets/javascripts/alerts_settings/index.js index 8506b3fda01..321af9fedb6 100644 --- a/app/assets/javascripts/alerts_settings/index.js +++ b/app/assets/javascripts/alerts_settings/index.js @@ -63,10 +63,7 @@ export default (el) => { render(createElement) { return createElement('alert-settings-wrapper', { props: { - alertFields: - gon.features?.multipleHttpIntegrationsCustomMapping && parseBoolean(multiIntegrations) - ? JSON.parse(alertFields) - : null, + alertFields: parseBoolean(multiIntegrations) ? JSON.parse(alertFields) : null, }, }); }, diff --git a/app/assets/javascripts/alerts_settings/utils/cache_updates.js b/app/assets/javascripts/alerts_settings/utils/cache_updates.js index 758f3eb6dd4..716c709a931 100644 --- a/app/assets/javascripts/alerts_settings/utils/cache_updates.js +++ b/app/assets/javascripts/alerts_settings/utils/cache_updates.js @@ -15,7 +15,6 @@ const deleteIntegrationFromStore = (store, query, { httpIntegrationDestroy }, va }); const data = produce(sourceData, (draftData) => { - // eslint-disable-next-line no-param-reassign draftData.project.alertManagementIntegrations.nodes = draftData.project.alertManagementIntegrations.nodes.filter( ({ id }) => id !== integration.id, ); @@ -46,7 +45,6 @@ const addIntegrationToStore = ( }); const data = produce(sourceData, (draftData) => { - // eslint-disable-next-line no-param-reassign draftData.project.alertManagementIntegrations.nodes = [ integration, ...draftData.project.alertManagementIntegrations.nodes, @@ -60,6 +58,31 @@ const addIntegrationToStore = ( }); }; +const addHttpIntegrationToStore = (store, query, { httpIntegrationCreate }, variables) => { + const integration = httpIntegrationCreate?.integration; + if (!integration) { + return; + } + + const sourceData = store.readQuery({ + query, + variables, + }); + + const data = produce(sourceData, (draftData) => { + draftData.project.alertManagementHttpIntegrations.nodes = [ + integration, + ...draftData.project.alertManagementHttpIntegrations.nodes, + ]; + }); + + store.writeQuery({ + query, + variables, + data, + }); +}; + const onError = (data, message) => { createFlash({ message }); throw new Error(data.errors); @@ -82,3 +105,11 @@ export const updateStoreAfterIntegrationAdd = (store, query, data, variables) => addIntegrationToStore(store, query, data, variables); } }; + +export const updateStoreAfterHttpIntegrationAdd = (store, query, data, variables) => { + if (hasErrors(data)) { + onError(data, ADD_INTEGRATION_ERROR); + } else { + addHttpIntegrationToStore(store, query, data, variables); + } +}; diff --git a/app/assets/javascripts/alerts_settings/utils/error_messages.js b/app/assets/javascripts/alerts_settings/utils/error_messages.js index 979d1ca3ccc..e380257f983 100644 --- a/app/assets/javascripts/alerts_settings/utils/error_messages.js +++ b/app/assets/javascripts/alerts_settings/utils/error_messages.js @@ -17,5 +17,5 @@ export const RESET_INTEGRATION_TOKEN_ERROR = s__( ); export const INTEGRATION_PAYLOAD_TEST_ERROR = s__( - 'AlertsIntegrations|Integration payload is invalid. You can still save your changes.', + 'AlertsIntegrations|Integration payload is invalid.', ); diff --git a/app/assets/javascripts/alerts_settings/utils/mapping_transformations.js b/app/assets/javascripts/alerts_settings/utils/mapping_transformations.js index a86103540c0..5c4b9bcd505 100644 --- a/app/assets/javascripts/alerts_settings/utils/mapping_transformations.js +++ b/app/assets/javascripts/alerts_settings/utils/mapping_transformations.js @@ -1,3 +1,4 @@ +import { isEqual } from 'lodash'; /** * Given data for GitLab alert fields, parsed payload fields data and previously stored mapping (if any) * creates an object in a form convenient to build UI && interact with it @@ -10,16 +11,19 @@ export const getMappingData = (gitlabFields, payloadFields, savedMapping) => { return gitlabFields.map((gitlabField) => { // find fields from payload that match gitlab alert field by type - const mappingFields = payloadFields.filter(({ type }) => gitlabField.types.includes(type)); + const mappingFields = payloadFields.filter(({ type }) => + gitlabField.types.includes(type.toLowerCase()), + ); // find the mapping that was previously stored - const foundMapping = savedMapping.find(({ fieldName }) => fieldName === gitlabField.name); - - const { fallbackAlertPaths, payloadAlertPaths } = foundMapping || {}; + const foundMapping = savedMapping.find( + ({ fieldName }) => fieldName.toLowerCase() === gitlabField.name, + ); + const { path: mapping, fallbackPath: fallback } = foundMapping || {}; return { - mapping: payloadAlertPaths, - fallback: fallbackAlertPaths, + mapping, + fallback, searchTerm: '', fallbackSearchTerm: '', mappingFields, @@ -36,7 +40,7 @@ export const getMappingData = (gitlabFields, payloadFields, savedMapping) => { */ export const transformForSave = (mappingData) => { return mappingData.reduce((acc, field) => { - const mapped = field.mappingFields.find(({ name }) => name === field.mapping); + const mapped = field.mappingFields.find(({ path }) => isEqual(path, field.mapping)); if (mapped) { const { path, type, label } = mapped; acc.push({ @@ -49,13 +53,3 @@ export const transformForSave = (mappingData) => { return acc; }, []); }; - -/** - * Adds `name` prop to each provided by BE parsed payload field - * @param {Object} payload - parsed sample payload - * - * @return {Object} same as input with an extra `name` property which basically serves as a key to make a match - */ -export const getPayloadFields = (payload) => { - return payload.map((field) => ({ ...field, name: field.path.join('_') })); -}; diff --git a/app/assets/javascripts/analytics/devops_report/components/usage_ping_disabled.vue b/app/assets/javascripts/analytics/devops_report/components/usage_ping_disabled.vue new file mode 100644 index 00000000000..c0ad814172d --- /dev/null +++ b/app/assets/javascripts/analytics/devops_report/components/usage_ping_disabled.vue @@ -0,0 +1,53 @@ + + diff --git a/app/assets/javascripts/analytics/devops_report/devops_score_empty_state.js b/app/assets/javascripts/analytics/devops_report/devops_score_empty_state.js new file mode 100644 index 00000000000..0cb8d9be0e4 --- /dev/null +++ b/app/assets/javascripts/analytics/devops_report/devops_score_empty_state.js @@ -0,0 +1,27 @@ +import Vue from 'vue'; +import UserCallout from '~/user_callout'; +import UsagePingDisabled from './components/usage_ping_disabled.vue'; + +export default () => { + // eslint-disable-next-line no-new + new UserCallout(); + + const emptyStateContainer = document.getElementById('js-devops-empty-state'); + + if (!emptyStateContainer) return false; + + const { emptyStateSvgPath, enableUsagePingLink, docsLink, isAdmin } = emptyStateContainer.dataset; + + return new Vue({ + el: emptyStateContainer, + provide: { + isAdmin: Boolean(isAdmin), + svgPath: emptyStateSvgPath, + primaryButtonPath: enableUsagePingLink, + docsLink, + }, + render(h) { + return h(UsagePingDisabled); + }, + }); +}; diff --git a/app/assets/javascripts/analytics/instance_statistics/components/app.vue b/app/assets/javascripts/analytics/instance_statistics/components/app.vue deleted file mode 100644 index 3bf41eaa008..00000000000 --- a/app/assets/javascripts/analytics/instance_statistics/components/app.vue +++ /dev/null @@ -1,48 +0,0 @@ - - - diff --git a/app/assets/javascripts/analytics/instance_statistics/components/charts_config.js b/app/assets/javascripts/analytics/instance_statistics/components/charts_config.js deleted file mode 100644 index 6fba3c56cfe..00000000000 --- a/app/assets/javascripts/analytics/instance_statistics/components/charts_config.js +++ /dev/null @@ -1,87 +0,0 @@ -import { s__, __, sprintf } from '~/locale'; -import query from '../graphql/queries/instance_count.query.graphql'; - -const noDataMessage = s__('InstanceStatistics|No data available.'); - -export default [ - { - loadChartError: sprintf( - s__( - 'InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again.', - ), - ), - noDataMessage, - chartTitle: s__('InstanceStatistics|Pipelines'), - yAxisTitle: s__('InstanceStatistics|Items'), - xAxisTitle: s__('InstanceStatistics|Month'), - queries: [ - { - query, - title: s__('InstanceStatistics|Pipelines total'), - identifier: 'PIPELINES', - loadError: sprintf( - s__('InstanceStatistics|There was an error fetching the total pipelines'), - ), - }, - { - query, - title: s__('InstanceStatistics|Pipelines succeeded'), - identifier: 'PIPELINES_SUCCEEDED', - loadError: sprintf( - s__('InstanceStatistics|There was an error fetching the successful pipelines'), - ), - }, - { - query, - title: s__('InstanceStatistics|Pipelines failed'), - identifier: 'PIPELINES_FAILED', - loadError: sprintf( - s__('InstanceStatistics|There was an error fetching the failed pipelines'), - ), - }, - { - query, - title: s__('InstanceStatistics|Pipelines canceled'), - identifier: 'PIPELINES_CANCELED', - loadError: sprintf( - s__('InstanceStatistics|There was an error fetching the cancelled pipelines'), - ), - }, - { - query, - title: s__('InstanceStatistics|Pipelines skipped'), - identifier: 'PIPELINES_SKIPPED', - loadError: sprintf( - s__('InstanceStatistics|There was an error fetching the skipped pipelines'), - ), - }, - ], - }, - { - loadChartError: sprintf( - s__( - 'InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again.', - ), - ), - noDataMessage, - chartTitle: s__('InstanceStatistics|Issues & Merge Requests'), - yAxisTitle: s__('InstanceStatistics|Items'), - xAxisTitle: s__('InstanceStatistics|Month'), - queries: [ - { - query, - title: __('Issues'), - identifier: 'ISSUES', - loadError: sprintf(s__('InstanceStatistics|There was an error fetching the issues')), - }, - { - query, - title: __('Merge requests'), - identifier: 'MERGE_REQUESTS', - loadError: sprintf( - s__('InstanceStatistics|There was an error fetching the merge requests'), - ), - }, - ], - }, -]; diff --git a/app/assets/javascripts/analytics/instance_statistics/components/instance_counts.vue b/app/assets/javascripts/analytics/instance_statistics/components/instance_counts.vue deleted file mode 100644 index f3779ed62e9..00000000000 --- a/app/assets/javascripts/analytics/instance_statistics/components/instance_counts.vue +++ /dev/null @@ -1,64 +0,0 @@ - - - diff --git a/app/assets/javascripts/analytics/instance_statistics/components/instance_statistics_count_chart.vue b/app/assets/javascripts/analytics/instance_statistics/components/instance_statistics_count_chart.vue deleted file mode 100644 index e2defe0572d..00000000000 --- a/app/assets/javascripts/analytics/instance_statistics/components/instance_statistics_count_chart.vue +++ /dev/null @@ -1,206 +0,0 @@ - - diff --git a/app/assets/javascripts/analytics/instance_statistics/components/projects_and_groups_chart.vue b/app/assets/javascripts/analytics/instance_statistics/components/projects_and_groups_chart.vue deleted file mode 100644 index 3ffec90fb68..00000000000 --- a/app/assets/javascripts/analytics/instance_statistics/components/projects_and_groups_chart.vue +++ /dev/null @@ -1,224 +0,0 @@ - - diff --git a/app/assets/javascripts/analytics/instance_statistics/components/users_chart.vue b/app/assets/javascripts/analytics/instance_statistics/components/users_chart.vue deleted file mode 100644 index 73940f028a1..00000000000 --- a/app/assets/javascripts/analytics/instance_statistics/components/users_chart.vue +++ /dev/null @@ -1,143 +0,0 @@ - - diff --git a/app/assets/javascripts/analytics/instance_statistics/constants.js b/app/assets/javascripts/analytics/instance_statistics/constants.js deleted file mode 100644 index 846c0ef408b..00000000000 --- a/app/assets/javascripts/analytics/instance_statistics/constants.js +++ /dev/null @@ -1,5 +0,0 @@ -import { getDateInPast } from '~/lib/utils/datetime_utility'; - -export const TOTAL_DAYS_TO_SHOW = 365; -export const TODAY = new Date(); -export const START_DATE = getDateInPast(TODAY, TOTAL_DAYS_TO_SHOW); diff --git a/app/assets/javascripts/analytics/instance_statistics/graphql/fragments/count.fragment.graphql b/app/assets/javascripts/analytics/instance_statistics/graphql/fragments/count.fragment.graphql deleted file mode 100644 index 40cef95c2e7..00000000000 --- a/app/assets/javascripts/analytics/instance_statistics/graphql/fragments/count.fragment.graphql +++ /dev/null @@ -1,4 +0,0 @@ -fragment Count on InstanceStatisticsMeasurement { - count - recordedAt -} diff --git a/app/assets/javascripts/analytics/instance_statistics/graphql/queries/groups.query.graphql b/app/assets/javascripts/analytics/instance_statistics/graphql/queries/groups.query.graphql deleted file mode 100644 index ec56d91ffaa..00000000000 --- a/app/assets/javascripts/analytics/instance_statistics/graphql/queries/groups.query.graphql +++ /dev/null @@ -1,13 +0,0 @@ -#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" -#import "../fragments/count.fragment.graphql" - -query getGroupsCount($first: Int, $after: String) { - groups: instanceStatisticsMeasurements(identifier: GROUPS, first: $first, after: $after) { - nodes { - ...Count - } - pageInfo { - ...PageInfo - } - } -} diff --git a/app/assets/javascripts/analytics/instance_statistics/graphql/queries/instance_count.query.graphql b/app/assets/javascripts/analytics/instance_statistics/graphql/queries/instance_count.query.graphql deleted file mode 100644 index dd22a16cd51..00000000000 --- a/app/assets/javascripts/analytics/instance_statistics/graphql/queries/instance_count.query.graphql +++ /dev/null @@ -1,13 +0,0 @@ -#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" -#import "../fragments/count.fragment.graphql" - -query getCount($identifier: MeasurementIdentifier!, $first: Int, $after: String) { - instanceStatisticsMeasurements(identifier: $identifier, first: $first, after: $after) { - nodes { - ...Count - } - pageInfo { - ...PageInfo - } - } -} diff --git a/app/assets/javascripts/analytics/instance_statistics/graphql/queries/instance_statistics_count.query.graphql b/app/assets/javascripts/analytics/instance_statistics/graphql/queries/instance_statistics_count.query.graphql deleted file mode 100644 index f14c2658674..00000000000 --- a/app/assets/javascripts/analytics/instance_statistics/graphql/queries/instance_statistics_count.query.graphql +++ /dev/null @@ -1,34 +0,0 @@ -#import "../fragments/count.fragment.graphql" - -query getInstanceCounts { - projects: instanceStatisticsMeasurements(identifier: PROJECTS, first: 1) { - nodes { - ...Count - } - } - groups: instanceStatisticsMeasurements(identifier: GROUPS, first: 1) { - nodes { - ...Count - } - } - users: instanceStatisticsMeasurements(identifier: USERS, first: 1) { - nodes { - ...Count - } - } - issues: instanceStatisticsMeasurements(identifier: ISSUES, first: 1) { - nodes { - ...Count - } - } - mergeRequests: instanceStatisticsMeasurements(identifier: MERGE_REQUESTS, first: 1) { - nodes { - ...Count - } - } - pipelines: instanceStatisticsMeasurements(identifier: PIPELINES, first: 1) { - nodes { - ...Count - } - } -} diff --git a/app/assets/javascripts/analytics/instance_statistics/graphql/queries/projects.query.graphql b/app/assets/javascripts/analytics/instance_statistics/graphql/queries/projects.query.graphql deleted file mode 100644 index 0845b703435..00000000000 --- a/app/assets/javascripts/analytics/instance_statistics/graphql/queries/projects.query.graphql +++ /dev/null @@ -1,13 +0,0 @@ -#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" -#import "../fragments/count.fragment.graphql" - -query getProjectsCount($first: Int, $after: String) { - projects: instanceStatisticsMeasurements(identifier: PROJECTS, first: $first, after: $after) { - nodes { - ...Count - } - pageInfo { - ...PageInfo - } - } -} diff --git a/app/assets/javascripts/analytics/instance_statistics/graphql/queries/users.query.graphql b/app/assets/javascripts/analytics/instance_statistics/graphql/queries/users.query.graphql deleted file mode 100644 index 6235e36eb89..00000000000 --- a/app/assets/javascripts/analytics/instance_statistics/graphql/queries/users.query.graphql +++ /dev/null @@ -1,13 +0,0 @@ -#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" -#import "../fragments/count.fragment.graphql" - -query getUsersCount($first: Int, $after: String) { - users: instanceStatisticsMeasurements(identifier: USERS, first: $first, after: $after) { - nodes { - ...Count - } - pageInfo { - ...PageInfo - } - } -} diff --git a/app/assets/javascripts/analytics/instance_statistics/index.js b/app/assets/javascripts/analytics/instance_statistics/index.js deleted file mode 100644 index 0d7dcf6ace8..00000000000 --- a/app/assets/javascripts/analytics/instance_statistics/index.js +++ /dev/null @@ -1,24 +0,0 @@ -import Vue from 'vue'; -import VueApollo from 'vue-apollo'; -import createDefaultClient from '~/lib/graphql'; -import InstanceStatisticsApp from './components/app.vue'; - -Vue.use(VueApollo); - -const apolloProvider = new VueApollo({ - defaultClient: createDefaultClient(), -}); - -export default () => { - const el = document.getElementById('js-instance-statistics-app'); - - if (!el) return false; - - return new Vue({ - el, - apolloProvider, - render(h) { - return h(InstanceStatisticsApp); - }, - }); -}; diff --git a/app/assets/javascripts/analytics/instance_statistics/utils.js b/app/assets/javascripts/analytics/instance_statistics/utils.js deleted file mode 100644 index 396962ffad6..00000000000 --- a/app/assets/javascripts/analytics/instance_statistics/utils.js +++ /dev/null @@ -1,68 +0,0 @@ -import { masks } from 'dateformat'; -import { get } from 'lodash'; -import { formatDate } from '~/lib/utils/datetime_utility'; - -const { isoDate } = masks; - -/** - * Takes an array of items and returns one item per month with the average of the `count`s from that month - * @param {Array} items - * @param {Number} items[index].count value to be averaged - * @param {String} items[index].recordedAt item dateTime time stamp to be collected into a month - * @param {Object} options - * @param {Object} options.shouldRound an option to specify whether the retuned averages should be rounded - * @return {Array} items collected into [month, average], - * where month is a dateTime string representing the first of the given month - * and average is the average of the count - */ -export function getAverageByMonth(items = [], options = {}) { - const { shouldRound = false } = options; - const itemsMap = items.reduce((memo, item) => { - const { count, recordedAt } = item; - const date = new Date(recordedAt); - const month = formatDate(new Date(date.getFullYear(), date.getMonth(), 1), isoDate); - if (memo[month]) { - const { sum, recordCount } = memo[month]; - return { ...memo, [month]: { sum: sum + count, recordCount: recordCount + 1 } }; - } - - return { ...memo, [month]: { sum: count, recordCount: 1 } }; - }, {}); - - return Object.keys(itemsMap).map((month) => { - const { sum, recordCount } = itemsMap[month]; - const avg = sum / recordCount; - if (shouldRound) { - return [month, Math.round(avg)]; - } - - return [month, avg]; - }); -} - -/** - * Takes an array of instance counts and returns the last item in the list - * @param {Array} arr array of instance counts in the form { count: Number, recordedAt: date String } - * @return {String} the 'recordedAt' value of the earliest item - */ -export const getEarliestDate = (arr = []) => { - const len = arr.length; - return get(arr, `[${len - 1}].recordedAt`, null); -}; - -/** - * Takes an array of queries and produces an object with the query identifier as key - * and a supplied defaultValue as its value - * @param {Array} queries array of chart query configs, - * see ./analytics/instance_statistics/components/charts_config.js - * @param {any} defaultValue value to set each identifier to - * @return {Object} key value pair of the form { queryIdentifier: defaultValue } - */ -export const generateDataKeys = (queries, defaultValue) => - queries.reduce( - (acc, { identifier }) => ({ - ...acc, - [identifier]: defaultValue, - }), - {}, - ); diff --git a/app/assets/javascripts/analytics/usage_trends/components/app.vue b/app/assets/javascripts/analytics/usage_trends/components/app.vue new file mode 100644 index 00000000000..4c5ddd7f458 --- /dev/null +++ b/app/assets/javascripts/analytics/usage_trends/components/app.vue @@ -0,0 +1,41 @@ + + + diff --git a/app/assets/javascripts/analytics/usage_trends/components/charts_config.js b/app/assets/javascripts/analytics/usage_trends/components/charts_config.js new file mode 100644 index 00000000000..014f823cdc4 --- /dev/null +++ b/app/assets/javascripts/analytics/usage_trends/components/charts_config.js @@ -0,0 +1,106 @@ +import { s__, __ } from '~/locale'; +import query from '../graphql/queries/usage_count.query.graphql'; + +const noDataMessage = s__('UsageTrends|No data available.'); + +export default [ + { + loadChartError: s__( + 'UsageTrends|Could not load the projects and groups chart. Please refresh the page to try again.', + ), + noDataMessage, + chartTitle: s__('UsageTrends|Total projects & groups'), + yAxisTitle: s__('UsageTrends|Total projects & groups'), + xAxisTitle: s__('UsageTrends|Month'), + queries: [ + { + query, + title: s__('UsageTrends|Total projects'), + identifier: 'PROJECTS', + loadError: s__('UsageTrends|There was an error fetching the projects. Please try again.'), + }, + { + query, + title: s__('UsageTrends|Total groups'), + identifier: 'GROUPS', + loadError: s__('UsageTrends|There was an error fetching the groups. Please try again.'), + }, + ], + }, + { + loadChartError: s__( + 'UsageTrends|Could not load the pipelines chart. Please refresh the page to try again.', + ), + noDataMessage, + chartTitle: s__('UsageTrends|Pipelines'), + yAxisTitle: s__('UsageTrends|Items'), + xAxisTitle: s__('UsageTrends|Month'), + queries: [ + { + query, + title: s__('UsageTrends|Pipelines total'), + identifier: 'PIPELINES', + loadError: s__( + 'UsageTrends|There was an error fetching the total pipelines. Please try again.', + ), + }, + { + query, + title: s__('UsageTrends|Pipelines succeeded'), + identifier: 'PIPELINES_SUCCEEDED', + loadError: s__( + 'UsageTrends|There was an error fetching the successful pipelines. Please try again.', + ), + }, + { + query, + title: s__('UsageTrends|Pipelines failed'), + identifier: 'PIPELINES_FAILED', + loadError: s__( + 'UsageTrends|There was an error fetching the failed pipelines. Please try again.', + ), + }, + { + query, + title: s__('UsageTrends|Pipelines canceled'), + identifier: 'PIPELINES_CANCELED', + loadError: s__( + 'UsageTrends|There was an error fetching the cancelled pipelines. Please try again.', + ), + }, + { + query, + title: s__('UsageTrends|Pipelines skipped'), + identifier: 'PIPELINES_SKIPPED', + loadError: s__( + 'UsageTrends|There was an error fetching the skipped pipelines. Please try again.', + ), + }, + ], + }, + { + loadChartError: s__( + 'UsageTrends|Could not load the issues and merge requests chart. Please refresh the page to try again.', + ), + noDataMessage, + chartTitle: s__('UsageTrends|Issues & Merge Requests'), + yAxisTitle: s__('UsageTrends|Items'), + xAxisTitle: s__('UsageTrends|Month'), + queries: [ + { + query, + title: __('Issues'), + identifier: 'ISSUES', + loadError: s__('UsageTrends|There was an error fetching the issues. Please try again.'), + }, + { + query, + title: __('Merge requests'), + identifier: 'MERGE_REQUESTS', + loadError: s__( + 'UsageTrends|There was an error fetching the merge requests. Please try again.', + ), + }, + ], + }, +]; diff --git a/app/assets/javascripts/analytics/usage_trends/components/usage_counts.vue b/app/assets/javascripts/analytics/usage_trends/components/usage_counts.vue new file mode 100644 index 00000000000..0630cca93ae --- /dev/null +++ b/app/assets/javascripts/analytics/usage_trends/components/usage_counts.vue @@ -0,0 +1,63 @@ + + + diff --git a/app/assets/javascripts/analytics/usage_trends/components/usage_trends_count_chart.vue b/app/assets/javascripts/analytics/usage_trends/components/usage_trends_count_chart.vue new file mode 100644 index 00000000000..8d7761694d1 --- /dev/null +++ b/app/assets/javascripts/analytics/usage_trends/components/usage_trends_count_chart.vue @@ -0,0 +1,206 @@ + + diff --git a/app/assets/javascripts/analytics/usage_trends/components/users_chart.vue b/app/assets/javascripts/analytics/usage_trends/components/users_chart.vue new file mode 100644 index 00000000000..09dfcddcb73 --- /dev/null +++ b/app/assets/javascripts/analytics/usage_trends/components/users_chart.vue @@ -0,0 +1,143 @@ + + diff --git a/app/assets/javascripts/analytics/usage_trends/constants.js b/app/assets/javascripts/analytics/usage_trends/constants.js new file mode 100644 index 00000000000..846c0ef408b --- /dev/null +++ b/app/assets/javascripts/analytics/usage_trends/constants.js @@ -0,0 +1,5 @@ +import { getDateInPast } from '~/lib/utils/datetime_utility'; + +export const TOTAL_DAYS_TO_SHOW = 365; +export const TODAY = new Date(); +export const START_DATE = getDateInPast(TODAY, TOTAL_DAYS_TO_SHOW); diff --git a/app/assets/javascripts/analytics/usage_trends/graphql/fragments/count.fragment.graphql b/app/assets/javascripts/analytics/usage_trends/graphql/fragments/count.fragment.graphql new file mode 100644 index 00000000000..2bde5973600 --- /dev/null +++ b/app/assets/javascripts/analytics/usage_trends/graphql/fragments/count.fragment.graphql @@ -0,0 +1,4 @@ +fragment Count on UsageTrendsMeasurement { + count + recordedAt +} diff --git a/app/assets/javascripts/analytics/usage_trends/graphql/queries/usage_count.query.graphql b/app/assets/javascripts/analytics/usage_trends/graphql/queries/usage_count.query.graphql new file mode 100644 index 00000000000..2a5546efb68 --- /dev/null +++ b/app/assets/javascripts/analytics/usage_trends/graphql/queries/usage_count.query.graphql @@ -0,0 +1,13 @@ +#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" +#import "../fragments/count.fragment.graphql" + +query getCount($identifier: MeasurementIdentifier!, $first: Int, $after: String) { + usageTrendsMeasurements(identifier: $identifier, first: $first, after: $after) { + nodes { + ...Count + } + pageInfo { + ...PageInfo + } + } +} diff --git a/app/assets/javascripts/analytics/usage_trends/graphql/queries/usage_trends_count.query.graphql b/app/assets/javascripts/analytics/usage_trends/graphql/queries/usage_trends_count.query.graphql new file mode 100644 index 00000000000..8cadcfae380 --- /dev/null +++ b/app/assets/javascripts/analytics/usage_trends/graphql/queries/usage_trends_count.query.graphql @@ -0,0 +1,34 @@ +#import "../fragments/count.fragment.graphql" + +query getInstanceCounts { + projects: usageTrendsMeasurements(identifier: PROJECTS, first: 1) { + nodes { + ...Count + } + } + groups: usageTrendsMeasurements(identifier: GROUPS, first: 1) { + nodes { + ...Count + } + } + users: usageTrendsMeasurements(identifier: USERS, first: 1) { + nodes { + ...Count + } + } + issues: usageTrendsMeasurements(identifier: ISSUES, first: 1) { + nodes { + ...Count + } + } + mergeRequests: usageTrendsMeasurements(identifier: MERGE_REQUESTS, first: 1) { + nodes { + ...Count + } + } + pipelines: usageTrendsMeasurements(identifier: PIPELINES, first: 1) { + nodes { + ...Count + } + } +} diff --git a/app/assets/javascripts/analytics/usage_trends/graphql/queries/users.query.graphql b/app/assets/javascripts/analytics/usage_trends/graphql/queries/users.query.graphql new file mode 100644 index 00000000000..7c02ac49a42 --- /dev/null +++ b/app/assets/javascripts/analytics/usage_trends/graphql/queries/users.query.graphql @@ -0,0 +1,13 @@ +#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" +#import "../fragments/count.fragment.graphql" + +query getUsersCount($first: Int, $after: String) { + users: usageTrendsMeasurements(identifier: USERS, first: $first, after: $after) { + nodes { + ...Count + } + pageInfo { + ...PageInfo + } + } +} diff --git a/app/assets/javascripts/analytics/usage_trends/index.js b/app/assets/javascripts/analytics/usage_trends/index.js new file mode 100644 index 00000000000..d1880b09f15 --- /dev/null +++ b/app/assets/javascripts/analytics/usage_trends/index.js @@ -0,0 +1,24 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import createDefaultClient from '~/lib/graphql'; +import UsageTrendsApp from './components/app.vue'; + +Vue.use(VueApollo); + +const apolloProvider = new VueApollo({ + defaultClient: createDefaultClient(), +}); + +export default () => { + const el = document.getElementById('js-usage-trends-app'); + + if (!el) return false; + + return new Vue({ + el, + apolloProvider, + render(h) { + return h(UsageTrendsApp); + }, + }); +}; diff --git a/app/assets/javascripts/analytics/usage_trends/utils.js b/app/assets/javascripts/analytics/usage_trends/utils.js new file mode 100644 index 00000000000..91907877ed6 --- /dev/null +++ b/app/assets/javascripts/analytics/usage_trends/utils.js @@ -0,0 +1,68 @@ +import { masks } from 'dateformat'; +import { get } from 'lodash'; +import { formatDate } from '~/lib/utils/datetime_utility'; + +const { isoDate } = masks; + +/** + * Takes an array of items and returns one item per month with the average of the `count`s from that month + * @param {Array} items + * @param {Number} items[index].count value to be averaged + * @param {String} items[index].recordedAt item dateTime time stamp to be collected into a month + * @param {Object} options + * @param {Object} options.shouldRound an option to specify whether the retuned averages should be rounded + * @return {Array} items collected into [month, average], + * where month is a dateTime string representing the first of the given month + * and average is the average of the count + */ +export function getAverageByMonth(items = [], options = {}) { + const { shouldRound = false } = options; + const itemsMap = items.reduce((memo, item) => { + const { count, recordedAt } = item; + const date = new Date(recordedAt); + const month = formatDate(new Date(date.getFullYear(), date.getMonth(), 1), isoDate); + if (memo[month]) { + const { sum, recordCount } = memo[month]; + return { ...memo, [month]: { sum: sum + count, recordCount: recordCount + 1 } }; + } + + return { ...memo, [month]: { sum: count, recordCount: 1 } }; + }, {}); + + return Object.keys(itemsMap).map((month) => { + const { sum, recordCount } = itemsMap[month]; + const avg = sum / recordCount; + if (shouldRound) { + return [month, Math.round(avg)]; + } + + return [month, avg]; + }); +} + +/** + * Takes an array of usage counts and returns the last item in the list + * @param {Array} arr array of usage counts in the form { count: Number, recordedAt: date String } + * @return {String} the 'recordedAt' value of the earliest item + */ +export const getEarliestDate = (arr = []) => { + const len = arr.length; + return get(arr, `[${len - 1}].recordedAt`, null); +}; + +/** + * Takes an array of queries and produces an object with the query identifier as key + * and a supplied defaultValue as its value + * @param {Array} queries array of chart query configs, + * see ./analytics/usage_trends/components/charts_config.js + * @param {any} defaultValue value to set each identifier to + * @return {Object} key value pair of the form { queryIdentifier: defaultValue } + */ +export const generateDataKeys = (queries, defaultValue) => + queries.reduce( + (acc, { identifier }) => ({ + ...acc, + [identifier]: defaultValue, + }), + {}, + ); diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js index c7e6b98a934..48005787d81 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -24,6 +24,7 @@ const Api = { projectPackagesPath: '/api/:version/projects/:id/packages', projectPackagePath: '/api/:version/projects/:id/packages/:package_id', groupProjectsPath: '/api/:version/groups/:id/projects.json', + groupSharePath: '/api/:version/groups/:id/share', projectsPath: '/api/:version/projects.json', projectPath: '/api/:version/projects/:id', forkedProjectsPath: '/api/:version/projects/:id/forks', @@ -39,6 +40,7 @@ const Api = { projectRunnersPath: '/api/:version/projects/:id/runners', projectProtectedBranchesPath: '/api/:version/projects/:id/protected_branches', projectSearchPath: '/api/:version/projects/:id/search', + projectSharePath: '/api/:version/projects/:id/share', projectMilestonesPath: '/api/:version/projects/:id/milestones', projectIssuePath: '/api/:version/projects/:id/issues/:issue_iid', mergeRequestsPath: '/api/:version/merge_requests', @@ -365,6 +367,16 @@ const Api = { }); }, + projectShareWithGroup(id, options = {}) { + const url = Api.buildUrl(Api.projectSharePath).replace(':id', encodeURIComponent(id)); + + return axios.post(url, { + expires_at: options.expires_at, + group_access: options.group_access, + group_id: options.group_id, + }); + }, + projectMilestones(id, params = {}) { const url = Api.buildUrl(Api.projectMilestonesPath).replace(':id', encodeURIComponent(id)); @@ -426,6 +438,16 @@ const Api = { }); }, + groupShareWithGroup(id, options = {}) { + const url = Api.buildUrl(Api.groupSharePath).replace(':id', encodeURIComponent(id)); + + return axios.post(url, { + expires_at: options.expires_at, + group_access: options.group_access, + group_id: options.group_id, + }); + }, + commit(id, sha, params = {}) { const url = Api.buildUrl(this.commitPath) .replace(':id', encodeURIComponent(id)) @@ -446,7 +468,7 @@ const Api = { applySuggestion(id, message = '') { const url = Api.buildUrl(Api.applySuggestionPath).replace(':id', encodeURIComponent(id)); - const params = gon.features?.suggestionsCustomCommit ? { commit_message: message } : false; + const params = { commit_message: message }; return axios.put(url, params); }, diff --git a/app/assets/javascripts/authentication/two_factor_auth/components/recovery_codes.vue b/app/assets/javascripts/authentication/two_factor_auth/components/recovery_codes.vue index 0e589d98668..55642aa64db 100644 --- a/app/assets/javascripts/authentication/two_factor_auth/components/recovery_codes.vue +++ b/app/assets/javascripts/authentication/two_factor_auth/components/recovery_codes.vue @@ -162,7 +162,7 @@ export default { :href="profileAccountPath" :disabled="proceedButtonDisabled" :title="$options.i18n.proceedButton" - variant="success" + variant="confirm" data-qa-selector="proceed_button" data-track-event="click_button" :data-track-label="`${$options.trackingLabelPrefix}proceed_button`" diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js index 6fb90551ed7..dbdc7e43d2d 100644 --- a/app/assets/javascripts/awards_handler.js +++ b/app/assets/javascripts/awards_handler.js @@ -5,12 +5,14 @@ import $ from 'jquery'; import Cookies from 'js-cookie'; import { uniq } from 'lodash'; import * as Emoji from '~/emoji'; +import { scrollToElement } from '~/lib/utils/common_utils'; import { dispose, fixTitle } from '~/tooltips'; import { deprecatedCreateFlash as flash } from './flash'; import axios from './lib/utils/axios_utils'; import { isInVueNoteablePage } from './lib/utils/dom_utils'; import { __ } from './locale'; +window.axios = axios; const animationEndEventString = 'animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd'; const transitionEndEventString = 'transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd'; @@ -495,12 +497,7 @@ export class AwardsHandler { } scrollToAwards() { - const options = { - scrollTop: $('.awards').offset().top - 110, - }; - - // eslint-disable-next-line no-jquery/no-animate - return $('body, html').animate(options, 200); + scrollToElement('.awards', { offset: -110 }); } addEmojiToFrequentlyUsedList(emoji) { diff --git a/app/assets/javascripts/badges/components/badge.vue b/app/assets/javascripts/badges/components/badge.vue index c3512773457..9e5d70075f3 100644 --- a/app/assets/javascripts/badges/components/badge.vue +++ b/app/assets/javascripts/badges/components/badge.vue @@ -96,7 +96,7 @@ export default { v-gl-tooltip.hover :title="s__('Badges|Reload badge image')" category="tertiary" - variant="success" + variant="confirm" type="button" icon="retry" size="small" diff --git a/app/assets/javascripts/badges/components/badge_form.vue b/app/assets/javascripts/badges/components/badge_form.vue index 20541ad8ccc..b65a8b4fa9c 100644 --- a/app/assets/javascripts/badges/components/badge_form.vue +++ b/app/assets/javascripts/badges/components/badge_form.vue @@ -225,7 +225,7 @@ export default { @@ -233,7 +233,7 @@ export default {
- + {{ s__('Badges|Add badge') }}
diff --git a/app/assets/javascripts/batch_comments/components/publish_button.vue b/app/assets/javascripts/batch_comments/components/publish_button.vue index 8568dba5947..2a7be605003 100644 --- a/app/assets/javascripts/batch_comments/components/publish_button.vue +++ b/app/assets/javascripts/batch_comments/components/publish_button.vue @@ -40,7 +40,8 @@ export default {