diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7e4f1a02646..8ac9396fb1f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -53,6 +53,8 @@ workflow: variables: RAILS_ENV: "test" NODE_ENV: "test" + # we override the max_old_space_size to prevent OOM errors + NODE_OPTIONS: --max_old_space_size=3584 SIMPLECOV: "true" GIT_DEPTH: "20" GIT_SUBMODULE_STRATEGY: "none" diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml index e739b046ff7..8c6556cdc11 100644 --- a/.gitlab/ci/frontend.gitlab-ci.yml +++ b/.gitlab/ci/frontend.gitlab-ci.yml @@ -1,22 +1,15 @@ -.frontend-base: - extends: - - .default-retry - - .default-before_script - variables: - SETUP_DB: "false" - # we override the max_old_space_size to prevent OOM errors - NODE_OPTIONS: --max_old_space_size=3584 - .yarn-install: &yarn-install - source scripts/utils.sh - run_timed_command "retry yarn install --frozen-lockfile" .compile-assets-base: extends: - - .frontend-base + - .default-retry + - .default-before_script - .assets-compile-cache image: registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.7.2-git-2.29-lfs-2.9-node-14.15-yarn-1.22-graphicsmagick-1.3.34 variables: + SETUP_DB: "false" WEBPACK_VENDOR_DLL: "true" stage: prepare script: @@ -93,13 +86,13 @@ update-yarn-cache: .frontend-fixtures-base: extends: - - .frontend-base + - .default-retry + - .default-before_script - .rails-cache - .use-pg11 stage: fixtures needs: ["setup-test-env", "retrieve-tests-metadata", "compile-test-assets"] variables: - SETUP_DB: "true" WEBPACK_VENDOR_DLL: "true" script: - run_timed_command "gem install knapsack --no-document" @@ -151,10 +144,8 @@ graphql-schema-dump: .frontend-test-base: extends: - - .frontend-base + - .default-retry - .yarn-cache - variables: - USE_BUNDLE_INSTALL: "false" stage: test eslint-as-if-foss: diff --git a/.gitlab/ci/memory.gitlab-ci.yml b/.gitlab/ci/memory.gitlab-ci.yml index ef6c9b9c8ff..3e5639e4d69 100644 --- a/.gitlab/ci/memory.gitlab-ci.yml +++ b/.gitlab/ci/memory.gitlab-ci.yml @@ -44,8 +44,6 @@ memory-on-boot: NODE_ENV: "production" RAILS_ENV: "production" SETUP_DB: "true" - # we override the max_old_space_size to prevent OOM errors - NODE_OPTIONS: --max_old_space_size=3584 script: - PATH_TO_HIT="/users/sign_in" CUT_OFF=0.3 bundle exec derailed exec perf:mem >> 'tmp/memory_on_boot.txt' - scripts/generate-memory-metrics-on-boot tmp/memory_on_boot.txt >> 'tmp/memory_on_boot_metrics.txt' diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js index 145b419f8f0..955f25a6f26 100644 --- a/app/assets/javascripts/lib/utils/datetime_utility.js +++ b/app/assets/javascripts/lib/utils/datetime_utility.js @@ -4,8 +4,6 @@ import { isString, mapValues, isNumber, reduce } from 'lodash'; import * as timeago from 'timeago.js'; import { languageCode, s__, __, n__ } from '../../locale'; -const MILLISECONDS_IN_HOUR = 60 * 60 * 1000; -const MILLISECONDS_IN_DAY = 24 * MILLISECONDS_IN_HOUR; const DAYS_IN_WEEK = 7; window.timeago = timeago; @@ -946,49 +944,6 @@ export const format24HourTimeStringFromInt = (time) => { return formatted24HourString; }; -/** - * A utility function which checks if two date ranges overlap. - * - * @param {Object} givenPeriodLeft - the first period to compare. - * @param {Object} givenPeriodRight - the second period to compare. - * @returns {Object} { daysOverlap: number of days the overlap is present, hoursOverlap: number of hours the overlap is present, overlapStartDate: the start date of the overlap in time format, overlapEndDate: the end date of the overlap in time format } - * @throws {Error} Uncaught Error: Invalid period - * - * @example - * getOverlapDateInPeriods( - * { start: new Date(2021, 0, 11), end: new Date(2021, 0, 13) }, - * { start: new Date(2021, 0, 11), end: new Date(2021, 0, 14) } - * ) => { daysOverlap: 2, hoursOverlap: 48, overlapStartDate: 1610323200000, overlapEndDate: 1610496000000 } - * - */ -export const getOverlapDateInPeriods = (givenPeriodLeft = {}, givenPeriodRight = {}) => { - const leftStartTime = new Date(givenPeriodLeft.start).getTime(); - const leftEndTime = new Date(givenPeriodLeft.end).getTime(); - const rightStartTime = new Date(givenPeriodRight.start).getTime(); - const rightEndTime = new Date(givenPeriodRight.end).getTime(); - - if (!(leftStartTime <= leftEndTime && rightStartTime <= rightEndTime)) { - throw new Error(__('Invalid period')); - } - - const isOverlapping = leftStartTime < rightEndTime && rightStartTime < leftEndTime; - - if (!isOverlapping) { - return { daysOverlap: 0 }; - } - - const overlapStartDate = Math.max(leftStartTime, rightStartTime); - const overlapEndDate = rightEndTime > leftEndTime ? leftEndTime : rightEndTime; - const differenceInMs = overlapEndDate - overlapStartDate; - - return { - hoursOverlap: Math.ceil(differenceInMs / MILLISECONDS_IN_HOUR), - daysOverlap: Math.ceil(differenceInMs / MILLISECONDS_IN_DAY), - overlapStartDate, - overlapEndDate, - }; -}; - /** * A utility function that checks that the date is today * diff --git a/app/assets/javascripts/pages/projects/blob/show/index.js b/app/assets/javascripts/pages/projects/blob/show/index.js index 10bac6d60c2..fc2702b8c37 100644 --- a/app/assets/javascripts/pages/projects/blob/show/index.js +++ b/app/assets/javascripts/pages/projects/blob/show/index.js @@ -5,10 +5,29 @@ import GpgBadges from '~/gpg_badges'; import initBlob from '~/pages/projects/init_blob'; import initWebIdeLink from '~/pages/projects/shared/web_ide_link'; import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue'; +import BlobContentViewer from '~/repository/components/blob_content_viewer.vue'; import '~/sourcegraph/load'; -new BlobViewer(); // eslint-disable-line no-new -initBlob(); +const viewBlobEl = document.querySelector('#js-view-blob-app'); + +if (viewBlobEl) { + const { blobPath } = viewBlobEl.dataset; + + // eslint-disable-next-line no-new + new Vue({ + el: viewBlobEl, + render(createElement) { + return createElement(BlobContentViewer, { + props: { + path: blobPath, + }, + }); + }, + }); +} else { + new BlobViewer(); // eslint-disable-line no-new + initBlob(); +} const CommitPipelineStatusEl = document.querySelector('.js-commit-pipeline-status'); const statusLink = document.querySelector('.commit-actions .ci-status-link'); diff --git a/app/assets/javascripts/performance_bar/components/detailed_metric.vue b/app/assets/javascripts/performance_bar/components/detailed_metric.vue index 57569340aa5..e5b26a00c4c 100644 --- a/app/assets/javascripts/performance_bar/components/detailed_metric.vue +++ b/app/assets/javascripts/performance_bar/components/detailed_metric.vue @@ -54,11 +54,17 @@ export default { return this.currentRequest.details[this.metric]; }, metricDetailsSummary() { - return { - [s__('Total')]: this.metricDetails.calls, - [s__('PerformanceBar|Total duration')]: this.metricDetails.duration, - ...(this.metricDetails.summary || {}), - }; + const summary = {}; + + if (!this.metricDetails.summaryOptions?.hideTotal) { + summary[s__('Total')] = this.metricDetails.calls; + } + + if (!this.metricDetails.summaryOptions?.hideDuration) { + summary[s__('PerformanceBar|Total duration')] = this.metricDetails.duration; + } + + return { ...summary, ...(this.metricDetails.summary || {}) }; }, metricDetailsLabel() { if (this.metricDetails.duration && this.metricDetails.calls) { @@ -133,7 +139,7 @@ export default { > {{ metricDetailsLabel }} @@ -208,7 +214,7 @@ export default {
- {{ title }} + {{ title }} diff --git a/app/assets/javascripts/performance_bar/components/performance_bar_app.vue b/app/assets/javascripts/performance_bar/components/performance_bar_app.vue index 4f79d99a49b..ebe9c4eee2f 100644 --- a/app/assets/javascripts/performance_bar/components/performance_bar_app.vue +++ b/app/assets/javascripts/performance_bar/components/performance_bar_app.vue @@ -136,7 +136,7 @@ export default {
@@ -157,16 +157,16 @@ export default { id="peek-view-trace" class="view" > - {{ + {{ s__('PerformanceBar|Trace') }}
- {{ + {{ s__('PerformanceBar|Stats') }} { const resourceEntries = performance.getEntriesByType('resource'); let durationString = ''; + let summary = {}; if (navigationEntries.length > 0) { - durationString = `${Math.round(navigationEntries[0].responseEnd)} | `; - durationString += `${Math.round(paintEntries[1].startTime)} | `; - durationString += ` ${Math.round(navigationEntries[0].domContentLoadedEventEnd)}`; + const backend = Math.round(navigationEntries[0].responseEnd); + const firstContentfulPaint = Math.round(paintEntries[1].startTime); + const domContentLoaded = Math.round(navigationEntries[0].domContentLoadedEventEnd); + + summary = { + [s__('PerformanceBar|Backend')]: backend, + [s__('PerformanceBar|First Contentful Paint')]: firstContentfulPaint, + [s__('PerformanceBar|DOM Content Loaded')]: domContentLoaded, + }; + + durationString = `${backend} | ${firstContentfulPaint} | ${domContentLoaded}`; } let newEntries = resourceEntries.map(this.transformResourceEntry); - this.updateFrontendPerformanceMetrics(durationString, newEntries); + this.updateFrontendPerformanceMetrics(durationString, summary, newEntries); if ('PerformanceObserver' in window) { // We start observing for more incoming timings const observer = new PerformanceObserver((list) => { newEntries = newEntries.concat(list.getEntries().map(this.transformResourceEntry)); - this.updateFrontendPerformanceMetrics(durationString, newEntries); + this.updateFrontendPerformanceMetrics(durationString, summary, newEntries); }); observer.observe({ entryTypes: ['resource'] }); } } }, - updateFrontendPerformanceMetrics(durationString, requestEntries) { + updateFrontendPerformanceMetrics(durationString, summary, requestEntries) { this.store.setRequestDetailsData(this.requestId, 'total', { duration: durationString, calls: requestEntries.length, details: requestEntries, + summaryOptions: { + hideDuration: true, + }, + summary, }); }, transformResourceEntry(entry) { - const nf = new Intl.NumberFormat(); return { + start: entry.startTime, name: entry.name.replace(document.location.origin, ''), duration: Math.round(entry.duration), - size: entry.transferSize ? `${nf.format(entry.transferSize)} bytes` : 'cached', + size: entry.transferSize ? numberToHumanSize(entry.transferSize) : 'cached', }; }, }, diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 1aa4177c902..7a7d85d2383 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -842,10 +842,10 @@ $linked-project-column-margin: 60px; /* Performance Bar */ -$perf-bar-production: #222; -$perf-bar-staging: #291430; -$perf-bar-development: #4c1210; -$perf-bar-bucket-bg: #111; +$perf-bar-production: $gray-950; +$perf-bar-staging: $indigo-950; +$perf-bar-development: $red-950; +$perf-bar-bucket-bg: $black; $perf-bar-bucket-box-shadow-from: rgba($white, 0.2); $perf-bar-bucket-box-shadow-to: rgba($black, 0.25); $perf-bar-canary-text: $orange-400; diff --git a/app/assets/stylesheets/performance_bar.scss b/app/assets/stylesheets/performance_bar.scss index c6c9f3b7365..bcc3c35e00e 100644 --- a/app/assets/stylesheets/performance_bar.scss +++ b/app/assets/stylesheets/performance_bar.scss @@ -14,7 +14,7 @@ color: $gray-300; select { - color: $gray-300; + color: $white; width: 200px; } diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml index a0f644717ad..84f2d352bc9 100644 --- a/app/views/projects/blob/_blob.html.haml +++ b/app/views/projects/blob/_blob.html.haml @@ -11,6 +11,11 @@ #blob-content-holder.blob-content-holder - if @code_navigation_path #js-code-navigation{ data: { code_navigation_path: @code_navigation_path, blob_path: blob.path, definition_path_prefix: project_blob_path(@project, @ref) } } - %article.file-holder - = render 'projects/blob/header', blob: blob - = render 'projects/blob/content', blob: blob + - if Feature.enabled?(:refactor_blob_viewer, @project, default_enabled: :yaml) + #js-view-blob-app{ data: { blob_path: blob.path } } + .gl-spinner-container + = loading_icon(size: 'md') + - else + %article.file-holder + = render 'projects/blob/header', blob: blob + = render 'projects/blob/content', blob: blob diff --git a/changelogs/unreleased/21121-fj-fix-n-1-in-projects-and-service-desk.yml b/changelogs/unreleased/21121-fj-fix-n-1-in-projects-and-service-desk.yml new file mode 100644 index 00000000000..a4e63f6420e --- /dev/null +++ b/changelogs/unreleased/21121-fj-fix-n-1-in-projects-and-service-desk.yml @@ -0,0 +1,5 @@ +--- +title: Fix N+1 in REST projects and service desk +merge_request: 58747 +author: +type: performance diff --git a/changelogs/unreleased/pb-bg-color.yml b/changelogs/unreleased/pb-bg-color.yml new file mode 100644 index 00000000000..207942d0736 --- /dev/null +++ b/changelogs/unreleased/pb-bg-color.yml @@ -0,0 +1,4 @@ +title: Update performance bar background color to use Pajamas compliant colour palette +merge_request: 52775 +author: Yogi (@yo) +type: changed diff --git a/changelogs/unreleased/qmnguyen0711-rollout-performance-bar-sort-order.yml b/changelogs/unreleased/qmnguyen0711-rollout-performance-bar-sort-order.yml new file mode 100644 index 00000000000..8a7a8547a8c --- /dev/null +++ b/changelogs/unreleased/qmnguyen0711-rollout-performance-bar-sort-order.yml @@ -0,0 +1,5 @@ +--- +title: Enable chronological sort order for other items in the performance bar +merge_request: 58572 +author: +type: changed diff --git a/lib/api/entities/project.rb b/lib/api/entities/project.rb index b159effdde7..690bc5d419d 100644 --- a/lib/api/entities/project.rb +++ b/lib/api/entities/project.rb @@ -132,6 +132,7 @@ module API .preload(:project_setting) .preload(:container_expiration_policy) .preload(:auto_devops) + .preload(:service_desk_setting) .preload(project_group_links: { group: :route }, fork_network: :root_project, fork_network_member: :forked_from_project, diff --git a/lib/gitlab/gitaly_client/call.rb b/lib/gitlab/gitaly_client/call.rb index 9d4d86997ad..4bb184bee2f 100644 --- a/lib/gitlab/gitaly_client/call.rb +++ b/lib/gitlab/gitaly_client/call.rb @@ -50,11 +50,11 @@ module Gitlab end def recording_request - start = Gitlab::Metrics::System.monotonic_time + @start = Gitlab::Metrics::System.monotonic_time yield ensure - @duration += Gitlab::Metrics::System.monotonic_time - start + @duration += Gitlab::Metrics::System.monotonic_time - @start end def store_timings @@ -64,8 +64,14 @@ module Gitlab request_hash = @request.is_a?(Google::Protobuf::MessageExts) ? @request.to_h : {} - GitalyClient.add_call_details(feature: "#{@service}##{@rpc}", duration: @duration, request: request_hash, rpc: @rpc, - backtrace: Gitlab::BacktraceCleaner.clean_backtrace(caller)) + GitalyClient.add_call_details( + start: @start, + feature: "#{@service}##{@rpc}", + duration: @duration, + request: request_hash, + rpc: @rpc, + backtrace: Gitlab::BacktraceCleaner.clean_backtrace(caller) + ) end end end diff --git a/lib/gitlab/metrics/subscribers/external_http.rb b/lib/gitlab/metrics/subscribers/external_http.rb index 94c5d965200..0df64f2897e 100644 --- a/lib/gitlab/metrics/subscribers/external_http.rb +++ b/lib/gitlab/metrics/subscribers/external_http.rb @@ -37,7 +37,7 @@ module Gitlab def request(event) payload = event.payload - add_to_detail_store(payload) + add_to_detail_store(event.time, payload) add_to_request_store(payload) expose_metrics(payload) end @@ -48,10 +48,11 @@ module Gitlab ::Gitlab::Metrics::Transaction.current end - def add_to_detail_store(payload) + def add_to_detail_store(start, payload) return unless Gitlab::PerformanceBar.enabled_for_request? self.class.detail_store << { + start: start, duration: payload[:duration], scheme: payload[:scheme], method: payload[:method], diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 1e5bc8fad7d..421d305ae3e 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -4194,6 +4194,9 @@ msgstr "" msgid "Ascending" msgstr "" +msgid "Ask again later" +msgstr "" + msgid "Ask your group maintainer to set up a group runner." msgstr "" @@ -17024,9 +17027,6 @@ msgstr "" msgid "Invalid login or password" msgstr "" -msgid "Invalid period" -msgstr "" - msgid "Invalid pin code" msgstr "" @@ -22634,9 +22634,15 @@ msgstr "" msgid "Performance optimization" msgstr "" +msgid "PerformanceBar|Backend" +msgstr "" + msgid "PerformanceBar|Bullet notifications" msgstr "" +msgid "PerformanceBar|DOM Content Loaded" +msgstr "" + msgid "PerformanceBar|Download" msgstr "" @@ -22646,6 +22652,9 @@ msgstr "" msgid "PerformanceBar|External Http calls" msgstr "" +msgid "PerformanceBar|First Contentful Paint" +msgstr "" + msgid "PerformanceBar|Frontend resources" msgstr "" @@ -27473,6 +27482,9 @@ msgstr "" msgid "SecurityReports|Although it's rare to have no vulnerabilities, it can happen. Check your settings to make sure you've set up your dashboard correctly." msgstr "" +msgid "SecurityReports|At GitLab, we're all about iteration and feedback. That's why we are reaching out to customers like you to help guide what we work on this year for Vulnerability Management. We have a lot of exciting ideas and ask that you assist us by taking a short survey %{boldStart}no longer than 10 minutes%{boldEnd} to evaluate a few of our potential features." +msgstr "" + msgid "SecurityReports|Change status" msgstr "" @@ -27632,6 +27644,9 @@ msgstr "" msgid "SecurityReports|Status" msgstr "" +msgid "SecurityReports|Take survey" +msgstr "" + msgid "SecurityReports|There was an error adding the comment." msgstr "" @@ -27677,6 +27692,9 @@ msgstr "" msgid "SecurityReports|Upgrade to manage vulnerabilities" msgstr "" +msgid "SecurityReports|Vulnerability Management feature survey" +msgstr "" + msgid "SecurityReports|Vulnerability Report" msgstr "" @@ -27692,6 +27710,9 @@ msgstr "" msgid "SecurityReports|You must sign in as an authorized user to see this report" msgstr "" +msgid "SecurityReports|Your feedback is important to us! We will ask again in a week." +msgstr "" + msgid "See GitLab's %{password_policy_guidelines}" msgstr "" diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_when_pipeline_succeeds_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_when_pipeline_succeeds_spec.rb index 7c71228c767..9cb689dacbe 100644 --- a/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_when_pipeline_succeeds_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_when_pipeline_succeeds_spec.rb @@ -46,7 +46,7 @@ module QA project&.remove_via_api! end - it 'merges when pipeline succeeds', :smoke, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1684' do + it 'merges when pipeline succeeds', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1684' do branch_name = "merge-request-test-#{SecureRandom.hex(8)}" # Create a branch that will be merged into the default branch diff --git a/qa/qa/specs/features/browser_ui/5_package/pypi_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/pypi_repository_spec.rb index 06ce74a1e85..fb29af43da6 100644 --- a/qa/qa/specs/features/browser_ui/5_package/pypi_repository_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/pypi_repository_spec.rb @@ -114,7 +114,7 @@ module QA end context 'Geo', :orchestrated, :geo do - it 'replicates a published pypi package to the Geo secondary site', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1120' do + it 'replicates a published pypi package to the Geo secondary site', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1120', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/325556', type: :investigating } do QA::Runtime::Logger.debug('Visiting the secondary Geo site') QA::Flow::Login.while_signed_in(address: :geo_secondary) do diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index 34601cab24f..4730679feb8 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -290,7 +290,6 @@ RSpec.describe 'Project' do let(:project) { create(:forked_project_with_submodules) } before do - stub_feature_flags(refactor_blob_viewer: false) project.add_maintainer(user) sign_in user visit project_path(project) diff --git a/spec/frontend/lib/utils/datetime_utility_spec.js b/spec/frontend/lib/utils/datetime_utility_spec.js index 2df0cb00f9a..2a226ca8ade 100644 --- a/spec/frontend/lib/utils/datetime_utility_spec.js +++ b/spec/frontend/lib/utils/datetime_utility_spec.js @@ -966,62 +966,6 @@ describe('format24HourTimeStringFromInt', () => { }); }); -describe('getOverlapDateInPeriods', () => { - const start = new Date(2021, 0, 11); - const end = new Date(2021, 0, 13); - - describe('when date periods overlap', () => { - const givenPeriodLeft = new Date(2021, 0, 11); - const givenPeriodRight = new Date(2021, 0, 14); - - it('returns an overlap object that contains the amount of days overlapping, the amount of hours overlapping, start date of overlap and end date of overlap', () => { - expect( - datetimeUtility.getOverlapDateInPeriods( - { start, end }, - { start: givenPeriodLeft, end: givenPeriodRight }, - ), - ).toEqual({ - daysOverlap: 2, - hoursOverlap: 48, - overlapStartDate: givenPeriodLeft.getTime(), - overlapEndDate: end.getTime(), - }); - }); - }); - - describe('when date periods do not overlap', () => { - const givenPeriodLeft = new Date(2021, 0, 9); - const givenPeriodRight = new Date(2021, 0, 10); - - it('returns an overlap object that contains a 0 value for days overlapping', () => { - expect( - datetimeUtility.getOverlapDateInPeriods( - { start, end }, - { start: givenPeriodLeft, end: givenPeriodRight }, - ), - ).toEqual({ daysOverlap: 0 }); - }); - }); - - describe('when date periods contain an invalid Date', () => { - const startInvalid = new Date(NaN); - const endInvalid = new Date(NaN); - const error = __('Invalid period'); - - it('throws an exception when the left period contains an invalid date', () => { - expect(() => - datetimeUtility.getOverlapDateInPeriods({ start, end }, { start: startInvalid, end }), - ).toThrow(error); - }); - - it('throws an exception when the right period contains an invalid date', () => { - expect(() => - datetimeUtility.getOverlapDateInPeriods({ start, end }, { start, end: endInvalid }), - ).toThrow(error); - }); - }); -}); - describe('isToday', () => { const today = new Date(); it.each` diff --git a/spec/frontend/performance_bar/components/detailed_metric_spec.js b/spec/frontend/performance_bar/components/detailed_metric_spec.js index a58712f2fec..c35bd772c86 100644 --- a/spec/frontend/performance_bar/components/detailed_metric_spec.js +++ b/spec/frontend/performance_bar/components/detailed_metric_spec.js @@ -120,6 +120,73 @@ describe('detailedMetric', () => { }); }); + describe('when the details have summaryOptions option', () => { + const gitalyDetails = { + duration: '123ms', + calls: 456, + details: requestDetails, + warnings: ['gitaly calls: 456 over 30'], + }; + + describe('when the details have summaryOptions > hideTotal option', () => { + beforeEach(() => { + createComponent({ + currentRequest: { + details: { + gitaly: { ...gitalyDetails, summaryOptions: { hideTotal: true } }, + }, + }, + }); + }); + + it('displays a summary section', () => { + expect(findAllSummaryItems()).toEqual(['Total duration 123ms']); + }); + }); + + describe('when the details have summaryOptions > hideDuration option', () => { + beforeEach(() => { + createComponent({ + currentRequest: { + details: { + gitaly: { ...gitalyDetails, summaryOptions: { hideDuration: true } }, + }, + }, + }); + }); + + it('displays a summary section', () => { + expect(findAllSummaryItems()).toEqual(['Total 456']); + }); + }); + + describe('when the details have both summary and summaryOptions field', () => { + beforeEach(() => { + createComponent({ + currentRequest: { + details: { + gitaly: { + ...gitalyDetails, + summary: { + 'In controllers': 100, + 'In middlewares': 20, + }, + summaryOptions: { + hideDuration: true, + hideTotal: true, + }, + }, + }, + }, + }); + }); + + it('displays a summary section', () => { + expect(findAllSummaryItems()).toEqual(['In controllers 100', 'In middlewares 20']); + }); + }); + }); + describe("when the details don't have a start field", () => { beforeEach(() => { createComponent({ diff --git a/spec/lib/gitlab/gitaly_client/call_spec.rb b/spec/lib/gitlab/gitaly_client/call_spec.rb index 5c33ac40460..099307fc4e1 100644 --- a/spec/lib/gitlab/gitaly_client/call_spec.rb +++ b/spec/lib/gitlab/gitaly_client/call_spec.rb @@ -24,11 +24,14 @@ RSpec.describe Gitlab::GitalyClient::Call do def expect_call_details_to_match(duration_higher_than: 0) expect(client.list_call_details.size).to eq(1) expect(client.list_call_details.first) - .to match a_hash_including(feature: "#{service}##{rpc}", - duration: a_value > duration_higher_than, - request: an_instance_of(Hash), - rpc: rpc, - backtrace: an_instance_of(Array)) + .to match a_hash_including( + start: a_value > 0, + feature: "#{service}##{rpc}", + duration: a_value > duration_higher_than, + request: an_instance_of(Hash), + rpc: rpc, + backtrace: an_instance_of(Array) + ) end context 'when the response is not an enumerator' do diff --git a/spec/lib/gitlab/metrics/subscribers/external_http_spec.rb b/spec/lib/gitlab/metrics/subscribers/external_http_spec.rb index 5bcaf8fbc47..adbc05cb711 100644 --- a/spec/lib/gitlab/metrics/subscribers/external_http_spec.rb +++ b/spec/lib/gitlab/metrics/subscribers/external_http_spec.rb @@ -6,29 +6,45 @@ RSpec.describe Gitlab::Metrics::Subscribers::ExternalHttp, :request_store do let(:transaction) { Gitlab::Metrics::Transaction.new } let(:subscriber) { described_class.new } + around do |example| + freeze_time { example.run } + end + let(:event_1) do - double(:event, payload: { - method: 'POST', code: "200", duration: 0.321, - scheme: 'https', host: 'gitlab.com', port: 80, path: '/api/v4/projects', - query: 'current=true' - }) + double( + :event, + payload: { + method: 'POST', code: "200", duration: 0.321, + scheme: 'https', host: 'gitlab.com', port: 80, path: '/api/v4/projects', + query: 'current=true' + }, + time: Time.current + ) end let(:event_2) do - double(:event, payload: { - method: 'GET', code: "301", duration: 0.12, - scheme: 'http', host: 'gitlab.com', port: 80, path: '/api/v4/projects/2', - query: 'current=true' - }) + double( + :event, + payload: { + method: 'GET', code: "301", duration: 0.12, + scheme: 'http', host: 'gitlab.com', port: 80, path: '/api/v4/projects/2', + query: 'current=true' + }, + time: Time.current + ) end let(:event_3) do - double(:event, payload: { - method: 'POST', duration: 5.3, - scheme: 'http', host: 'gitlab.com', port: 80, path: '/api/v4/projects/2/issues', - query: 'current=true', - exception_object: Net::ReadTimeout.new - }) + double( + :event, + payload: { + method: 'POST', duration: 5.3, + scheme: 'http', host: 'gitlab.com', port: 80, path: '/api/v4/projects/2/issues', + query: 'current=true', + exception_object: Net::ReadTimeout.new + }, + time: Time.current + ) end describe '.detail_store' do @@ -134,19 +150,22 @@ RSpec.describe Gitlab::Metrics::Subscribers::ExternalHttp, :request_store do subscriber.request(event_3) expect(Gitlab::SafeRequestStore[:external_http_detail_store].length).to eq(3) - expect(Gitlab::SafeRequestStore[:external_http_detail_store][0]).to include( + expect(Gitlab::SafeRequestStore[:external_http_detail_store][0]).to match a_hash_including( + start: be_like_time(Time.current), method: 'POST', code: "200", duration: 0.321, scheme: 'https', host: 'gitlab.com', port: 80, path: '/api/v4/projects', query: 'current=true', exception_object: nil, backtrace: be_a(Array) ) - expect(Gitlab::SafeRequestStore[:external_http_detail_store][1]).to include( + expect(Gitlab::SafeRequestStore[:external_http_detail_store][1]).to match a_hash_including( + start: be_like_time(Time.current), method: 'GET', code: "301", duration: 0.12, scheme: 'http', host: 'gitlab.com', port: 80, path: '/api/v4/projects/2', query: 'current=true', exception_object: nil, backtrace: be_a(Array) ) - expect(Gitlab::SafeRequestStore[:external_http_detail_store][2]).to include( + expect(Gitlab::SafeRequestStore[:external_http_detail_store][2]).to match a_hash_including( + start: be_like_time(Time.current), method: 'POST', duration: 5.3, scheme: 'http', host: 'gitlab.com', port: 80, path: '/api/v4/projects/2/issues', query: 'current=true', diff --git a/spec/lib/peek/views/external_http_spec.rb b/spec/lib/peek/views/external_http_spec.rb index 98c4f771f33..18ae1326493 100644 --- a/spec/lib/peek/views/external_http_spec.rb +++ b/spec/lib/peek/views/external_http_spec.rb @@ -11,6 +11,10 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do allow(Gitlab::PerformanceBar).to receive(:enabled_for_request?).and_return(true) end + around do |example| + freeze_time { example.run } + end + let(:event_1) do { method: 'POST', code: "200", duration: 0.03, @@ -44,9 +48,9 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do end it 'returns aggregated results' do - subscriber.request(double(:event, payload: event_1)) - subscriber.request(double(:event, payload: event_2)) - subscriber.request(double(:event, payload: event_3)) + subscriber.request(double(:event, payload: event_1, time: Time.current)) + subscriber.request(double(:event, payload: event_2, time: Time.current)) + subscriber.request(double(:event, payload: event_3, time: Time.current)) results = subject.results expect(results[:calls]).to eq(3) @@ -55,6 +59,7 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do expected = [ { + start: be_like_time(Time.current), duration: 30.0, label: "POST https://gitlab.com:80/api/v4/projects?current=true", code: "Response status: 200", @@ -63,6 +68,7 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do warnings: [] }, { + start: be_like_time(Time.current), duration: 1300, label: "POST http://gitlab.com:80/api/v4/projects/2/issues?current=true", code: nil, @@ -71,6 +77,7 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do warnings: ["1300.0 over 100"] }, { + start: be_like_time(Time.current), duration: 5.0, label: "GET http://gitlab.com:80/api/v4/projects/2?current=true", code: "Response status: 301", @@ -81,7 +88,7 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do ] expect( - results[:details].map { |data| data.slice(:duration, :label, :code, :proxy, :error, :warnings) } + results[:details].map { |data| data.slice(:start, :duration, :label, :code, :proxy, :error, :warnings) } ).to match_array(expected) end @@ -91,10 +98,11 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do end it 'displays IPv4 in the label' do - subscriber.request(double(:event, payload: event_1)) + subscriber.request(double(:event, payload: event_1, time: Time.current)) expect(subject.results[:details]).to contain_exactly( a_hash_including( + start: be_like_time(Time.current), duration: 30.0, label: "POST https://1.2.3.4:80/api/v4/projects?current=true", code: "Response status: 200", @@ -112,10 +120,11 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do end it 'displays IPv6 in the label' do - subscriber.request(double(:event, payload: event_1)) + subscriber.request(double(:event, payload: event_1, time: Time.current)) expect(subject.results[:details]).to contain_exactly( a_hash_including( + start: be_like_time(Time.current), duration: 30.0, label: "POST https://[2606:4700:90:0:f22e:fbec:5bed:a9b9]:80/api/v4/projects?current=true", code: "Response status: 200", @@ -133,10 +142,11 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do end it 'converts query hash into a query string' do - subscriber.request(double(:event, payload: event_1)) + subscriber.request(double(:event, payload: event_1, time: Time.current)) expect(subject.results[:details]).to contain_exactly( a_hash_including( + start: be_like_time(Time.current), duration: 30.0, label: "POST https://gitlab.com:80/api/v4/projects?current=true&item1=string&item2%5B%5D=1&item2%5B%5D=2", code: "Response status: 200", @@ -154,10 +164,11 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do end it 'displays unknown in the label' do - subscriber.request(double(:event, payload: event_1)) + subscriber.request(double(:event, payload: event_1, time: Time.current)) expect(subject.results[:details]).to contain_exactly( a_hash_including( + start: be_like_time(Time.current), duration: 30.0, label: "POST unknown", code: "Response status: 200", @@ -176,10 +187,11 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do end it 'displays unknown in the label' do - subscriber.request(double(:event, payload: event_1)) + subscriber.request(double(:event, payload: event_1, time: Time.current)) expect(subject.results[:details]).to contain_exactly( a_hash_including( + start: be_like_time(Time.current), duration: 30.0, label: "POST unknown", code: "Response status: 200", @@ -198,10 +210,11 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do end it 'displays unknown in the label' do - subscriber.request(double(:event, payload: event_1)) + subscriber.request(double(:event, payload: event_1, time: Time.current)) expect(subject.results[:details]).to contain_exactly( a_hash_including( + start: be_like_time(Time.current), duration: 30.0, label: "POST unknown", code: "Response status: 200", diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 1850363bb72..0a63f1d7fa0 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -835,6 +835,29 @@ RSpec.describe API::Projects do end.not_to exceed_query_limit(control.count) end end + + context 'when service desk is enabled', :use_clean_rails_memory_store_caching do + let_it_be(:admin) { create(:admin) } + + it 'avoids N+1 queries' do + allow(Gitlab::ServiceDeskEmail).to receive(:enabled?).and_return(true) + allow(Gitlab::IncomingEmail).to receive(:enabled?).and_return(true) + + get api('/projects', admin) + + create(:project, :public, :service_desk_enabled, namespace: admin.namespace) + + control = ActiveRecord::QueryRecorder.new do + get api('/projects', admin) + end + + create_list(:project, 2, :public, :service_desk_enabled, namespace: admin.namespace) + + expect do + get api('/projects', admin) + end.not_to exceed_query_limit(control.count) + end + end end describe 'POST /projects' do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a3925a0c0fb..1550e2f9b3b 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -277,6 +277,11 @@ RSpec.configure do |config| # Vue issues page has feature parity with the current Haml page stub_feature_flags(vue_issues_list: false) + # Disable `refactor_blob_viewer` as we refactor + # the blob viewer. See the follwing epic for more: + # https://gitlab.com/groups/gitlab-org/-/epics/5531 + stub_feature_flags(refactor_blob_viewer: false) + allow(Gitlab::GitalyClient).to receive(:can_use_disk?).and_return(enable_rugged) else unstub_all_feature_flags