From 983a0bba5d2a042c4a3bbb22432ec192c7501d82 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Mon, 20 Apr 2020 18:38:24 +0000 Subject: Add latest changes from gitlab-org/gitlab@12-10-stable-ee --- .../setup_metrics_and_profiling.js | 8 + .../blob/components/blob_edit_content.vue | 9 +- .../ci_variable_list/components/ci_key_field.vue | 169 +++++++++++ .../components/ci_variable_autocomplete_tokens.js | 29 ++ .../components/ci_variable_modal.vue | 100 +++++-- .../javascripts/ci_variable_list/constants.js | 5 + .../clusters/services/application_state_machine.js | 3 +- app/assets/javascripts/contextual_sidebar.js | 4 +- .../javascripts/create_merge_request_dropdown.js | 4 +- .../cycle_analytics/cycle_analytics_bundle.js | 6 - .../diffs/components/diff_table_cell.vue | 8 +- app/assets/javascripts/diffs/constants.js | 1 + .../diffs/store/getters_versions_dropdowns.js | 20 +- app/assets/javascripts/diffs/store/mutations.js | 17 +- app/assets/javascripts/diffs/store/utils.js | 10 +- app/assets/javascripts/filterable_list.js | 4 +- .../filtered_search/filtered_search_manager.js | 43 ++- .../javascripts/groups/components/groups.vue | 2 +- .../jira_import/components/jira_import_app.vue | 115 +++++++- .../jira_import/components/jira_import_form.vue | 54 +++- .../components/jira_import_progress.vue | 66 +++++ .../jira_import/components/jira_import_setup.vue | 23 +- app/assets/javascripts/jira_import/index.js | 3 + .../queries/getJiraProjects.query.graphql | 14 - .../queries/get_jira_import_details.query.graphql | 12 + .../queries/initiate_jira_import.mutation.graphql | 11 + .../queries/jira_import.fragment.graphql | 7 + app/assets/javascripts/jira_import/utils.js | 10 + app/assets/javascripts/lazy_loader.js | 8 +- app/assets/javascripts/lib/utils/keycodes.js | 7 +- .../javascripts/lib/utils/unit_format/index.js | 19 +- app/assets/javascripts/logs/constants.js | 2 +- app/assets/javascripts/main.js | 12 + .../monitoring/components/charts/annotations.js | 97 ++++--- .../monitoring/components/charts/empty_chart.vue | 6 - .../monitoring/components/charts/options.js | 6 +- .../monitoring/components/charts/time_series.vue | 38 +-- .../monitoring/components/dashboard.vue | 312 ++++++++++----------- .../monitoring/components/panel_type.vue | 10 +- app/assets/javascripts/monitoring/constants.js | 28 +- .../queries/getAnnotations.query.graphql | 29 +- .../javascripts/monitoring/stores/actions.js | 29 +- .../monitoring/stores/mutation_types.js | 1 - app/assets/javascripts/monitoring/stores/utils.js | 35 ++- .../notes/components/note_awards_list.vue | 177 +----------- .../metrics_and_profiling/index.js | 9 +- .../application_settings/payload_previewer.js | 62 ++++ .../application_settings/usage_ping_payload.js | 62 ---- .../permissions/components/settings_panel.vue | 44 ++- .../projects/default_project_templates.js | 4 + .../explorer/components/project_policy_alert.vue | 1 - .../javascripts/registry/explorer/constants.js | 45 ++- .../javascripts/registry/explorer/pages/list.vue | 168 ++++++----- .../registry/explorer/stores/actions.js | 8 +- .../registry/explorer/stores/mutation_types.js | 1 + .../registry/explorer/stores/mutations.js | 13 +- .../javascripts/releases/components/app_edit.vue | 12 +- .../releases/components/asset_links_form.vue | 12 +- .../repository/components/breadcrumbs.vue | 4 +- .../repository/components/table/parent_row.vue | 2 +- .../repository/components/table/row.vue | 2 +- app/assets/javascripts/repository/graphql.js | 2 +- app/assets/javascripts/repository/index.js | 4 +- app/assets/javascripts/repository/router.js | 2 +- app/assets/javascripts/snippet/snippet_edit.js | 13 +- .../javascripts/snippets/components/edit.vue | 211 ++++++++++++++ .../components/snippet_description_edit.vue | 2 +- .../snippets/components/snippet_header.vue | 14 +- app/assets/javascripts/snippets/index.js | 9 +- .../mutations/createSnippet.mutation.graphql | 8 + .../mutations/updateSnippet.mutation.graphql | 8 + .../components/invalid_content_message.vue | 29 ++ .../components/publish_toolbar.vue | 16 +- .../components/saved_changes_message.vue | 20 +- .../components/static_site_editor.vue | 85 ++++-- .../components/submit_changes_error.vue | 24 ++ app/assets/javascripts/static_site_editor/index.js | 11 +- .../static_site_editor/store/actions.js | 7 +- .../static_site_editor/store/mutation_types.js | 1 + .../static_site_editor/store/mutations.js | 7 +- .../javascripts/static_site_editor/store/state.js | 2 + app/assets/javascripts/user_popovers.js | 5 +- .../vue_shared/components/awards_list.vue | 178 ++++++++++++ .../vue_shared/components/clone_dropdown.vue | 12 +- .../date_time_picker/date_time_picker.vue | 2 +- .../vue_shared/components/form/title.vue | 2 +- .../components/sidebar/toggle_sidebar.vue | 2 +- .../components/user_popover/user_popover.vue | 40 +-- .../stylesheets/components/related_items_list.scss | 25 +- app/assets/stylesheets/pages/cycle_analytics.scss | 4 - app/assets/stylesheets/pages/prometheus.scss | 6 + app/assets/stylesheets/utilities.scss | 5 + app/controllers/admin/runners_controller.rb | 10 +- app/controllers/application_controller.rb | 2 +- .../clusters/applications_controller.rb | 2 +- .../concerns/enforces_two_factor_authentication.rb | 8 +- app/controllers/concerns/integrations_actions.rb | 4 +- app/controllers/dashboard/projects_controller.rb | 16 +- app/controllers/explore/projects_controller.rb | 17 +- app/controllers/groups/milestones_controller.rb | 3 + .../groups/settings/ci_cd_controller.rb | 4 +- app/controllers/projects/forks_controller.rb | 19 +- app/controllers/projects/import/jira_controller.rb | 13 +- app/controllers/projects/issues_controller.rb | 11 +- .../projects/merge_requests_controller.rb | 7 +- app/controllers/projects/milestones_controller.rb | 3 + .../projects/settings/ci_cd_controller.rb | 3 +- app/controllers/projects_controller.rb | 4 + .../repositories/git_http_controller.rb | 12 +- app/controllers/users_controller.rb | 7 +- app/finders/autocomplete/move_to_project_finder.rb | 3 +- app/finders/autocomplete/routes_finder.rb | 47 ++++ .../metrics/dashboards/annotations_finder.rb | 42 +++ .../metrics/dashboards/annotation_resolver.rb | 28 ++ app/graphql/types/merge_request_type.rb | 2 +- app/graphql/types/metrics/dashboard_type.rb | 5 + .../types/metrics/dashboards/annotation_type.rb | 31 ++ app/graphql/types/project_type.rb | 2 +- app/helpers/preferences_helper.rb | 34 +-- app/helpers/projects_helper.rb | 2 + app/mailers/emails/issues.rb | 14 + app/mailers/previews/notify_preview.rb | 4 + app/models/ci/bridge.rb | 1 - app/models/ci/build.rb | 19 +- app/models/ci/job_artifact.rb | 15 +- app/models/ci/processable.rb | 6 + app/models/ci/runner.rb | 6 + app/models/clusters/applications/fluentd.rb | 101 +++++++ app/models/clusters/applications/ingress.rb | 11 +- app/models/clusters/cluster.rb | 4 +- app/models/concerns/ci/has_ref.rb | 2 +- app/models/concerns/ci/pipeline_delegator.rb | 20 -- app/models/concerns/issuable.rb | 21 +- .../concerns/notification_branch_selection.rb | 14 +- .../concerns/project_features_compatibility.rb | 4 + app/models/diff_discussion.rb | 1 + app/models/diff_note_position.rb | 2 + app/models/group.rb | 2 +- app/models/import_failure.rb | 7 + app/models/jira_import_state.rb | 17 ++ app/models/lfs_object.rb | 13 - app/models/merge_request.rb | 22 +- app/models/merge_request_diff.rb | 19 +- app/models/metrics/dashboard/annotation.rb | 5 + app/models/note.rb | 2 +- app/models/project.rb | 19 +- app/models/project_feature.rb | 17 +- app/models/project_import_state.rb | 4 + .../project_services/chat_notification_service.rb | 10 +- app/models/project_services/discord_service.rb | 2 +- .../project_services/emails_on_push_service.rb | 2 +- .../project_services/hangouts_chat_service.rb | 2 +- .../project_services/issue_tracker_service.rb | 2 +- .../project_services/microsoft_teams_service.rb | 2 +- .../project_services/pipelines_email_service.rb | 2 +- app/models/project_services/prometheus_service.rb | 6 - .../project_services/unify_circuit_service.rb | 2 +- app/models/resource_label_event.rb | 2 +- app/models/resource_milestone_event.rb | 6 +- app/models/route.rb | 4 +- app/models/terraform/state.rb | 15 + app/models/user.rb | 17 +- app/models/user_type_enums.rb | 2 +- app/policies/global_policy.rb | 7 + app/policies/group_policy.rb | 1 + app/presenters/ci/pipeline_presenter.rb | 14 +- app/serializers/analytics_summary_entity.rb | 8 + app/serializers/cluster_application_entity.rb | 3 + app/serializers/discussion_entity.rb | 15 + app/serializers/merge_request_basic_entity.rb | 2 +- .../merge_request_poll_cached_widget_entity.rb | 2 +- app/serializers/merge_request_serializer.rb | 4 + app/serializers/route_entity.rb | 8 + app/serializers/route_serializer.rb | 5 + app/services/auto_merge/base_service.rb | 8 + .../merge_when_pipeline_succeeds_service.rb | 4 +- app/services/auto_merge_service.rb | 37 ++- app/services/clusters/applications/base_service.rb | 12 + app/services/concerns/deploy_token_methods.rb | 8 + app/services/emails/destroy_service.rb | 2 +- app/services/git/branch_push_service.rb | 2 + app/services/git/process_ref_changes_service.rb | 8 + .../groups/deploy_tokens/create_service.rb | 6 +- .../groups/import_export/import_service.rb | 10 +- app/services/groups/transfer_service.rb | 22 +- app/services/issues/export_csv_service.rb | 77 +++++ app/services/jira_import/start_import_service.rb | 8 +- .../merge_requests/merge_orchestration_service.rb | 40 +++ .../merge_requests/pushed_branches_service.rb | 32 +++ app/services/merge_requests/update_service.rb | 19 +- .../metrics/dashboard/transient_embed_service.rb | 5 + .../personal_access_tokens/create_service.rb | 31 ++ app/services/pod_logs/base_service.rb | 6 +- app/services/pod_logs/elasticsearch_service.rb | 21 +- app/services/pod_logs/kubernetes_service.rb | 19 +- .../projects/deploy_tokens/create_service.rb | 6 +- .../resources/create_access_token_service.rb | 111 ++++++++ app/services/snippets/create_service.rb | 4 +- app/services/terraform/remote_state_handler.rb | 77 +++++ app/services/users/build_service.rb | 12 +- app/uploaders/records_uploads.rb | 23 +- app/uploaders/terraform/state_uploader.rb | 2 +- app/views/admin/runners/show.html.haml | 2 +- app/views/ci/variables/_index.html.haml | 2 +- app/views/layouts/_page.html.haml | 3 +- .../header/_current_user_dropdown.html.haml | 2 +- app/views/notify/issues_csv_email.html.haml | 9 + app/views/notify/issues_csv_email.text.erb | 5 + app/views/projects/_flash_messages.html.haml | 2 + app/views/projects/_home_panel.html.haml | 1 + app/views/projects/commits/_commits.html.haml | 9 +- app/views/projects/cycle_analytics/show.html.haml | 40 ++- app/views/projects/import/jira/show.html.haml | 7 +- app/views/projects/issues/_nav_btns.html.haml | 4 +- .../projects/issues/export_csv/_button.html.haml | 4 + .../projects/issues/export_csv/_modal.html.haml | 22 ++ app/views/projects/services/_form.html.haml | 9 +- .../projects/services/prometheus/_help.html.haml | 6 +- .../settings/operations/_prometheus.html.haml | 6 +- app/views/shared/projects/_project.html.haml | 4 + app/views/shared/runners/_form.html.haml | 11 + app/views/shared/snippets/_form.html.haml | 112 ++++---- app/workers/all_queues.yml | 9 +- app/workers/concerns/cronjob_queue.rb | 10 + app/workers/create_commit_signature_worker.rb | 13 +- app/workers/expire_pipeline_cache_worker.rb | 4 +- app/workers/export_csv_worker.rb | 21 ++ .../jira_import/stage/finish_import_worker.rb | 2 +- app/workers/project_daily_statistics_worker.rb | 1 + 229 files changed, 3346 insertions(+), 1222 deletions(-) create mode 100644 app/assets/javascripts/admin/application_settings/setup_metrics_and_profiling.js create mode 100644 app/assets/javascripts/ci_variable_list/components/ci_key_field.vue create mode 100644 app/assets/javascripts/ci_variable_list/components/ci_variable_autocomplete_tokens.js create mode 100644 app/assets/javascripts/jira_import/components/jira_import_progress.vue delete mode 100644 app/assets/javascripts/jira_import/queries/getJiraProjects.query.graphql create mode 100644 app/assets/javascripts/jira_import/queries/get_jira_import_details.query.graphql create mode 100644 app/assets/javascripts/jira_import/queries/initiate_jira_import.mutation.graphql create mode 100644 app/assets/javascripts/jira_import/queries/jira_import.fragment.graphql create mode 100644 app/assets/javascripts/jira_import/utils.js create mode 100644 app/assets/javascripts/pages/admin/application_settings/payload_previewer.js delete mode 100644 app/assets/javascripts/pages/admin/application_settings/usage_ping_payload.js create mode 100644 app/assets/javascripts/snippets/components/edit.vue create mode 100644 app/assets/javascripts/snippets/mutations/createSnippet.mutation.graphql create mode 100644 app/assets/javascripts/snippets/mutations/updateSnippet.mutation.graphql create mode 100644 app/assets/javascripts/static_site_editor/components/invalid_content_message.vue create mode 100644 app/assets/javascripts/static_site_editor/components/submit_changes_error.vue create mode 100644 app/assets/javascripts/vue_shared/components/awards_list.vue create mode 100644 app/finders/autocomplete/routes_finder.rb create mode 100644 app/finders/metrics/dashboards/annotations_finder.rb create mode 100644 app/graphql/resolvers/metrics/dashboards/annotation_resolver.rb create mode 100644 app/graphql/types/metrics/dashboards/annotation_type.rb create mode 100644 app/models/clusters/applications/fluentd.rb delete mode 100644 app/models/concerns/ci/pipeline_delegator.rb create mode 100644 app/serializers/route_entity.rb create mode 100644 app/serializers/route_serializer.rb create mode 100644 app/services/issues/export_csv_service.rb create mode 100644 app/services/merge_requests/merge_orchestration_service.rb create mode 100644 app/services/merge_requests/pushed_branches_service.rb create mode 100644 app/services/personal_access_tokens/create_service.rb create mode 100644 app/services/resources/create_access_token_service.rb create mode 100644 app/services/terraform/remote_state_handler.rb create mode 100644 app/views/notify/issues_csv_email.html.haml create mode 100644 app/views/notify/issues_csv_email.text.erb create mode 100644 app/views/projects/issues/export_csv/_button.html.haml create mode 100644 app/views/projects/issues/export_csv/_modal.html.haml create mode 100644 app/workers/export_csv_worker.rb (limited to 'app') diff --git a/app/assets/javascripts/admin/application_settings/setup_metrics_and_profiling.js b/app/assets/javascripts/admin/application_settings/setup_metrics_and_profiling.js new file mode 100644 index 00000000000..b4803be4d52 --- /dev/null +++ b/app/assets/javascripts/admin/application_settings/setup_metrics_and_profiling.js @@ -0,0 +1,8 @@ +import PayloadPreviewer from '~/pages/admin/application_settings/payload_previewer'; + +export default () => { + new PayloadPreviewer( + document.querySelector('.js-usage-ping-payload-trigger'), + document.querySelector('.js-usage-ping-payload'), + ).init(); +}; diff --git a/app/assets/javascripts/blob/components/blob_edit_content.vue b/app/assets/javascripts/blob/components/blob_edit_content.vue index 9a30ed93330..056b4ea4aa8 100644 --- a/app/assets/javascripts/blob/components/blob_edit_content.vue +++ b/app/assets/javascripts/blob/components/blob_edit_content.vue @@ -1,5 +1,6 @@ diff --git a/app/assets/javascripts/ci_variable_list/components/ci_key_field.vue b/app/assets/javascripts/ci_variable_list/components/ci_key_field.vue new file mode 100644 index 00000000000..f5c2cc57f3f --- /dev/null +++ b/app/assets/javascripts/ci_variable_list/components/ci_key_field.vue @@ -0,0 +1,169 @@ + + diff --git a/app/assets/javascripts/ci_variable_list/components/ci_variable_autocomplete_tokens.js b/app/assets/javascripts/ci_variable_list/components/ci_variable_autocomplete_tokens.js new file mode 100644 index 00000000000..9022bf51514 --- /dev/null +++ b/app/assets/javascripts/ci_variable_list/components/ci_variable_autocomplete_tokens.js @@ -0,0 +1,29 @@ +import { __ } from '~/locale'; + +import { AWS_ACCESS_KEY_ID, AWS_DEFAULT_REGION, AWS_SECRET_ACCESS_KEY } from '../constants'; + +export const awsTokens = { + [AWS_ACCESS_KEY_ID]: { + name: AWS_ACCESS_KEY_ID, + /* Checks for exactly twenty characters that match key. + Based on greps suggested by Amazon at: + https://aws.amazon.com/blogs/security/a-safer-way-to-distribute-aws-credentials-to-ec2/ + */ + validation: val => /^[A-Za-z0-9]{20}$/.test(val), + invalidMessage: __('This variable does not match the expected pattern.'), + }, + [AWS_DEFAULT_REGION]: { + name: AWS_DEFAULT_REGION, + }, + [AWS_SECRET_ACCESS_KEY]: { + name: AWS_SECRET_ACCESS_KEY, + /* Checks for exactly forty characters that match secret. + Based on greps suggested by Amazon at: + https://aws.amazon.com/blogs/security/a-safer-way-to-distribute-aws-credentials-to-ec2/ + */ + validation: val => /^[A-Za-z0-9/+=]{40}$/.test(val), + invalidMessage: __('This variable does not match the expected pattern.'), + }, +}; + +export const awsTokenList = Object.keys(awsTokens); diff --git a/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue b/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue index 316408adfb2..8f5acd4a0a0 100644 --- a/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue +++ b/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue @@ -1,8 +1,4 @@ diff --git a/app/assets/javascripts/jira_import/components/jira_import_form.vue b/app/assets/javascripts/jira_import/components/jira_import_form.vue index 4de04efe1b0..0146f564260 100644 --- a/app/assets/javascripts/jira_import/components/jira_import_form.vue +++ b/app/assets/javascripts/jira_import/components/jira_import_form.vue @@ -1,17 +1,50 @@ @@ -19,14 +52,21 @@ export default {

{{ __('New Jira import') }}


-
+ - +
diff --git a/app/assets/javascripts/jira_import/components/jira_import_progress.vue b/app/assets/javascripts/jira_import/components/jira_import_progress.vue new file mode 100644 index 00000000000..2d610224658 --- /dev/null +++ b/app/assets/javascripts/jira_import/components/jira_import_progress.vue @@ -0,0 +1,66 @@ + + + diff --git a/app/assets/javascripts/jira_import/components/jira_import_setup.vue b/app/assets/javascripts/jira_import/components/jira_import_setup.vue index 917930397f4..44773a773d5 100644 --- a/app/assets/javascripts/jira_import/components/jira_import_setup.vue +++ b/app/assets/javascripts/jira_import/components/jira_import_setup.vue @@ -1,6 +1,11 @@ diff --git a/app/assets/javascripts/jira_import/index.js b/app/assets/javascripts/jira_import/index.js index 13b16b81c49..8bd70e4e277 100644 --- a/app/assets/javascripts/jira_import/index.js +++ b/app/assets/javascripts/jira_import/index.js @@ -24,7 +24,10 @@ export default function mountJiraImportApp() { render(createComponent) { return createComponent(App, { props: { + inProgressIllustration: el.dataset.inProgressIllustration, isJiraConfigured: parseBoolean(el.dataset.isJiraConfigured), + issuesPath: el.dataset.issuesPath, + jiraProjects: el.dataset.jiraProjects ? JSON.parse(el.dataset.jiraProjects) : [], projectPath: el.dataset.projectPath, setupIllustration: el.dataset.setupIllustration, }, diff --git a/app/assets/javascripts/jira_import/queries/getJiraProjects.query.graphql b/app/assets/javascripts/jira_import/queries/getJiraProjects.query.graphql deleted file mode 100644 index 13100eac221..00000000000 --- a/app/assets/javascripts/jira_import/queries/getJiraProjects.query.graphql +++ /dev/null @@ -1,14 +0,0 @@ -query getJiraProjects($fullPath: ID!) { - project(fullPath: $fullPath) { - jiraImportStatus - jiraImports { - nodes { - jiraProjectKey - scheduledAt - scheduledBy { - username - } - } - } - } -} diff --git a/app/assets/javascripts/jira_import/queries/get_jira_import_details.query.graphql b/app/assets/javascripts/jira_import/queries/get_jira_import_details.query.graphql new file mode 100644 index 00000000000..0eaaad580fc --- /dev/null +++ b/app/assets/javascripts/jira_import/queries/get_jira_import_details.query.graphql @@ -0,0 +1,12 @@ +#import "./jira_import.fragment.graphql" + +query($fullPath: ID!) { + project(fullPath: $fullPath) { + jiraImportStatus + jiraImports(last: 1) { + nodes { + ...JiraImport + } + } + } +} diff --git a/app/assets/javascripts/jira_import/queries/initiate_jira_import.mutation.graphql b/app/assets/javascripts/jira_import/queries/initiate_jira_import.mutation.graphql new file mode 100644 index 00000000000..8fda8287988 --- /dev/null +++ b/app/assets/javascripts/jira_import/queries/initiate_jira_import.mutation.graphql @@ -0,0 +1,11 @@ +#import "./jira_import.fragment.graphql" + +mutation($input: JiraImportStartInput!) { + jiraImportStart(input: $input) { + clientMutationId + jiraImport { + ...JiraImport + } + errors + } +} diff --git a/app/assets/javascripts/jira_import/queries/jira_import.fragment.graphql b/app/assets/javascripts/jira_import/queries/jira_import.fragment.graphql new file mode 100644 index 00000000000..fde2ebeff91 --- /dev/null +++ b/app/assets/javascripts/jira_import/queries/jira_import.fragment.graphql @@ -0,0 +1,7 @@ +fragment JiraImport on JiraImport { + jiraProjectKey + scheduledAt + scheduledBy { + name + } +} diff --git a/app/assets/javascripts/jira_import/utils.js b/app/assets/javascripts/jira_import/utils.js new file mode 100644 index 00000000000..504cf19e44e --- /dev/null +++ b/app/assets/javascripts/jira_import/utils.js @@ -0,0 +1,10 @@ +export const IMPORT_STATE = { + FAILED: 'failed', + FINISHED: 'finished', + NONE: 'none', + SCHEDULED: 'scheduled', + STARTED: 'started', +}; + +export const isInProgress = state => + state === IMPORT_STATE.SCHEDULED || state === IMPORT_STATE.STARTED; diff --git a/app/assets/javascripts/lazy_loader.js b/app/assets/javascripts/lazy_loader.js index 9e8edd05b88..a464290ffb5 100644 --- a/app/assets/javascripts/lazy_loader.js +++ b/app/assets/javascripts/lazy_loader.js @@ -1,4 +1,4 @@ -import _ from 'underscore'; +import { debounce, throttle } from 'lodash'; export const placeholderImage = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='; @@ -82,7 +82,7 @@ export default class LazyLoader { } startIntersectionObserver = () => { - this.throttledElementsInView = _.throttle(() => this.checkElementsInView(), 300); + this.throttledElementsInView = throttle(() => this.checkElementsInView(), 300); this.intersectionObserver = new IntersectionObserver(this.onIntersection, { rootMargin: `${SCROLL_THRESHOLD}px 0px`, thresholds: 0.1, @@ -102,8 +102,8 @@ export default class LazyLoader { }; startLegacyObserver() { - this.throttledScrollCheck = _.throttle(() => this.scrollCheck(), 300); - this.debouncedElementsInView = _.debounce(() => this.checkElementsInView(), 300); + this.throttledScrollCheck = throttle(() => this.scrollCheck(), 300); + this.debouncedElementsInView = debounce(() => this.checkElementsInView(), 300); window.addEventListener('scroll', this.throttledScrollCheck); window.addEventListener('resize', this.debouncedElementsInView); } diff --git a/app/assets/javascripts/lib/utils/keycodes.js b/app/assets/javascripts/lib/utils/keycodes.js index 2270d329c24..16bffc5c2cf 100644 --- a/app/assets/javascripts/lib/utils/keycodes.js +++ b/app/assets/javascripts/lib/utils/keycodes.js @@ -1,5 +1,6 @@ -export const UP_KEY_CODE = 38; -export const DOWN_KEY_CODE = 40; +export const BACKSPACE_KEY_CODE = 8; export const ENTER_KEY_CODE = 13; export const ESC_KEY_CODE = 27; -export const BACKSPACE_KEY_CODE = 8; +export const UP_KEY_CODE = 38; +export const DOWN_KEY_CODE = 40; +export const DELETE_KEY_CODE = 46; diff --git a/app/assets/javascripts/lib/utils/unit_format/index.js b/app/assets/javascripts/lib/utils/unit_format/index.js index d3aea37e677..adf374db66c 100644 --- a/app/assets/javascripts/lib/utils/unit_format/index.js +++ b/app/assets/javascripts/lib/utils/unit_format/index.js @@ -1,3 +1,4 @@ +import { engineeringNotation } from '@gitlab/ui/src/utils/number_utils'; import { s__ } from '~/locale'; import { @@ -39,15 +40,18 @@ export const SUPPORTED_FORMATS = { gibibytes: 'gibibytes', tebibytes: 'tebibytes', pebibytes: 'pebibytes', + + // Engineering Notation + engineering: 'engineering', }; /** * Returns a function that formats number to different units - * @param {String} format - Format to use, must be one of the SUPPORTED_FORMATS. Defaults to number. + * @param {String} format - Format to use, must be one of the SUPPORTED_FORMATS. Defaults to engineering notation. * * */ -export const getFormatter = (format = SUPPORTED_FORMATS.number) => { +export const getFormatter = (format = SUPPORTED_FORMATS.engineering) => { // Number if (format === SUPPORTED_FORMATS.number) { @@ -252,6 +256,17 @@ export const getFormatter = (format = SUPPORTED_FORMATS.number) => { return scaledBinaryFormatter('B', 5); } + if (format === SUPPORTED_FORMATS.engineering) { + /** + * Formats via engineering notation + * + * @function + * @param {Number} value - Value to format + * @param {Number} fractionDigits - precision decimals - Defaults to 2 + */ + return engineeringNotation; + } + // Fail so client library addresses issue throw TypeError(`${format} is not a valid number format`); }; diff --git a/app/assets/javascripts/logs/constants.js b/app/assets/javascripts/logs/constants.js index 450b83f4827..51770aa7a1c 100644 --- a/app/assets/javascripts/logs/constants.js +++ b/app/assets/javascripts/logs/constants.js @@ -1,3 +1,3 @@ -export const dateFormatMask = 'UTC:mmm dd HH:MM:ss.l"Z"'; +export const dateFormatMask = 'mmm dd HH:MM:ss.l'; export const TOKEN_TYPE_POD_NAME = 'TOKEN_TYPE_POD_NAME'; diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index 81b2e9f13a5..6c8f6372795 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -298,6 +298,18 @@ document.addEventListener('DOMContentLoaded', () => { if ($gutterIcon.hasClass('fa-angle-double-right')) { $sidebarGutterToggle.trigger('click'); } + + const sidebarGutterVueToggleEl = document.querySelector('.js-sidebar-vue-toggle'); + + // Sidebar has an icon which corresponds to collapsing the sidebar + // only then trigger the click. + if (sidebarGutterVueToggleEl) { + const collapseIcon = sidebarGutterVueToggleEl.querySelector('i.fa-angle-double-right'); + + if (collapseIcon) { + collapseIcon.click(); + } + } } }); diff --git a/app/assets/javascripts/monitoring/components/charts/annotations.js b/app/assets/javascripts/monitoring/components/charts/annotations.js index 947750b3721..418107c4126 100644 --- a/app/assets/javascripts/monitoring/components/charts/annotations.js +++ b/app/assets/javascripts/monitoring/components/charts/annotations.js @@ -1,20 +1,20 @@ -import { graphTypes, symbolSizes, colorValues } from '../../constants'; +import { graphTypes, symbolSizes, colorValues, annotationsSymbolIcon } from '../../constants'; /** * Annotations and deployments are decoration layers on * top of the actual chart data. We use a scatter plot to * display this information. Each chart has its coordinate - * system based on data and irresptive of the data, these + * system based on data and irrespective of the data, these * decorations have to be placed in specific locations. * For this reason, annotations have their own coordinate system, * * As of %12.9, only deployment icons, a type of annotations, need * to be displayed on the chart. * - * After https://gitlab.com/gitlab-org/gitlab/-/issues/211418, - * annotations and deployments will co-exist in the same - * series as they logically belong together. Annotations will be - * passed as markLine objects. + * Annotations and deployments co-exist in the same series as + * they logically belong together. Annotations are passed as + * markLines and markPoints while deployments are passed as + * data points with custom icons. */ /** @@ -45,42 +45,49 @@ export const annotationsYAxis = { * Fetched list of annotations are parsed into a * format the eCharts accepts to draw markLines * - * If Annotation is a single line, the `starting_at` property - * has a value and the `ending_at` is null. Because annotations - * only supports lines the `ending_at` value does not exist yet. - * + * If Annotation is a single line, the `startingAt` property + * has a value and the `endingAt` is null. Because annotations + * only supports lines the `endingAt` value does not exist yet. * * @param {Object} annotation object * @returns {Object} markLine object */ -export const parseAnnotations = ({ starting_at = '', color = colorValues.primaryColor }) => ({ - xAxis: starting_at, - lineStyle: { - color, - }, -}); +export const parseAnnotations = annotations => + annotations.reduce( + (acc, annotation) => { + acc.lines.push({ + xAxis: annotation.startingAt, + lineStyle: { + color: colorValues.primaryColor, + }, + }); + + acc.points.push({ + name: 'annotations', + xAxis: annotation.startingAt, + yAxis: annotationsYAxisCoords.min, + tooltipData: { + title: annotation.startingAt, + content: annotation.description, + }, + }); + + return acc; + }, + { lines: [], points: [] }, + ); /** - * This method currently generates deployments and annotations - * but are not used in the chart. The method calling - * generateAnnotationsSeries will not pass annotations until - * https://gitlab.com/gitlab-org/gitlab/-/issues/211330 is - * implemented. - * - * This method is extracted out of the charts so that - * annotation lines can be easily supported in - * the future. - * - * In order to make hover work, hidden annotation data points - * are created along with the markLines. These data points have - * the necessart metadata that is used to display in the tooltip. + * This method generates a decorative series that has + * deployments as data points with custom icons and + * annotations as markLines and markPoints * * @param {Array} deployments deployments data * @returns {Object} annotation series object */ export const generateAnnotationsSeries = ({ deployments = [], annotations = [] } = {}) => { // deployment data points - const deploymentsData = deployments.map(deployment => { + const data = deployments.map(deployment => { return { name: 'deployments', value: [deployment.createdAt, annotationsYAxisCoords.pos], @@ -98,31 +105,29 @@ export const generateAnnotationsSeries = ({ deployments = [], annotations = [] } }; }); - // annotation data points - const annotationsData = annotations.map(annotation => { - return { - name: 'annotations', - value: [annotation.starting_at, annotationsYAxisCoords.pos], - // style options - symbol: 'none', - // metadata that are accessible in `formatTooltipText` method - tooltipData: { - description: annotation.description, - }, - }; - }); + const parsedAnnotations = parseAnnotations(annotations); - // annotation markLine option + // markLine option draws the annotations dotted line const markLine = { symbol: 'none', silent: true, - data: annotations.map(parseAnnotations), + data: parsedAnnotations.lines, + }; + + // markPoints are the arrows under the annotations lines + const markPoint = { + symbol: annotationsSymbolIcon, + symbolSize: '8', + symbolOffset: [0, ' 60%'], + data: parsedAnnotations.points, }; return { + name: 'annotations', type: graphTypes.annotationsData, yAxisIndex: 1, // annotationsYAxis index - data: [...deploymentsData, ...annotationsData], + data, markLine, + markPoint, }; }; diff --git a/app/assets/javascripts/monitoring/components/charts/empty_chart.vue b/app/assets/javascripts/monitoring/components/charts/empty_chart.vue index 5588d9ac060..e015ef32d8c 100644 --- a/app/assets/javascripts/monitoring/components/charts/empty_chart.vue +++ b/app/assets/javascripts/monitoring/components/charts/empty_chart.vue @@ -3,12 +3,6 @@ import chartEmptyStateIllustration from '@gitlab/svgs/dist/illustrations/chart-e import { chartHeight } from '../../constants'; export default { - props: { - graphTitle: { - type: String, - required: true, - }, - }, data() { return { height: chartHeight, diff --git a/app/assets/javascripts/monitoring/components/charts/options.js b/app/assets/javascripts/monitoring/components/charts/options.js index d9f49bd81f5..09b03774580 100644 --- a/app/assets/javascripts/monitoring/components/charts/options.js +++ b/app/assets/javascripts/monitoring/components/charts/options.js @@ -6,9 +6,8 @@ const yAxisBoundaryGap = [0.1, 0.1]; * Max string length of formatted axis tick */ const maxDataAxisTickLength = 8; - // Defaults -const defaultFormat = SUPPORTED_FORMATS.number; +const defaultFormat = SUPPORTED_FORMATS.engineering; const defaultYAxisFormat = defaultFormat; const defaultYAxisPrecision = 2; @@ -26,8 +25,7 @@ const chartGridLeft = 75; * @param {Object} param - Dashboard .yml definition options */ const getDataAxisOptions = ({ format, precision, name }) => { - const formatter = getFormatter(format); - + const formatter = getFormatter(format); // default to engineeringNotation, same as gitlab-ui return { name, nameLocation: 'center', // same as gitlab-ui's default diff --git a/app/assets/javascripts/monitoring/components/charts/time_series.vue b/app/assets/javascripts/monitoring/components/charts/time_series.vue index 9041b01088c..bf40e8f448e 100644 --- a/app/assets/javascripts/monitoring/components/charts/time_series.vue +++ b/app/assets/javascripts/monitoring/components/charts/time_series.vue @@ -6,7 +6,7 @@ import dateFormat from 'dateformat'; import { s__, __ } from '~/locale'; import { getSvgIconPathContent } from '~/lib/utils/icon_utils'; import Icon from '~/vue_shared/components/icon.vue'; -import { chartHeight, lineTypes, lineWidths, dateFormats, tooltipTypes } from '../../constants'; +import { chartHeight, lineTypes, lineWidths, dateFormats } from '../../constants'; import { getYAxisOptions, getChartGrid, getTooltipFormatter } from './options'; import { annotationsYAxis, generateAnnotationsSeries } from './annotations'; import { makeDataSeries } from '~/helpers/monitor_helper'; @@ -20,7 +20,6 @@ const events = { }; export default { - tooltipTypes, components: { GlAreaChart, GlLineChart, @@ -262,6 +261,21 @@ export default { isTooltipOfType(tooltipType, defaultType) { return tooltipType === defaultType; }, + /** + * This method is triggered when hovered over a single markPoint. + * + * The annotations title timestamp should match the data tooltip + * title. + * + * @params {Object} params markPoint object + * @returns {Object} + */ + formatAnnotationsTooltipText(params) { + return { + title: dateFormat(params.data?.tooltipData?.title, dateFormats.default), + content: params.data?.tooltipData?.content, + }; + }, formatTooltipText(params) { this.tooltip.title = dateFormat(params.value, dateFormats.default); this.tooltip.content = []; @@ -270,15 +284,10 @@ export default { if (dataPoint.value) { const [, yVal] = dataPoint.value; this.tooltip.type = dataPoint.name; - if (this.isTooltipOfType(this.tooltip.type, this.$options.tooltipTypes.deployments)) { + if (this.tooltip.type === 'deployments') { const { data = {} } = dataPoint; this.tooltip.sha = data?.tooltipData?.sha; this.tooltip.commitUrl = data?.tooltipData?.commitUrl; - } else if ( - this.isTooltipOfType(this.tooltip.type, this.$options.tooltipTypes.annotations) - ) { - const { data } = dataPoint; - this.tooltip.content.push(data?.tooltipData?.description); } else { const { seriesName, color, dataIndex } = dataPoint; @@ -356,6 +365,7 @@ export default { :data="chartData" :option="chartOptions" :format-tooltip-text="formatTooltipText" + :format-annotations-tooltip-text="formatAnnotationsTooltipText" :thresholds="thresholds" :width="width" :height="height" @@ -364,7 +374,7 @@ export default { @created="onChartCreated" @updated="onChartUpdated" > -