Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
9ce920f62f
commit
cca8451493
|
@ -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"
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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 {
|
|||
>
|
||||
<gl-button v-gl-modal="modalId" class="gl-mr-2" type="button" variant="link">
|
||||
<span
|
||||
class="gl-text-blue-300 gl-font-weight-bold"
|
||||
class="gl-text-blue-200 gl-font-weight-bold"
|
||||
data-testid="performance-bar-details-label"
|
||||
>
|
||||
{{ metricDetailsLabel }}
|
||||
|
@ -208,7 +214,7 @@ export default {
|
|||
<div></div>
|
||||
</template>
|
||||
</gl-modal>
|
||||
{{ title }}
|
||||
<span class="gl-text-white">{{ title }}</span>
|
||||
<request-warning :html-id="htmlId" :warnings="warnings" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -136,7 +136,7 @@ export default {
|
|||
<div id="peek-view-host" class="view">
|
||||
<span
|
||||
v-if="hasHost"
|
||||
class="current-host"
|
||||
class="current-host gl-text-white"
|
||||
:class="{ canary: currentRequest.details.host.canary }"
|
||||
>
|
||||
<span v-html="birdEmoji"></span>
|
||||
|
@ -157,16 +157,16 @@ export default {
|
|||
id="peek-view-trace"
|
||||
class="view"
|
||||
>
|
||||
<a class="gl-text-blue-300" :href="currentRequest.details.tracing.tracing_url">{{
|
||||
<a class="gl-text-blue-200" :href="currentRequest.details.tracing.tracing_url">{{
|
||||
s__('PerformanceBar|Trace')
|
||||
}}</a>
|
||||
</div>
|
||||
<div v-if="currentRequest.details" id="peek-download" class="view">
|
||||
<a class="gl-text-blue-300" :download="downloadName" :href="downloadPath">{{
|
||||
<a class="gl-text-blue-200" :download="downloadName" :href="downloadPath">{{
|
||||
s__('PerformanceBar|Download')
|
||||
}}</a>
|
||||
</div>
|
||||
<a v-if="statsUrl" class="gl-text-blue-300 view" :href="statsUrl">{{
|
||||
<a v-if="statsUrl" class="gl-text-blue-200 view" :href="statsUrl">{{
|
||||
s__('PerformanceBar|Stats')
|
||||
}}</a>
|
||||
<request-selector
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* eslint-disable @gitlab/require-i18n-strings */
|
||||
import Vue from 'vue';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { numberToHumanSize } from '~/lib/utils/number_utils';
|
||||
import { s__ } from '~/locale';
|
||||
import Translate from '~/vue_shared/translate';
|
||||
|
||||
import initPerformanceBarLog from './performance_bar_log';
|
||||
|
@ -75,40 +76,53 @@ const initPerformanceBar = (el) => {
|
|||
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',
|
||||
};
|
||||
},
|
||||
},
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
color: $gray-300;
|
||||
|
||||
select {
|
||||
color: $gray-300;
|
||||
color: $white;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix N+1 in REST projects and service desk
|
||||
merge_request: 58747
|
||||
author:
|
||||
type: performance
|
|
@ -0,0 +1,4 @@
|
|||
title: Update performance bar background color to use Pajamas compliant colour palette
|
||||
merge_request: 52775
|
||||
author: Yogi (@yo)
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Enable chronological sort order for other items in the performance bar
|
||||
merge_request: 58572
|
||||
author:
|
||||
type: changed
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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 ""
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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`
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue