Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
a9a2f9257e
commit
386dcdbe9d
|
|
@ -115,6 +115,14 @@ include:
|
|||
--input-files "rspec/rspec-*.json" \
|
||||
--merge_request_iid "$CI_MERGE_REQUEST_IID";
|
||||
fi
|
||||
if [ "$ALLOW_KNAPSACK_REPORT_CREATE_ISSUES" == "true" ]; then
|
||||
bundle exec knapsack-report-issues \
|
||||
--token "${KNAPSACK_REPORT_ISSUES_PROJECT_TOKEN}" \
|
||||
--project "gitlab-org/gitlab" \
|
||||
--input-file knapsack/rspec_*_report.json \
|
||||
--expected-report node_specs_expected_duration.json \
|
||||
--merge_request_iid "$CI_MERGE_REQUEST_IID";
|
||||
fi
|
||||
- echo -e "\e[0Ksection_end:`date +%s`:report_results_section\r\e[0K"
|
||||
- tooling/bin/push_job_metrics || true
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
3.2.2
|
||||
3.2.3
|
||||
|
|
|
|||
2
Gemfile
2
Gemfile
|
|
@ -518,7 +518,7 @@ group :test do
|
|||
# Moved in `test` because https://gitlab.com/gitlab-org/gitlab/-/issues/217527
|
||||
gem 'derailed_benchmarks', require: false # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
|
||||
gem 'gitlab_quality-test_tooling', '~> 1.12.0', require: false, feature_category: :tooling
|
||||
gem 'gitlab_quality-test_tooling', '~> 1.13.0', require: false, feature_category: :tooling
|
||||
end
|
||||
|
||||
gem 'octokit', '~> 6.0' # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@
|
|||
{"name":"gitlab-styles","version":"11.0.0","platform":"ruby","checksum":"0dd8ec066ce9955ac51d3616c6bfded30f75bb526f39ff392ece6f43d5b9406b"},
|
||||
{"name":"gitlab_chronic_duration","version":"0.12.0","platform":"ruby","checksum":"0d766944d415b5c831f176871ee8625783fc0c5bfbef2d79a3a616f207ffc16d"},
|
||||
{"name":"gitlab_omniauth-ldap","version":"2.2.0","platform":"ruby","checksum":"bb4d20acb3b123ed654a8f6a47d3fac673ece7ed0b6992edb92dca14bad2838c"},
|
||||
{"name":"gitlab_quality-test_tooling","version":"1.12.0","platform":"ruby","checksum":"6d687db96777bd8c87264253a7cc9ce1d71851d2b20a64d594770c35555630b1"},
|
||||
{"name":"gitlab_quality-test_tooling","version":"1.13.0","platform":"ruby","checksum":"04c51c0b372a38e030572ab85b997234e139d0d639d617ce1924269021b51328"},
|
||||
{"name":"globalid","version":"1.1.0","platform":"ruby","checksum":"b337e1746f0c8cb0a6c918234b03a1ddeb4966206ce288fbb57779f59b2d154f"},
|
||||
{"name":"gon","version":"6.4.0","platform":"ruby","checksum":"e3a618d659392890f1aa7db420f17c75fd7d35aeb5f8fe003697d02c4b88d2f0"},
|
||||
{"name":"google-apis-androidpublisher_v3","version":"0.34.0","platform":"ruby","checksum":"d7e1d7dd92f79c498fe2082222a1740d788e022e660c135564b3fd299cab5425"},
|
||||
|
|
@ -572,7 +572,7 @@
|
|||
{"name":"ruby-saml","version":"1.15.0","platform":"ruby","checksum":"3a9dda2b448310f4f90d5cf0967d4b668530fa7994d2a4d9cbfdfa62e35f76a3"},
|
||||
{"name":"ruby-statistics","version":"3.0.0","platform":"ruby","checksum":"610301370346931cb701e3a8d3d3e28eb65681162cae6066c0c11abf20efdc81"},
|
||||
{"name":"ruby2_keywords","version":"0.0.5","platform":"ruby","checksum":"ffd13740c573b7301cf7a2e61fc857b2a8e3d3aff32545d6f8300d8bae10e3ef"},
|
||||
{"name":"ruby_parser","version":"3.20.3","platform":"ruby","checksum":"8d2289a695dc81ffddcdd5a56e80c9a109806bc0d0b1239a1c852b0c71251c49"},
|
||||
{"name":"ruby_parser","version":"3.21.0","platform":"ruby","checksum":"3842893d2f4602dcd93c0a79d77f9ce8e1ce197d41ac533d8e25c684f8f1c56b"},
|
||||
{"name":"rubyntlm","version":"0.6.3","platform":"ruby","checksum":"5b321456dba3130351f7451f8669f1afa83a0d26fd63cdec285b7b88e667102d"},
|
||||
{"name":"rubypants","version":"0.2.0","platform":"ruby","checksum":"f07e38eac793655a0323fe91946081052341b9e69807026fcf102346589eedee"},
|
||||
{"name":"rubyzip","version":"2.3.2","platform":"ruby","checksum":"3f57e3935dc2255c414484fbf8d673b4909d8a6a57007ed754dde39342d2373f"},
|
||||
|
|
@ -593,7 +593,7 @@
|
|||
{"name":"sentry-ruby","version":"5.10.0","platform":"ruby","checksum":"115c24c0aee1309210f3a2988fb118e2bec1f11609feeda90e694388b1183619"},
|
||||
{"name":"sentry-sidekiq","version":"5.10.0","platform":"ruby","checksum":"cc81018d0733fb1be3fb5641c9e0b61030bbeaa1d0b23ca64797d70def7aea1a"},
|
||||
{"name":"set","version":"1.0.2","platform":"ruby","checksum":"02ffa4de1f2621495e05b72326040dd014d7abbcb02fea698bc600a389992c02"},
|
||||
{"name":"sexp_processor","version":"4.17.0","platform":"ruby","checksum":"4daa4874ce1838cd801c65e66ed5d4f140024404a3de7482c36d4ef2604dff6f"},
|
||||
{"name":"sexp_processor","version":"4.17.1","platform":"ruby","checksum":"91110946720307f30bf1d549e90d9a529fef40d1fc471c069c8cca7667015da0"},
|
||||
{"name":"shellany","version":"0.0.1","platform":"ruby","checksum":"0e127a9132698766d7e752e82cdac8250b6adbd09e6c0a7fbbb6f61964fedee7"},
|
||||
{"name":"shoulda-matchers","version":"5.1.0","platform":"ruby","checksum":"a01d20589989e9653ab4a28c67d9db2b82bcf0a2496cf01d5e1a95a4aaaf5b07"},
|
||||
{"name":"sidekiq","version":"7.1.6","platform":"ruby","checksum":"7859da66d5bcef3c22bea2c3091d08c866890168e003f5bf4dea197dc37843a2"},
|
||||
|
|
|
|||
|
|
@ -732,7 +732,7 @@ GEM
|
|||
omniauth (>= 1.3, < 3)
|
||||
pyu-ruby-sasl (>= 0.0.3.3, < 0.1)
|
||||
rubyntlm (~> 0.5)
|
||||
gitlab_quality-test_tooling (1.12.0)
|
||||
gitlab_quality-test_tooling (1.13.0)
|
||||
activesupport (>= 6.1, < 7.2)
|
||||
amatch (~> 0.4.1)
|
||||
gitlab (~> 4.19)
|
||||
|
|
@ -1500,7 +1500,8 @@ GEM
|
|||
rexml
|
||||
ruby-statistics (3.0.0)
|
||||
ruby2_keywords (0.0.5)
|
||||
ruby_parser (3.20.3)
|
||||
ruby_parser (3.21.0)
|
||||
racc (~> 1.5)
|
||||
sexp_processor (~> 4.16)
|
||||
rubyntlm (0.6.3)
|
||||
rubypants (0.2.0)
|
||||
|
|
@ -1547,7 +1548,7 @@ GEM
|
|||
sentry-ruby (~> 5.10.0)
|
||||
sidekiq (>= 3.0)
|
||||
set (1.0.2)
|
||||
sexp_processor (4.17.0)
|
||||
sexp_processor (4.17.1)
|
||||
shellany (0.0.1)
|
||||
shoulda-matchers (5.1.0)
|
||||
activesupport (>= 5.2.0)
|
||||
|
|
@ -1914,7 +1915,7 @@ DEPENDENCIES
|
|||
gitlab-utils!
|
||||
gitlab_chronic_duration (~> 0.12)
|
||||
gitlab_omniauth-ldap (~> 2.2.0)
|
||||
gitlab_quality-test_tooling (~> 1.12.0)
|
||||
gitlab_quality-test_tooling (~> 1.13.0)
|
||||
gon (~> 6.4.0)
|
||||
google-apis-androidpublisher_v3 (~> 0.34.0)
|
||||
google-apis-cloudbilling_v1 (~> 0.21.0)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import {
|
|||
MR_COMMITS_PREVIOUS_COMMIT,
|
||||
} from '~/behaviors/shortcuts/keybindings';
|
||||
import { createAlert } from '~/alert';
|
||||
import { InternalEvents } from '~/tracking';
|
||||
import { isSingleViewStyle } from '~/helpers/diffs_helper';
|
||||
import { helpPagePath } from '~/helpers/help_page_helper';
|
||||
import { parseBoolean, handleLocationHash } from '~/lib/utils/common_utils';
|
||||
|
|
@ -86,7 +87,7 @@ export default {
|
|||
GlSprintf,
|
||||
GlAlert,
|
||||
},
|
||||
mixins: [glFeatureFlagsMixin()],
|
||||
mixins: [glFeatureFlagsMixin(), InternalEvents.mixin()],
|
||||
alerts: {
|
||||
ALERT_OVERFLOW_HIDDEN,
|
||||
ALERT_MERGE_CONFLICT,
|
||||
|
|
@ -443,6 +444,8 @@ export default {
|
|||
notesEventHub.$once('fetchDiffData', this.fetchData);
|
||||
notesEventHub.$on('refetchDiffData', this.refetchDiffData);
|
||||
notesEventHub.$on('fetchedNotesData', this.rereadNoteHash);
|
||||
notesEventHub.$on('noteFormAddToReview', this.handleReviewTracking);
|
||||
notesEventHub.$on('noteFormStartReview', this.handleReviewTracking);
|
||||
diffsEventHub.$on('diffFilesModified', this.setDiscussions);
|
||||
diffsEventHub.$on('doneLoadingBatches', this.autoScroll);
|
||||
diffsEventHub.$on(EVT_MR_PREPARED, this.fetchData);
|
||||
|
|
@ -453,6 +456,8 @@ export default {
|
|||
diffsEventHub.$off(EVT_MR_PREPARED, this.fetchData);
|
||||
diffsEventHub.$off('doneLoadingBatches', this.autoScroll);
|
||||
diffsEventHub.$off('diffFilesModified', this.setDiscussions);
|
||||
notesEventHub.$off('noteFormStartReview', this.handleReviewTracking);
|
||||
notesEventHub.$off('noteFormAddToReview', this.handleReviewTracking);
|
||||
notesEventHub.$off('fetchedNotesData', this.rereadNoteHash);
|
||||
notesEventHub.$off('refetchDiffData', this.refetchDiffData);
|
||||
notesEventHub.$off('fetchDiffData', this.fetchData);
|
||||
|
|
@ -679,6 +684,16 @@ export default {
|
|||
reloadPage() {
|
||||
window.location.reload();
|
||||
},
|
||||
handleReviewTracking(event) {
|
||||
const types = {
|
||||
noteFormStartReview: 'merge_request_click_start_review_on_changes_tab',
|
||||
noteFormAddToReview: 'merge_request_click_add_to_review_on_changes_tab',
|
||||
};
|
||||
|
||||
if (this.shouldShow && types[event.name]) {
|
||||
this.trackEvent(types[event.name]);
|
||||
}
|
||||
},
|
||||
},
|
||||
howToMergeDocsPath: helpPagePath('user/project/merge_requests/reviews/index.md', {
|
||||
anchor: 'checkout-merge-requests-locally-through-the-head-ref',
|
||||
|
|
|
|||
|
|
@ -401,6 +401,8 @@ export const nWeeksBefore = (date, numberOfWeeks, options) =>
|
|||
|
||||
/**
|
||||
* Returns the date `n` years after the date provided.
|
||||
* When Feb 29 is the specified date, the default behaviour is to return March 1.
|
||||
* But to align with the equivalent rails code, moment JS and datefns we should return Feb 28 instead.
|
||||
*
|
||||
* @param {Date} date the initial date
|
||||
* @param {Number} numberOfYears number of years after
|
||||
|
|
@ -408,7 +410,16 @@ export const nWeeksBefore = (date, numberOfWeeks, options) =>
|
|||
*/
|
||||
export const nYearsAfter = (date, numberOfYears) => {
|
||||
const clone = newDate(date);
|
||||
clone.setFullYear(clone.getFullYear() + numberOfYears);
|
||||
clone.setUTCMonth(clone.getUTCMonth());
|
||||
|
||||
// If the date we are calculating from is Feb 29, return the equivalent result for Feb 28
|
||||
if (clone.getUTCMonth() === 1 && clone.getUTCDate() === 29) {
|
||||
clone.setUTCDate(28);
|
||||
} else {
|
||||
clone.setUTCDate(clone.getUTCDate());
|
||||
}
|
||||
|
||||
clone.setUTCFullYear(clone.getUTCFullYear() + numberOfYears);
|
||||
return clone;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import {
|
|||
slugifyWithUnderscore,
|
||||
} from '~/lib/utils/text_utility';
|
||||
import { sprintf } from '~/locale';
|
||||
import { InternalEvents } from '~/tracking';
|
||||
import { badgeState } from '~/merge_requests/components/merge_request_header.vue';
|
||||
import MarkdownEditor from '~/vue_shared/components/markdown/markdown_editor.vue';
|
||||
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
|
||||
|
|
@ -48,7 +49,7 @@ export default {
|
|||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
mixins: [issuableStateMixin],
|
||||
mixins: [issuableStateMixin, InternalEvents.mixin()],
|
||||
props: {
|
||||
noteableType: {
|
||||
type: String,
|
||||
|
|
@ -253,6 +254,10 @@ export default {
|
|||
|
||||
this.isSubmitting = true;
|
||||
|
||||
if (isDraft) {
|
||||
eventHub.$emit('noteFormAddToReview', { name: 'noteFormAddToReview' });
|
||||
}
|
||||
|
||||
trackSavedUsingEditor(
|
||||
this.$refs.markdownEditor.isContentEditorActive,
|
||||
`${this.noteableType}_${this.noteType}`,
|
||||
|
|
|
|||
|
|
@ -320,12 +320,14 @@ export default {
|
|||
);
|
||||
},
|
||||
handleAddToReview() {
|
||||
const clickType = this.hasDrafts ? 'noteFormAddToReview' : 'noteFormStartReview';
|
||||
// check if draft should resolve thread
|
||||
const shouldResolve =
|
||||
(this.discussionResolved && !this.isUnresolving) ||
|
||||
(!this.discussionResolved && this.isResolving);
|
||||
this.isSubmitting = true;
|
||||
|
||||
eventHub.$emit(clickType, { name: clickType });
|
||||
this.$emit(
|
||||
'handleFormUpdateAddToReview',
|
||||
this.updatedNoteBody,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// eslint-disable-next-line no-restricted-imports
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { InternalEvents } from '~/tracking';
|
||||
import highlightCurrentUser from '~/behaviors/markdown/highlight_current_user';
|
||||
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
|
||||
import OrderedLayout from '~/vue_shared/components/ordered_layout.vue';
|
||||
|
|
@ -39,7 +40,7 @@ export default {
|
|||
TimelineEntryItem,
|
||||
AiSummary: () => import('ee_component/notes/components/ai_summary.vue'),
|
||||
},
|
||||
mixins: [glFeatureFlagsMixin()],
|
||||
mixins: [glFeatureFlagsMixin(), InternalEvents.mixin()],
|
||||
provide() {
|
||||
return {
|
||||
summarizeClientSubscriptionId: uuidv4(),
|
||||
|
|
@ -165,6 +166,9 @@ export default {
|
|||
});
|
||||
}
|
||||
|
||||
eventHub.$on('noteFormAddToReview', this.handleReviewTracking);
|
||||
eventHub.$on('noteFormStartReview', this.handleReviewTracking);
|
||||
|
||||
window.addEventListener('hashchange', this.handleHashChanged);
|
||||
|
||||
eventHub.$on('notesApp.updateIssuableConfidentiality', this.setConfidentiality);
|
||||
|
|
@ -177,6 +181,8 @@ export default {
|
|||
beforeDestroy() {
|
||||
window.removeEventListener('hashchange', this.handleHashChanged);
|
||||
eventHub.$off('notesApp.updateIssuableConfidentiality', this.setConfidentiality);
|
||||
eventHub.$off('noteFormStartReview', this.handleReviewTracking);
|
||||
eventHub.$off('noteFormAddToReview', this.handleReviewTracking);
|
||||
},
|
||||
methods: {
|
||||
...mapActions([
|
||||
|
|
@ -222,6 +228,16 @@ export default {
|
|||
setAiLoading(loading) {
|
||||
this.aiLoading = loading;
|
||||
},
|
||||
handleReviewTracking(event) {
|
||||
const types = {
|
||||
noteFormStartReview: 'merge_request_click_start_review_on_overview_tab',
|
||||
noteFormAddToReview: 'merge_request_click_add_to_review_on_overview_tab',
|
||||
};
|
||||
|
||||
if (this.shouldShow && window.mrTabs && types[event.name]) {
|
||||
this.trackEvent(types[event.name]);
|
||||
}
|
||||
},
|
||||
},
|
||||
systemNote: constants.SYSTEM_NOTE,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,20 +1,23 @@
|
|||
<script>
|
||||
import { GlAlert } from '@gitlab/ui';
|
||||
import { GlAlert, GlKeysetPagination } from '@gitlab/ui';
|
||||
import StorageUsageStatistics from 'ee_else_ce/usage_quotas/storage/components/storage_usage_statistics.vue';
|
||||
import SearchAndSortBar from '~/usage_quotas/components/search_and_sort_bar/search_and_sort_bar.vue';
|
||||
import DependencyProxyUsage from './dependency_proxy_usage.vue';
|
||||
import ContainerRegistryUsage from './container_registry_usage.vue';
|
||||
import ProjectList from './project_list.vue';
|
||||
|
||||
export default {
|
||||
name: 'NamespaceStorageApp',
|
||||
components: {
|
||||
GlAlert,
|
||||
GlKeysetPagination,
|
||||
StorageUsageStatistics,
|
||||
DependencyProxyUsage,
|
||||
ContainerRegistryUsage,
|
||||
SearchAndSortBar,
|
||||
ProjectList,
|
||||
},
|
||||
inject: ['userNamespace', 'namespaceId'],
|
||||
inject: ['userNamespace', 'namespaceId', 'helpLinks', 'defaultPerPage'],
|
||||
props: {
|
||||
namespaceLoadingError: {
|
||||
type: Boolean,
|
||||
|
|
@ -31,11 +34,26 @@ export default {
|
|||
required: false,
|
||||
default: false,
|
||||
},
|
||||
isNamespaceProjectsLoading: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
namespace: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({}),
|
||||
},
|
||||
projects: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({}),
|
||||
},
|
||||
initialSortBy: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'storage',
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
usedStorage() {
|
||||
|
|
@ -55,6 +73,27 @@ export default {
|
|||
containerRegistrySizeIsEstimated() {
|
||||
return this.namespace.rootStorageStatistics?.containerRegistrySizeIsEstimated ?? false;
|
||||
},
|
||||
projectList() {
|
||||
return this.projects?.nodes ?? [];
|
||||
},
|
||||
pageInfo() {
|
||||
return this.projects?.pageInfo;
|
||||
},
|
||||
showPagination() {
|
||||
return Boolean(this.pageInfo?.hasPreviousPage || this.pageInfo?.hasNextPage);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onPrev(before) {
|
||||
if (this.pageInfo?.hasPreviousPage) {
|
||||
this.$emit('fetch-more-projects', { before, last: this.defaultPerPage, first: undefined });
|
||||
}
|
||||
},
|
||||
onNext(after) {
|
||||
if (this.pageInfo?.hasNextPage) {
|
||||
this.$emit('fetch-more-projects', { after, first: this.defaultPerPage });
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -103,7 +142,26 @@ export default {
|
|||
"
|
||||
/>
|
||||
</div>
|
||||
<slot name="ee-storage-app"></slot>
|
||||
<project-list
|
||||
:projects="projectList"
|
||||
:is-loading="isNamespaceProjectsLoading"
|
||||
:help-links="helpLinks"
|
||||
:sort-by="initialSortBy"
|
||||
:sort-desc="true"
|
||||
@sortChanged="
|
||||
($event) => {
|
||||
$emit('sort-changed', $event);
|
||||
}
|
||||
"
|
||||
/>
|
||||
<div class="gl-display-flex gl-justify-content-center gl-mt-5">
|
||||
<gl-keyset-pagination
|
||||
v-if="showPagination"
|
||||
v-bind="pageInfo"
|
||||
@prev="onPrev"
|
||||
@next="onNext"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,208 @@
|
|||
<script>
|
||||
import { GlTable, GlLink, GlSprintf, GlIcon } from '@gitlab/ui';
|
||||
import { __ } from '~/locale';
|
||||
import ProjectAvatar from '~/vue_shared/components/project_avatar.vue';
|
||||
import { containerRegistryPopover } from '~/usage_quotas/storage/constants';
|
||||
import NumberToHumanSize from '~/vue_shared/components/number_to_human_size/number_to_human_size.vue';
|
||||
import HelpPageLink from '~/vue_shared/components/help_page_link/help_page_link.vue';
|
||||
import StorageTypeHelpLink from './storage_type_help_link.vue';
|
||||
import StorageTypeWarning from './storage_type_warning.vue';
|
||||
|
||||
export default {
|
||||
name: 'ProjectList',
|
||||
components: {
|
||||
GlTable,
|
||||
GlLink,
|
||||
GlSprintf,
|
||||
GlIcon,
|
||||
ProjectAvatar,
|
||||
NumberToHumanSize,
|
||||
HelpPageLink,
|
||||
StorageTypeHelpLink,
|
||||
StorageTypeWarning,
|
||||
},
|
||||
inject: ['isUsingProjectEnforcementWithLimits'],
|
||||
props: {
|
||||
projects: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
isLoading: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
helpLinks: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
sortBy: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: undefined,
|
||||
},
|
||||
sortDesc: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.fields = [
|
||||
{ key: 'name', label: __('Project') },
|
||||
{ key: 'storage', label: __('Total'), sortable: !this.isUsingProjectEnforcementWithLimits },
|
||||
{ key: 'repository', label: __('Repository') },
|
||||
{ key: 'snippets', label: __('Snippets') },
|
||||
{ key: 'buildArtifacts', label: __('Jobs') },
|
||||
{ key: 'lfsObjects', label: __('LFS') },
|
||||
{ key: 'packages', label: __('Packages') },
|
||||
{ key: 'wiki', label: __('Wiki') },
|
||||
{
|
||||
key: 'containerRegistry',
|
||||
label: __('Containers'),
|
||||
thClass: 'gl-border-l!',
|
||||
tdClass: 'gl-border-l!',
|
||||
},
|
||||
].map((f) => ({
|
||||
...f,
|
||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||
thClass: `${f.thClass ?? ''} gl-px-3!`,
|
||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||
tdClass: `${f.tdClass ?? ''} gl-px-3!`,
|
||||
}));
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* Builds a gl-table td cell slot name for particular field
|
||||
* @param {string} key
|
||||
* @returns {string} */
|
||||
getHeaderSlotName(key) {
|
||||
return `head(${key})`;
|
||||
},
|
||||
getUsageQuotasUrl(projectUrl) {
|
||||
return `${projectUrl}/-/usage_quotas`;
|
||||
},
|
||||
/**
|
||||
* Creates a relative path from a full project path.
|
||||
* E.g. input `namespace / subgroup / project`
|
||||
* results in `subgroup / project`
|
||||
*/
|
||||
getProjectRelativePath(fullPath) {
|
||||
return fullPath.replace(/.*?\s?\/\s?/, '');
|
||||
},
|
||||
isCostFactored(project) {
|
||||
return project.statistics.storageSize !== project.statistics.costFactoredStorageSize;
|
||||
},
|
||||
},
|
||||
containerRegistryPopover,
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-table
|
||||
:fields="fields"
|
||||
:items="projects"
|
||||
:busy="isLoading"
|
||||
show-empty
|
||||
:empty-text="s__('UsageQuota|No projects to display.')"
|
||||
small
|
||||
stacked="lg"
|
||||
:sort-by="sortBy"
|
||||
:sort-desc="sortDesc"
|
||||
no-local-sorting
|
||||
no-sort-reset
|
||||
@sort-changed="$emit('sortChanged', $event)"
|
||||
>
|
||||
<template v-for="field in fields" #[getHeaderSlotName(field.key)]>
|
||||
<div :key="field.key" :data-testid="'th-' + field.key">
|
||||
{{ field.label }}
|
||||
|
||||
<storage-type-help-link
|
||||
v-if="field.key in helpLinks"
|
||||
:storage-type="field.key"
|
||||
:help-links="helpLinks"
|
||||
/><storage-type-warning v-if="field.key == 'containerRegistry'">
|
||||
{{ $options.containerRegistryPopover.content }}
|
||||
<gl-link :href="$options.containerRegistryPopover.docsLink" target="_blank">
|
||||
{{ __('Learn more.') }}
|
||||
</gl-link>
|
||||
</storage-type-warning>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #cell(name)="{ item: project }">
|
||||
<project-avatar
|
||||
:project-id="project.id"
|
||||
:project-name="project.name"
|
||||
:project-avatar-url="project.avatarUrl"
|
||||
:size="16"
|
||||
:alt="project.name"
|
||||
class="gl-display-inline-block gl-mr-2 gl-text-center!"
|
||||
/>
|
||||
|
||||
<gl-link
|
||||
:href="getUsageQuotasUrl(project.webUrl)"
|
||||
class="gl-text-gray-900! js-project-link gl-word-break-word"
|
||||
data-testid="project-link"
|
||||
>
|
||||
{{ getProjectRelativePath(project.nameWithNamespace) }}
|
||||
</gl-link>
|
||||
</template>
|
||||
|
||||
<template #cell(storage)="{ item: project }">
|
||||
<template v-if="isCostFactored(project)">
|
||||
<number-to-human-size :value="project.statistics.costFactoredStorageSize" />
|
||||
|
||||
<div class="gl-text-gray-600 gl-mt-2 gl-font-sm">
|
||||
<gl-sprintf :message="s__('UsageQuotas|(of %{totalStorageSize})')">
|
||||
<template #totalStorageSize>
|
||||
<number-to-human-size :value="project.statistics.storageSize" />
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
<help-page-link href="user/usage_quotas#view-project-fork-storage-usage" target="_blank">
|
||||
<gl-icon name="question-o" :size="12" />
|
||||
</help-page-link>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<number-to-human-size :value="project.statistics.storageSize" />
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<template #cell(repository)="{ item: project }">
|
||||
<number-to-human-size
|
||||
:value="project.statistics.repositorySize"
|
||||
data-testid="project-repository-size"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #cell(lfsObjects)="{ item: project }">
|
||||
<number-to-human-size :value="project.statistics.lfsObjectsSize" />
|
||||
</template>
|
||||
|
||||
<template #cell(buildArtifacts)="{ item: project }">
|
||||
<number-to-human-size :value="project.statistics.buildArtifactsSize" />
|
||||
</template>
|
||||
|
||||
<template #cell(packages)="{ item: project }">
|
||||
<number-to-human-size :value="project.statistics.packagesSize" />
|
||||
</template>
|
||||
|
||||
<template #cell(wiki)="{ item: project }">
|
||||
<number-to-human-size :value="project.statistics.wikiSize" data-testid="project-wiki-size" />
|
||||
</template>
|
||||
|
||||
<template #cell(snippets)="{ item: project }">
|
||||
<number-to-human-size
|
||||
:value="project.statistics.snippetsSize"
|
||||
data-testid="project-snippets-size"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #cell(containerRegistry)="{ item: project }">
|
||||
<number-to-human-size
|
||||
:value="project.statistics.containerRegistrySize"
|
||||
data-testid="project-containers-registry-size"
|
||||
/>
|
||||
</template>
|
||||
</gl-table>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<script>
|
||||
import { GlLink, GlIcon } from '@gitlab/ui';
|
||||
import { sprintf } from '~/locale';
|
||||
import { HELP_LINK_ARIA_LABEL } from '~/usage_quotas/storage/constants';
|
||||
|
||||
export default {
|
||||
name: 'StorageTypeHelpLink',
|
||||
components: {
|
||||
GlLink,
|
||||
GlIcon,
|
||||
},
|
||||
props: {
|
||||
storageType: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
helpLinks: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
ariaLabel() {
|
||||
return sprintf(HELP_LINK_ARIA_LABEL, {
|
||||
linkTitle: this.storageType,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-link :href="helpLinks[storageType]" target="_blank" :aria-label="ariaLabel">
|
||||
<gl-icon name="question-o" :size="12" />
|
||||
</gl-link>
|
||||
</template>
|
||||
|
|
@ -16,7 +16,7 @@ module ServicePing
|
|||
end
|
||||
|
||||
def execute
|
||||
return unless ServicePing::ServicePingSettings.product_intelligence_enabled?
|
||||
return unless ServicePing::ServicePingSettings.enabled_and_consented?
|
||||
|
||||
start_time = Time.current
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
description: User clicks the "Add to review" button on the Merge Request Changes tab
|
||||
category: InternalEventTracking
|
||||
action: merge_request_click_add_to_review_on_changes_tab
|
||||
identifiers:
|
||||
- project
|
||||
- namespace
|
||||
- user
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: code_review
|
||||
milestone: '16.9'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141911
|
||||
distributions:
|
||||
- ce
|
||||
- ee
|
||||
tiers:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
description: User clicks the "Add to review" button on the Merge Request Overview tab
|
||||
category: InternalEventTracking
|
||||
action: merge_request_click_add_to_review_on_overview_tab
|
||||
identifiers:
|
||||
- project
|
||||
- namespace
|
||||
- user
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: code_review
|
||||
milestone: '16.9'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141911
|
||||
distributions:
|
||||
- ce
|
||||
- ee
|
||||
tiers:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
description: User clicks the "Start a review" button on the Merge Request Changes tab
|
||||
category: InternalEventTracking
|
||||
action: merge_request_click_start_review_on_changes_tab
|
||||
identifiers:
|
||||
- project
|
||||
- namespace
|
||||
- user
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: code_review
|
||||
milestone: '16.9'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141911
|
||||
distributions:
|
||||
- ce
|
||||
- ee
|
||||
tiers:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
description: User clicks the "Start a review" button on the Merge Request Overview tab
|
||||
category: InternalEventTracking
|
||||
action: merge_request_click_start_review_on_overview_tab
|
||||
identifiers:
|
||||
- project
|
||||
- namespace
|
||||
- user
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: code_review
|
||||
milestone: '16.9'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141911
|
||||
distributions:
|
||||
- ce
|
||||
- ee
|
||||
tiers:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
---
|
||||
name: github_importer_attachments
|
||||
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/436400
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140524
|
||||
rollout_issue_url: https://gitlab.com/gitlab-com/gl-infra/production/-/issues/17318
|
||||
milestone: '16.8'
|
||||
group: group::import and integrate
|
||||
type: gitlab_com_derisk
|
||||
default_enabled: false
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
key_path: counts.count_total_merge_request_click_add_to_review_on_changes_tab_monthly
|
||||
description: Monthly count of clicks on the Add to review button on the MR Changes tab
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: code_review
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '16.9'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141911
|
||||
time_frame: 28d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
options:
|
||||
events:
|
||||
- merge_request_click_add_to_review_on_changes_tab
|
||||
events:
|
||||
- name: merge_request_click_add_to_review_on_changes_tab
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
key_path: counts.count_total_merge_request_click_add_to_review_on_overview_tab_monthly
|
||||
description: Monthly count of clicks on the Add to review button on the MR Overview tab
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: code_review
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '16.9'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141911
|
||||
time_frame: 28d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
options:
|
||||
events:
|
||||
- merge_request_click_add_to_review_on_overview_tab
|
||||
events:
|
||||
- name: merge_request_click_add_to_review_on_overview_tab
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
key_path: counts.count_total_merge_request_click_start_review_on_changes_tab_monthly
|
||||
description: Monthly count of clicks on the Start a Review button on the MR Changes tab
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: code_review
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '16.9'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141911
|
||||
time_frame: 28d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
options:
|
||||
events:
|
||||
- merge_request_click_start_review_on_changes_tab
|
||||
events:
|
||||
- name: merge_request_click_start_review_on_changes_tab
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
key_path: counts.count_total_merge_request_click_start_review_on_overview_tab_monthly
|
||||
description: Monthly count of clicks on the Start a Review button on the MR Overview tab
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: code_review
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '16.9'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141911
|
||||
time_frame: 28d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
options:
|
||||
events:
|
||||
- merge_request_click_start_review_on_overview_tab
|
||||
events:
|
||||
- name: merge_request_click_start_review_on_overview_tab
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
key_path: counts.count_total_merge_request_click_add_to_review_on_changes_tab_weekly
|
||||
description: Weekly count of clicks on the Add to review button on the MR Changes tab
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: code_review
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '16.9'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141911
|
||||
time_frame: 7d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
options:
|
||||
events:
|
||||
- merge_request_click_add_to_review_on_changes_tab
|
||||
events:
|
||||
- name: merge_request_click_add_to_review_on_changes_tab
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
key_path: counts.count_total_merge_request_click_add_to_review_on_overview_tab_weekly
|
||||
description: Weekly count of clicks on the Add to review button on the MR Overview tab
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: code_review
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '16.9'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141911
|
||||
time_frame: 7d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
options:
|
||||
events:
|
||||
- merge_request_click_add_to_review_on_overview_tab
|
||||
events:
|
||||
- name: merge_request_click_add_to_review_on_overview_tab
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
key_path: counts.count_total_merge_request_click_start_review_on_changes_tab_weekly
|
||||
description: Weekly count of clicks on the Start a Review button on the MR Changes tab
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: code_review
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '16.9'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141911
|
||||
time_frame: 7d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
options:
|
||||
events:
|
||||
- merge_request_click_start_review_on_changes_tab
|
||||
events:
|
||||
- name: merge_request_click_start_review_on_changes_tab
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
key_path: counts.count_total_merge_request_click_start_review_on_overview_tab_weekly
|
||||
description: Weekly count of clicks on the Start a Review button on the MR Overview tab
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: code_review
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '16.9'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141911
|
||||
time_frame: 7d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
options:
|
||||
events:
|
||||
- merge_request_click_start_review_on_overview_tab
|
||||
events:
|
||||
- name: merge_request_click_start_review_on_overview_tab
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
key_path: counts.count_total_merge_request_click_add_to_review_on_changes_tab
|
||||
description: Total count of clicks on the Add to review button on the MR Changes tab
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: code_review
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '16.9'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141911
|
||||
time_frame: all
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
options:
|
||||
events:
|
||||
- merge_request_click_add_to_review_on_changes_tab
|
||||
events:
|
||||
- name: merge_request_click_add_to_review_on_changes_tab
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
key_path: counts.count_total_merge_request_click_add_to_review_on_overview_tab
|
||||
description: Total count of clicks on the Add to review button on the MR Overview tab
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: code_review
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '16.9'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141911
|
||||
time_frame: all
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
options:
|
||||
events:
|
||||
- merge_request_click_add_to_review_on_overview_tab
|
||||
events:
|
||||
- name: merge_request_click_add_to_review_on_overview_tab
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
key_path: counts.count_total_merge_request_click_start_review_on_changes_tab
|
||||
description: Total count of clicks on the Start a Review button on the MR Changes tab
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: code_review
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '16.9'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141911
|
||||
time_frame: all
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
options:
|
||||
events:
|
||||
- merge_request_click_start_review_on_changes_tab
|
||||
events:
|
||||
- name: merge_request_click_start_review_on_changes_tab
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
key_path: counts.count_total_merge_request_click_start_review_on_overview_tab
|
||||
description: Total count of clicks on the Start a Review button on the MR Overview tab
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: code_review
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '16.9'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141911
|
||||
time_frame: all
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
options:
|
||||
events:
|
||||
- merge_request_click_start_review_on_overview_tab
|
||||
events:
|
||||
- name: merge_request_click_start_review_on_overview_tab
|
||||
|
|
@ -13,6 +13,9 @@ Resource state events keep track of what happens to GitLab [issues](../user/proj
|
|||
|
||||
Use them to track which state was set, who did it, and when it happened.
|
||||
|
||||
Resource state events API does not track the initial state ("create" or "open") of resources.
|
||||
For a resource that was not closed or re-opened, an empty list is returned.
|
||||
|
||||
## Issues
|
||||
|
||||
### List project issue state events
|
||||
|
|
|
|||
|
|
@ -32,9 +32,8 @@ In the Markdown doc for a resource (AKA endpoint):
|
|||
- Every method must have a detailed [description of the response body](#response-body-description).
|
||||
- Every method must have a response body example (in JSON format).
|
||||
- If an attribute is available only to higher level tiers than the other
|
||||
attributes, add the appropriate inline [tier badge](styleguide/index.md#product-tier-badges).
|
||||
Put the badge in the **Attribute** column, like the
|
||||
`**(<tier>)**` code in the following template.
|
||||
attributes, add the appropriate tier to the description. If an attribute is
|
||||
for Premium, include that it's also available for Ultimate.
|
||||
|
||||
After a new API documentation page is added, [add an entry in the global navigation](site_architecture/global_nav.md#add-a-navigation-entry). [Example](https://gitlab.com/gitlab-org/gitlab-docs/-/merge_requests/3497).
|
||||
|
||||
|
|
@ -65,7 +64,7 @@ Supported attributes:
|
|||
| Attribute | Type | Required | Description |
|
||||
|--------------------------|----------|----------|-----------------------|
|
||||
| `attribute` | datatype | Yes | Detailed description. |
|
||||
| `attribute` **(<tier>)** | datatype | No | Detailed description. |
|
||||
| `attribute` | datatype | No | Detailed description. |
|
||||
| `attribute` | datatype | No | Detailed description. |
|
||||
| `attribute` | datatype | No | Detailed description. |
|
||||
|
||||
|
|
@ -75,7 +74,7 @@ response attributes:
|
|||
| Attribute | Type | Description |
|
||||
|--------------------------|----------|-----------------------|
|
||||
| `attribute` | datatype | Detailed description. |
|
||||
| `attribute` **(<tier>)** | datatype | Detailed description. |
|
||||
| `attribute` | datatype | Detailed description. |
|
||||
|
||||
Example request:
|
||||
|
||||
|
|
@ -146,7 +145,7 @@ Sort the table by required attributes first, then alphabetically.
|
|||
| Attribute | Type | Required | Description |
|
||||
|------------------------------|---------------|----------|-----------------------------------------------------|
|
||||
| `title` | string | Yes | Title of the issue. |
|
||||
| `assignee_ids` **(PREMIUM ALL)** | integer array | No | IDs of the users to assign the issue to. |
|
||||
| `assignee_ids` | integer array | No | IDs of the users to assign the issue to. Ultimate only. |
|
||||
| `confidential` | boolean | No | Sets the issue to confidential. Default is `false`. |
|
||||
```
|
||||
|
||||
|
|
@ -155,7 +154,7 @@ Rendered example:
|
|||
| Attribute | Type | Required | Description |
|
||||
|------------------------------|---------------|----------|-----------------------------------------------------|
|
||||
| `title` | string | Yes | Title of the issue. |
|
||||
| `assignee_ids` **(PREMIUM ALL)** | integer array | No | IDs of the users to assign the issue to. |
|
||||
| `assignee_ids` | integer array | No | IDs of the users to assign the issue to. Premium and Ultimate only. |
|
||||
| `confidential` | boolean | No | Sets the issue to confidential. Default is `false`. |
|
||||
|
||||
For information about writing attribute descriptions, see the [GraphQL API description style guide](../api_graphql_styleguide.md#description-style-guide).
|
||||
|
|
@ -181,7 +180,7 @@ Sort the table alphabetically.
|
|||
```markdown
|
||||
| Attribute | Type | Description |
|
||||
|------------------------------|---------------|-------------------------------------------|
|
||||
| `assignee_ids` **(PREMIUM ALL)** | integer array | IDs of the users to assign the issue to. |
|
||||
| `assignee_ids` | integer array | IDs of the users to assign the issue to. Premium and Ultimate only. |
|
||||
| `confidential` | boolean | Whether the issue is confidential or not. |
|
||||
| `title` | string | Title of the issue. |
|
||||
```
|
||||
|
|
@ -190,7 +189,7 @@ Rendered example:
|
|||
|
||||
| Attribute | Type | Description |
|
||||
|------------------------------|---------------|-------------------------------------------|
|
||||
| `assignee_ids` **(PREMIUM ALL)** | integer array | IDs of the users to assign the issue to. |
|
||||
| `assignee_ids` | integer array | IDs of the users to assign the issue to. Premium and Ultimate only. |
|
||||
| `confidential` | boolean | Whether the issue is confidential or not. |
|
||||
| `title` | string | Title of the issue. |
|
||||
|
||||
|
|
|
|||
|
|
@ -1648,7 +1648,7 @@ Tier badges provide information about a feature and are displayed next to the to
|
|||
|
||||
Assign tier badges to:
|
||||
|
||||
- All H1 topic titles, except the pages under `doc/development/*` and `doc/solutions/*`.
|
||||
- Most H1 topic titles, except the pages under `doc/development/*` and `doc/solutions/*`.
|
||||
- Topic titles that don't apply to the same tier as the H1.
|
||||
|
||||
The H1 tier badge should be the badge that applies to the lowest tier for the features on the page.
|
||||
|
|
@ -1731,10 +1731,19 @@ Or add the status by itself:
|
|||
|
||||
##### Inline tier badges
|
||||
|
||||
Do not add tier badges inline with other text, except for [API attributes](../restful_api_styleguide.md).
|
||||
Do not add tier badges inline with other text.
|
||||
The single source of truth for a feature should be the topic where the
|
||||
functionality is described.
|
||||
|
||||
If you need to mention a tier inline, write it in plain text. For example,
|
||||
for an API topic:
|
||||
|
||||
```markdown
|
||||
IDs of the users to assign the issue to. Ultimate only.
|
||||
```
|
||||
|
||||
For more examples, see the [REST API style guide](../restful_api_styleguide.md).
|
||||
|
||||
##### Administrator documentation tier badges
|
||||
|
||||
Topics that are only for instance administrators should be badged `<TIER> SELF`. Instance
|
||||
|
|
|
|||
|
|
@ -14,22 +14,22 @@ GitLab is creating AI-assisted features across our DevSecOps platform. These fea
|
|||
|
||||
| Goal | Feature | Tier/Offering/Status |
|
||||
|---|---|---|
|
||||
| Helps you discover or recall Git commands when and where you need them. | [Git suggestions](https://gitlab.com/gitlab-org/gitlab/-/issues/409636) | **(ULTIMATE SAAS EXPERIMENT)** |
|
||||
| Assists with quickly getting everyone up to speed on lengthy conversations to help ensure you are all on the same page. | [Discussion summary](#summarize-issue-discussions-with-discussion-summary) | **(ULTIMATE SAAS EXPERIMENT)** |
|
||||
| Generates issue descriptions. | [Issue description generation](#summarize-an-issue-with-issue-description-generation) | **(ULTIMATE SAAS EXPERIMENT)** |
|
||||
| Helps you write code more efficiently by viewing code suggestions as you type. <br><br><i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Watch overview](https://www.youtube.com/watch?v=hCAyCTacdAQ) | [Code Suggestions](project/repository/code_suggestions/index.md) | For SaaS: **(FREE)**<br><br> For self-managed: **(PREMIUM)** |
|
||||
| Automates repetitive tasks and helps catch bugs early. | [Test generation](gitlab_duo_chat.md#write-tests-in-the-ide) | **(ULTIMATE BETA)** |
|
||||
| Generates a description for the merge request based on the contents of the template. | [Merge request template population](project/merge_requests/ai_in_merge_requests.md#fill-in-merge-request-templates) | **(ULTIMATE SAAS EXPERIMENT)** |
|
||||
| Assists in creating faster and higher-quality reviews by automatically suggesting reviewers for your merge request. <br><br><i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Watch overview](https://www.youtube.com/watch?v=ivwZQgh4Rxw) | [Suggested Reviewers](project/merge_requests/reviews/index.md#gitlab-duo-suggested-reviewers) | **(ULTIMATE SAAS)** |
|
||||
| Efficiently communicates the impact of your merge request changes. | [Merge request summary](project/merge_requests/ai_in_merge_requests.md#summarize-merge-request-changes) | **(ULTIMATE SAAS EXPERIMENT)** |
|
||||
| Helps ease merge request handoff between authors and reviewers and help reviewers efficiently understand suggestions. | [Code review summary](project/merge_requests/ai_in_merge_requests.md#summarize-my-merge-request-review) | **(ULTIMATE SAAS EXPERIMENT)** |
|
||||
| Helps you remediate vulnerabilities more efficiently, boost your skills, and write more secure code. <br><br><i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Watch overview](https://www.youtube.com/watch?v=6sDf73QOav8) | [Vulnerability summary](application_security/vulnerabilities/index.md#explaining-a-vulnerability) | **(ULTIMATE SAAS BETA)** |
|
||||
| Generates a merge request containing the changes required to mitigate a vulnerability. | [Vulnerability resolution](application_security/vulnerabilities/index.md#explaining-a-vulnerability) | **(ULTIMATE SAAS EXPERIMENT)** |
|
||||
| Helps you understand code by explaining it in English language. <br><br><i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Watch overview](https://www.youtube.com/watch?v=1izKaLmmaCA) | [Code explanation](#explain-code-in-the-web-ui-with-code-explanation) | **(ULTIMATE SAAS EXPERIMENT)** |
|
||||
| Processes and generates text and code in a conversational manner. Helps you quickly identify useful information in large volumes of text in issues, epics, code, and GitLab documentation. | [GitLab Duo Chat](gitlab_duo_chat.md) | **(ULTIMATE ALL BETA)** |
|
||||
| Assists you in determining the root cause for a pipeline failure and failed CI/CD build. | [Root cause analysis](#root-cause-analysis) | **(ULTIMATE SAAS EXPERIMENT)** |
|
||||
| Assists you with predicting productivity metrics and identifying anomalies across your software development lifecycle. | [Value stream forecasting](#forecast-deployment-frequency-with-value-stream-forecasting) | **(ULTIMATE ALL EXPERIMENT)** |
|
||||
| Processes and responds to your questions about your application's usage data. | [Product Analytics](product_analytics/index.md) | **(ULTIMATE SAAS EXPERIMENT)** |
|
||||
| Helps you discover or recall Git commands when and where you need them. | [Git suggestions](https://gitlab.com/gitlab-org/gitlab/-/issues/409636) | **Tier:** Ultimate <br>**Offering:** SaaS <br>**Status:** Experiment |
|
||||
| Assists with quickly getting everyone up to speed on lengthy conversations to help ensure you are all on the same page. | [Discussion summary](#summarize-issue-discussions-with-discussion-summary) | **Tier:** Ultimate <br>**Offering:** SaaS <br>**Status:** Experiment |
|
||||
| Generates issue descriptions. | [Issue description generation](#summarize-an-issue-with-issue-description-generation) | **Tier:** Ultimate <br>**Offering:** SaaS <br>**Status:** Experiment |
|
||||
| Helps you write code more efficiently by viewing code suggestions as you type. <br><br><i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Watch overview](https://www.youtube.com/watch?v=hCAyCTacdAQ) | [Code Suggestions](project/repository/code_suggestions/index.md) | **Offering:** SaaS **Tier:** Free <br><br>**Offering:** Self-managed **Tier:** Premium and Ultimate |
|
||||
| Automates repetitive tasks and helps catch bugs early. | [Test generation](gitlab_duo_chat.md#write-tests-in-the-ide) | **Tier:** Ultimate <br>**Offering:** SaaS, self-managed <br>**Status:** Beta |
|
||||
| Generates a description for the merge request based on the contents of the template. | [Merge request template population](project/merge_requests/ai_in_merge_requests.md#fill-in-merge-request-templates) | **Tier:** Ultimate <br>**Offering:** SaaS <br>**Status:** Experiment |
|
||||
| Assists in creating faster and higher-quality reviews by automatically suggesting reviewers for your merge request. <br><br><i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Watch overview](https://www.youtube.com/watch?v=ivwZQgh4Rxw) | [Suggested Reviewers](project/merge_requests/reviews/index.md#gitlab-duo-suggested-reviewers) | **Tier:** Ultimate <br>**Offering:** SaaS |
|
||||
| Efficiently communicates the impact of your merge request changes. | [Merge request summary](project/merge_requests/ai_in_merge_requests.md#summarize-merge-request-changes) | **Tier:** Ultimate <br>**Offering:** SaaS <br>**Status:** Experiment |
|
||||
| Helps ease merge request handoff between authors and reviewers and help reviewers efficiently understand suggestions. | [Code review summary](project/merge_requests/ai_in_merge_requests.md#summarize-my-merge-request-review) | **Tier:** Ultimate <br>**Offering:** SaaS <br>**Status:** Experiment |
|
||||
| Helps you remediate vulnerabilities more efficiently, boost your skills, and write more secure code. <br><br><i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Watch overview](https://www.youtube.com/watch?v=6sDf73QOav8) | [Vulnerability summary](application_security/vulnerabilities/index.md#explaining-a-vulnerability) | **Tier:** Ultimate <br>**Offering:** SaaS <br>**Status:** Beta |
|
||||
| Generates a merge request containing the changes required to mitigate a vulnerability. | [Vulnerability resolution](application_security/vulnerabilities/index.md#explaining-a-vulnerability) | **Tier:** Ultimate <br>**Offering:** SaaS <br>**Status:** Experiment |
|
||||
| Helps you understand code by explaining it in English language. <br><br><i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Watch overview](https://www.youtube.com/watch?v=1izKaLmmaCA) | [Code explanation](#explain-code-in-the-web-ui-with-code-explanation) | **Tier:** Ultimate <br>**Offering:** SaaS <br>**Status:** Experiment |
|
||||
| Processes and generates text and code in a conversational manner. Helps you quickly identify useful information in large volumes of text in issues, epics, code, and GitLab documentation. | [GitLab Duo Chat](gitlab_duo_chat.md) | **Tier:** Ultimate <br>**Offering:** SaaS, self-managed<br>**Status:** Beta |
|
||||
| Assists you in determining the root cause for a pipeline failure and failed CI/CD build. | [Root cause analysis](#root-cause-analysis) | **Tier:** Ultimate <br>**Offering:** SaaS <br>**Status:** Experiment |
|
||||
| Assists you with predicting productivity metrics and identifying anomalies across your software development lifecycle. | [Value stream forecasting](#forecast-deployment-frequency-with-value-stream-forecasting) | **Tier:** Ultimate <br>**Offering:** SaaS, self-managed <br>**Status:** Experiment |
|
||||
| Processes and responds to your questions about your application's usage data. | [Product Analytics](product_analytics/index.md) | **Tier:** Ultimate <br>**Offering:** SaaS <br>**Status:** Experiment |
|
||||
|
||||
## Enable AI/ML features
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,12 @@ module BulkImports
|
|||
wiki = context.portable.wiki
|
||||
url = data[:url].sub("://", "://oauth2:#{context.configuration.access_token}@")
|
||||
|
||||
Gitlab::UrlBlocker.validate!(url, schemes: %w[http https], allow_local_network: allow_local_requests?, allow_localhost: allow_local_requests?)
|
||||
Gitlab::HTTP_V2::UrlBlocker.validate!(
|
||||
url,
|
||||
schemes: %w[http https],
|
||||
allow_local_network: allow_local_requests?,
|
||||
allow_localhost: allow_local_requests?,
|
||||
deny_all_requests_except_allowed: Gitlab::CurrentSettings.deny_all_requests_except_allowed?)
|
||||
|
||||
wiki.create_wiki_repository
|
||||
wiki.repository.fetch_as_mirror(url)
|
||||
|
|
|
|||
|
|
@ -21,7 +21,12 @@ module BulkImports
|
|||
url = url.sub("://", "://oauth2:#{context.configuration.access_token}@")
|
||||
project = context.portable
|
||||
|
||||
Gitlab::UrlBlocker.validate!(url, schemes: %w[http https], allow_local_network: allow_local_requests?, allow_localhost: allow_local_requests?)
|
||||
Gitlab::HTTP_V2::UrlBlocker.validate!(
|
||||
url,
|
||||
schemes: %w[http https],
|
||||
allow_local_network: allow_local_requests?,
|
||||
allow_localhost: allow_local_requests?,
|
||||
deny_all_requests_except_allowed: Gitlab::CurrentSettings.deny_all_requests_except_allowed?)
|
||||
|
||||
project.ensure_repository
|
||||
project.repository.fetch_as_mirror(url)
|
||||
|
|
|
|||
|
|
@ -53,10 +53,11 @@ module BulkImports
|
|||
end
|
||||
|
||||
def validate_url(url)
|
||||
Gitlab::UrlBlocker.validate!(
|
||||
Gitlab::HTTP_V2::UrlBlocker.validate!(
|
||||
url,
|
||||
allow_local_network: allow_local_requests?,
|
||||
allow_localhost: allow_local_requests?,
|
||||
deny_all_requests_except_allowed: Gitlab::CurrentSettings.deny_all_requests_except_allowed?,
|
||||
schemes: %w[http https]
|
||||
)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ module Gitlab
|
|||
MAX_TEXT_SIZE_LIMIT = 1_000_000
|
||||
|
||||
# Migrations before this version may have been removed
|
||||
MIN_SCHEMA_GITLAB_VERSION = '15.11'
|
||||
MIN_SCHEMA_GITLAB_VERSION = '16.7'
|
||||
|
||||
# Schema we store dynamically managed partitions in (e.g. for time partitioning)
|
||||
DYNAMIC_PARTITIONS_SCHEMA = :gitlab_partitions_dynamic
|
||||
|
|
|
|||
|
|
@ -232,7 +232,11 @@ module Gitlab
|
|||
url = Gitlab::CurrentSettings.current_application_settings.error_tracking_api_url ||
|
||||
'http://localhost:8080'
|
||||
|
||||
Gitlab::UrlBlocker.validate!(url, schemes: %w[http https], allow_localhost: true)
|
||||
Gitlab::HTTP_V2::UrlBlocker.validate!(
|
||||
url,
|
||||
schemes: %w[http https],
|
||||
allow_localhost: true,
|
||||
deny_all_requests_except_allowed: Gitlab::CurrentSettings.deny_all_requests_except_allowed?)
|
||||
|
||||
URI(url)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -58,11 +58,13 @@ module Gitlab
|
|||
end
|
||||
|
||||
def get_resolved_address
|
||||
validated_pull_url, host = Gitlab::UrlBlocker.validate!(gist.git_pull_url,
|
||||
schemes: Project::VALID_IMPORT_PROTOCOLS,
|
||||
ports: Project::VALID_IMPORT_PORTS,
|
||||
allow_localhost: allow_local_requests?,
|
||||
allow_local_network: allow_local_requests?)
|
||||
validated_pull_url, host = Gitlab::HTTP_V2::UrlBlocker.validate!(
|
||||
gist.git_pull_url,
|
||||
schemes: Project::VALID_IMPORT_PROTOCOLS,
|
||||
ports: Project::VALID_IMPORT_PORTS,
|
||||
allow_localhost: allow_local_requests?,
|
||||
allow_local_network: allow_local_requests?,
|
||||
deny_all_requests_except_allowed: Gitlab::CurrentSettings.deny_all_requests_except_allowed?)
|
||||
|
||||
host.present? ? validated_pull_url.host.to_s : ''
|
||||
end
|
||||
|
|
|
|||
|
|
@ -52,8 +52,6 @@ module Gitlab
|
|||
end
|
||||
|
||||
def has_attachments?(object)
|
||||
return true if Feature.disabled?(:github_importer_attachments, project, type: :gitlab_com_derisk)
|
||||
|
||||
object_representation(object).has_attachments?
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -161,7 +161,11 @@ module Gitlab
|
|||
def validate_url!
|
||||
return if Gitlab::CurrentSettings.allow_local_requests_from_web_hooks_and_services?
|
||||
|
||||
Gitlab::UrlBlocker.validate!(api_prefix, allow_local_network: false, schemes: %w[http https])
|
||||
Gitlab::HTTP_V2::UrlBlocker.validate!(
|
||||
api_prefix,
|
||||
allow_local_network: false,
|
||||
schemes: %w[http https],
|
||||
deny_all_requests_except_allowed: Gitlab::CurrentSettings.deny_all_requests_except_allowed?)
|
||||
end
|
||||
|
||||
def service_account_exists?(resource)
|
||||
|
|
|
|||
|
|
@ -8,11 +8,12 @@ module Gitlab
|
|||
end
|
||||
|
||||
def call(env)
|
||||
Gitlab::UrlBlocker.validate!(env[:url],
|
||||
Gitlab::HTTP_V2::UrlBlocker.validate!(env[:url],
|
||||
schemes: %w[http https],
|
||||
allow_localhost: allow_local_requests?,
|
||||
allow_local_network: allow_local_requests?,
|
||||
dns_rebind_protection: dns_rebind_protection?
|
||||
dns_rebind_protection: dns_rebind_protection?,
|
||||
deny_all_requests_except_allowed: Gitlab::CurrentSettings.deny_all_requests_except_allowed?
|
||||
)
|
||||
|
||||
@app.call(env)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ module ServicePing
|
|||
module ServicePingSettings
|
||||
extend self
|
||||
|
||||
def product_intelligence_enabled?
|
||||
def enabled_and_consented?
|
||||
enabled? && !User.single_user&.requires_usage_stats_consent?
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import Vuex from 'vuex';
|
|||
import getMRCodequalityAndSecurityReports from '~/diffs/components/graphql/get_mr_codequality_and_security_reports.query.graphql';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import setWindowLocation from 'helpers/set_window_location_helper';
|
||||
import { mockTracking } from 'helpers/tracking_helper';
|
||||
import { TEST_HOST } from 'spec/test_constants';
|
||||
|
||||
import App from '~/diffs/components/app.vue';
|
||||
|
|
@ -22,6 +23,7 @@ import CollapsedFilesWarning from '~/diffs/components/collapsed_files_warning.vu
|
|||
import HiddenFilesWarning from '~/diffs/components/hidden_files_warning.vue';
|
||||
|
||||
import eventHub from '~/diffs/event_hub';
|
||||
import notesEventHub from '~/notes/event_hub';
|
||||
import { EVT_DISCUSSIONS_ASSIGNED } from '~/diffs/constants';
|
||||
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
|
|
@ -888,4 +890,38 @@ describe('diffs/components/app', () => {
|
|||
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('draft comments', () => {
|
||||
let trackingSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
trackingSpy = mockTracking(undefined, window.document, jest.spyOn);
|
||||
});
|
||||
|
||||
describe('when adding a new comment to an existing review', () => {
|
||||
it('sends the correct tracking event', () => {
|
||||
createComponent({ shouldShow: true });
|
||||
notesEventHub.$emit('noteFormAddToReview', { name: 'noteFormAddToReview' });
|
||||
|
||||
expect(trackingSpy).toHaveBeenCalledWith(
|
||||
undefined,
|
||||
'merge_request_click_add_to_review_on_changes_tab',
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when adding a comment to a new review', () => {
|
||||
it('sends the correct tracking event', () => {
|
||||
createComponent({ shouldShow: true });
|
||||
notesEventHub.$emit('noteFormStartReview', { name: 'noteFormStartReview' });
|
||||
|
||||
expect(trackingSpy).toHaveBeenCalledWith(
|
||||
undefined,
|
||||
'merge_request_click_start_review_on_changes_tab',
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -785,11 +785,16 @@ describe('date addition/subtraction methods', () => {
|
|||
);
|
||||
});
|
||||
|
||||
// NOTE: 2024-02-29 is a leap day
|
||||
describe('nYearsAfter', () => {
|
||||
it.each`
|
||||
date | numberOfYears | expected
|
||||
${'2020-07-06'} | ${1} | ${'2021-07-06'}
|
||||
${'2020-07-06'} | ${15} | ${'2035-07-06'}
|
||||
${'2024-03-02'} | ${1} | ${'2025-03-02'}
|
||||
${'2024-03-01'} | ${1} | ${'2025-03-01'}
|
||||
${'2024-02-29'} | ${1} | ${'2025-02-28'}
|
||||
${'2024-02-28'} | ${1} | ${'2025-02-28'}
|
||||
`(
|
||||
'returns $expected for "$numberOfYears year(s) after $date"',
|
||||
({ date, numberOfYears, expected }) => {
|
||||
|
|
@ -805,6 +810,10 @@ describe('date addition/subtraction methods', () => {
|
|||
date | numberOfYears | expected
|
||||
${'2020-07-06'} | ${4} | ${'2016-07-06'}
|
||||
${'2020-07-06'} | ${1} | ${'2019-07-06'}
|
||||
${'2024-03-02'} | ${1} | ${'2023-03-02'}
|
||||
${'2024-03-01'} | ${1} | ${'2023-03-01'}
|
||||
${'2024-02-29'} | ${1} | ${'2023-02-28'}
|
||||
${'2024-02-28'} | ${1} | ${'2023-02-28'}
|
||||
`(
|
||||
'returns $expected for "$numberOfYears year(s) before $date"',
|
||||
({ date, numberOfYears, expected }) => {
|
||||
|
|
|
|||
|
|
@ -401,11 +401,28 @@ describe('issue_comment_form component', () => {
|
|||
let store;
|
||||
|
||||
beforeEach(() => {
|
||||
store = createStore();
|
||||
store = createStore({
|
||||
actions: {
|
||||
saveNote: jest.fn().mockResolvedValue(),
|
||||
},
|
||||
});
|
||||
store.registerModule('batchComments', batchComments());
|
||||
store.state.batchComments.drafts = [{ note: 'A' }];
|
||||
});
|
||||
|
||||
it('sends the event to indicate that a new draft comment has been added', () => {
|
||||
const note = 'some note text which enables actually adding a draft note';
|
||||
|
||||
jest.spyOn(eventHub, '$emit');
|
||||
mountComponent({ mountFunction: mount, initialData: { note }, store });
|
||||
|
||||
findAddToReviewButton().trigger('click');
|
||||
|
||||
expect(eventHub.$emit).toHaveBeenCalledWith('noteFormAddToReview', {
|
||||
name: 'noteFormAddToReview',
|
||||
});
|
||||
});
|
||||
|
||||
it('should save note draft when cmd+enter is pressed', async () => {
|
||||
mountComponent({ mountFunction: mount, store });
|
||||
jest.spyOn(wrapper.vm, 'handleSaveDraft');
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import MarkdownField from '~/vue_shared/components/markdown/field.vue';
|
|||
import CommentFieldLayout from '~/notes/components/comment_field_layout.vue';
|
||||
import { AT_WHO_ACTIVE_CLASS } from '~/gfm_auto_complete';
|
||||
import eventHub from '~/environments/event_hub';
|
||||
import notesEventHub from '~/notes/event_hub';
|
||||
import { mountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import { mockTracking } from 'helpers/tracking_helper';
|
||||
import { noteableDataMock, notesDataMock, discussionMock, note } from '../mock_data';
|
||||
|
|
@ -16,6 +17,7 @@ jest.mock('~/lib/utils/autosave');
|
|||
describe('issue_note_form component', () => {
|
||||
let store;
|
||||
let wrapper;
|
||||
let textarea;
|
||||
let props;
|
||||
let trackingSpy;
|
||||
|
||||
|
|
@ -39,10 +41,13 @@ describe('issue_note_form component', () => {
|
|||
},
|
||||
},
|
||||
});
|
||||
|
||||
textarea = wrapper.find('textarea');
|
||||
};
|
||||
|
||||
const findCancelButton = () => wrapper.findByTestId('cancel');
|
||||
const findCancelCommentButton = () => wrapper.findByTestId('cancelBatchCommentsEnabled');
|
||||
const findAddToStartReviewButton = () => wrapper.findByTestId('start-review-button');
|
||||
const findMarkdownField = () => wrapper.findComponent(MarkdownField);
|
||||
|
||||
beforeEach(() => {
|
||||
|
|
@ -112,14 +117,10 @@ describe('issue_note_form component', () => {
|
|||
});
|
||||
|
||||
it('should render text area with placeholder', () => {
|
||||
const textarea = wrapper.find('textarea');
|
||||
|
||||
expect(textarea.attributes('placeholder')).toBe('Write a comment or drag your files here…');
|
||||
});
|
||||
|
||||
it('should set data-supports-quick-actions to enable autocomplete', () => {
|
||||
const textarea = wrapper.find('textarea');
|
||||
|
||||
expect(textarea.attributes('data-supports-quick-actions')).toBe('true');
|
||||
});
|
||||
|
||||
|
|
@ -147,10 +148,7 @@ describe('issue_note_form component', () => {
|
|||
});
|
||||
|
||||
describe('keyboard events', () => {
|
||||
let textarea;
|
||||
|
||||
beforeEach(() => {
|
||||
textarea = wrapper.find('textarea');
|
||||
textarea.setValue('Foo');
|
||||
});
|
||||
|
||||
|
|
@ -213,7 +211,6 @@ describe('issue_note_form component', () => {
|
|||
it('should be possible to update the note', () => {
|
||||
createComponentWrapper();
|
||||
|
||||
const textarea = wrapper.find('textarea');
|
||||
textarea.setValue('Foo');
|
||||
const saveButton = wrapper.find('.js-vue-issue-save');
|
||||
saveButton.vm.$emit('click');
|
||||
|
|
@ -224,7 +221,6 @@ describe('issue_note_form component', () => {
|
|||
it('tracks event when save button is clicked', () => {
|
||||
createComponentWrapper();
|
||||
|
||||
const textarea = wrapper.find('textarea');
|
||||
textarea.setValue('Foo');
|
||||
const saveButton = wrapper.find('.js-vue-issue-save');
|
||||
saveButton.vm.$emit('click');
|
||||
|
|
@ -313,8 +309,6 @@ describe('issue_note_form component', () => {
|
|||
|
||||
describe('on enter', () => {
|
||||
it('should start review or add to review when cmd+enter is pressed', async () => {
|
||||
const textarea = wrapper.find('textarea');
|
||||
|
||||
textarea.setValue('Foo');
|
||||
textarea.trigger('keydown.enter', { metaKey: true });
|
||||
|
||||
|
|
@ -325,5 +319,40 @@ describe('issue_note_form component', () => {
|
|||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when adding a draft comment', () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(notesEventHub, '$emit');
|
||||
});
|
||||
|
||||
it('sends the event to indicate that a draft has been added to the review', () => {
|
||||
store.state.batchComments.drafts = [{ note: 'A' }];
|
||||
createComponentWrapper({
|
||||
isDraft: true,
|
||||
noteId: '',
|
||||
discussion: { ...discussionMock, for_commit: false },
|
||||
});
|
||||
|
||||
findAddToStartReviewButton().trigger('click');
|
||||
|
||||
expect(notesEventHub.$emit).toHaveBeenCalledWith('noteFormAddToReview', {
|
||||
name: 'noteFormAddToReview',
|
||||
});
|
||||
});
|
||||
|
||||
it('sends the event to indicate that a review has been started with the new draft', () => {
|
||||
createComponentWrapper({
|
||||
isDraft: true,
|
||||
noteId: '',
|
||||
discussion: { ...discussionMock, for_commit: false },
|
||||
});
|
||||
|
||||
findAddToStartReviewButton().trigger('click');
|
||||
|
||||
expect(notesEventHub.$emit).toHaveBeenCalledWith('noteFormStartReview', {
|
||||
name: 'noteFormStartReview',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import AxiosMockAdapter from 'axios-mock-adapter';
|
|||
import $ from 'jquery';
|
||||
import { nextTick } from 'vue';
|
||||
import setWindowLocation from 'helpers/set_window_location_helper';
|
||||
import { mockTracking } from 'helpers/tracking_helper';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import DraftNote from '~/batch_comments/components/draft_note.vue';
|
||||
import batchComments from '~/batch_comments/stores/modules/batch_comments';
|
||||
|
|
@ -10,6 +11,7 @@ import axios from '~/lib/utils/axios_utils';
|
|||
import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
|
||||
import { getLocationHash } from '~/lib/utils/url_utility';
|
||||
import * as urlUtility from '~/lib/utils/url_utility';
|
||||
import notesEventHub from '~/notes/event_hub';
|
||||
import CommentForm from '~/notes/components/comment_form.vue';
|
||||
import NotesApp from '~/notes/components/notes_app.vue';
|
||||
import NotesActivityHeader from '~/notes/components/notes_activity_header.vue';
|
||||
|
|
@ -453,4 +455,39 @@ describe('note_app', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('draft comments', () => {
|
||||
let trackingSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
window.mrTabs = { eventHub: notesEventHub };
|
||||
axiosMock.onAny().reply(mockData.getIndividualNoteResponse);
|
||||
trackingSpy = mockTracking(undefined, window.document, jest.spyOn);
|
||||
wrapper = mountComponent();
|
||||
});
|
||||
|
||||
describe('when adding a new comment to an existing review', () => {
|
||||
it('sends the correct tracking event', () => {
|
||||
notesEventHub.$emit('noteFormAddToReview', { name: 'noteFormAddToReview' });
|
||||
|
||||
expect(trackingSpy).toHaveBeenCalledWith(
|
||||
undefined,
|
||||
'merge_request_click_add_to_review_on_overview_tab',
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when adding a comment to a new review', () => {
|
||||
it('sends the correct tracking event', () => {
|
||||
notesEventHub.$emit('noteFormStartReview', { name: 'noteFormStartReview' });
|
||||
|
||||
expect(trackingSpy).toHaveBeenCalledWith(
|
||||
undefined,
|
||||
'merge_request_click_start_review_on_overview_tab',
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
import { GlLink } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import StorageTypeHelpLink from '~/usage_quotas/storage/components/storage_type_help_link.vue';
|
||||
import { storageTypeHelpPaths } from '~/usage_quotas/storage/constants';
|
||||
|
||||
let wrapper;
|
||||
|
||||
const createComponent = ({ props = {} } = {}) => {
|
||||
wrapper = shallowMount(StorageTypeHelpLink, {
|
||||
propsData: {
|
||||
helpLinks: storageTypeHelpPaths,
|
||||
...props,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const findLink = () => wrapper.findComponent(GlLink);
|
||||
|
||||
describe('StorageTypeHelpLink', () => {
|
||||
describe('Storage type w/ link', () => {
|
||||
describe.each(Object.entries(storageTypeHelpPaths))('%s', (storageType, url) => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
props: {
|
||||
storageType,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('will have proper href', () => {
|
||||
expect(findLink().attributes('href')).toBe(url);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Storage type w/o help link', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
props: {
|
||||
storageType: 'Yellow Submarine',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('will not have a href', () => {
|
||||
expect(findLink().attributes('href')).toBe(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import mockGetProjectStorageStatisticsGraphQLResponse from 'test_fixtures/graphql/usage_quotas/storage/project_storage.query.graphql.json';
|
||||
import mockGetNamespaceStorageGraphQLResponse from 'test_fixtures/graphql/usage_quotas/storage/namespace_storage.query.graphql.json';
|
||||
import mockGetProjectListStorageGraphQLResponse from 'test_fixtures/graphql/usage_quotas/storage/project_list_storage.query.graphql.json';
|
||||
import { storageTypeHelpPaths } from '~/usage_quotas/storage/constants';
|
||||
|
||||
export { mockGetProjectStorageStatisticsGraphQLResponse };
|
||||
export { mockGetNamespaceStorageGraphQLResponse };
|
||||
|
|
@ -15,4 +16,6 @@ export const defaultProjectProvideValues = {
|
|||
export const defaultNamespaceProvideValues = {
|
||||
userNamespace: false,
|
||||
namespaceId: '42',
|
||||
defaultPerPage: 20,
|
||||
helpLinks: storageTypeHelpPaths,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -167,13 +167,16 @@ RSpec.describe Gitlab::GithubGistsImport::Importer::GistImporter, feature_catego
|
|||
before do
|
||||
allow(::Gitlab::CurrentSettings)
|
||||
.to receive(:allow_local_requests_from_web_hooks_and_services?).and_return(true)
|
||||
allow(::Gitlab::CurrentSettings)
|
||||
.to receive(:deny_all_requests_except_allowed?).and_return(true)
|
||||
end
|
||||
|
||||
it 'raises error' do
|
||||
expect(Gitlab::UrlBlocker)
|
||||
expect(Gitlab::HTTP_V2::UrlBlocker)
|
||||
.to receive(:validate!)
|
||||
.with(url, ports: [80, 443], schemes: %w[http https git],
|
||||
allow_localhost: true, allow_local_network: true)
|
||||
allow_localhost: true, allow_local_network: true,
|
||||
deny_all_requests_except_allowed: true)
|
||||
.and_raise(Gitlab::HTTP_V2::UrlBlocker::BlockedUrlError)
|
||||
|
||||
expect { subject.execute }.to raise_error(Gitlab::HTTP_V2::UrlBlocker::BlockedUrlError)
|
||||
|
|
@ -184,13 +187,16 @@ RSpec.describe Gitlab::GithubGistsImport::Importer::GistImporter, feature_catego
|
|||
before do
|
||||
allow(::Gitlab::CurrentSettings)
|
||||
.to receive(:allow_local_requests_from_web_hooks_and_services?).and_return(false)
|
||||
allow(::Gitlab::CurrentSettings)
|
||||
.to receive(:deny_all_requests_except_allowed?).and_return(true)
|
||||
end
|
||||
|
||||
it 'raises error' do
|
||||
expect(Gitlab::UrlBlocker)
|
||||
expect(Gitlab::HTTP_V2::UrlBlocker)
|
||||
.to receive(:validate!)
|
||||
.with(url, ports: [80, 443], schemes: %w[http https git],
|
||||
allow_localhost: false, allow_local_network: false)
|
||||
allow_localhost: false, allow_local_network: false,
|
||||
deny_all_requests_except_allowed: true)
|
||||
.and_raise(Gitlab::HTTP_V2::UrlBlocker::BlockedUrlError)
|
||||
|
||||
expect { subject.execute }.to raise_error(Gitlab::HTTP_V2::UrlBlocker::BlockedUrlError)
|
||||
|
|
|
|||
|
|
@ -41,20 +41,6 @@ RSpec.describe Gitlab::GithubImport::Importer::Attachments::IssuesImporter, feat
|
|||
importer.sequential_import
|
||||
end
|
||||
|
||||
context 'when flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(github_importer_attachments: false)
|
||||
end
|
||||
|
||||
it 'executes importer for both issues' do
|
||||
expect_next_instances_of(Gitlab::GithubImport::Importer::NoteAttachmentsImporter, 2) do |importer|
|
||||
expect(importer).to receive(:execute)
|
||||
end
|
||||
|
||||
importer.sequential_import
|
||||
end
|
||||
end
|
||||
|
||||
context 'when issue has already been processed' do
|
||||
before do
|
||||
importer.mark_as_imported(issue_with_attachment)
|
||||
|
|
|
|||
|
|
@ -42,20 +42,6 @@ RSpec.describe Gitlab::GithubImport::Importer::Attachments::MergeRequestsImporte
|
|||
importer.sequential_import
|
||||
end
|
||||
|
||||
context 'when flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(github_importer_attachments: false)
|
||||
end
|
||||
|
||||
it 'executes importer for both merge requests' do
|
||||
expect_next_instances_of(Gitlab::GithubImport::Importer::NoteAttachmentsImporter, 2) do |importer|
|
||||
expect(importer).to receive(:execute)
|
||||
end
|
||||
|
||||
importer.sequential_import
|
||||
end
|
||||
end
|
||||
|
||||
context 'when merge request has already been processed' do
|
||||
before do
|
||||
importer.mark_as_imported(mr_with_attachment)
|
||||
|
|
|
|||
|
|
@ -52,20 +52,6 @@ RSpec.describe Gitlab::GithubImport::Importer::Attachments::NotesImporter, featu
|
|||
importer.sequential_import
|
||||
end
|
||||
|
||||
context 'when flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(github_importer_attachments: false)
|
||||
end
|
||||
|
||||
it 'executes importer for both user notes' do
|
||||
expect_next_instances_of(Gitlab::GithubImport::Importer::NoteAttachmentsImporter, 2) do |importer|
|
||||
expect(importer).to receive(:execute)
|
||||
end
|
||||
|
||||
importer.sequential_import
|
||||
end
|
||||
end
|
||||
|
||||
context 'when note has already been processed' do
|
||||
before do
|
||||
importer.mark_as_imported(note_with_attachment)
|
||||
|
|
|
|||
|
|
@ -41,20 +41,6 @@ RSpec.describe Gitlab::GithubImport::Importer::Attachments::ReleasesImporter, fe
|
|||
importer.sequential_import
|
||||
end
|
||||
|
||||
context 'when flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(github_importer_attachments: false)
|
||||
end
|
||||
|
||||
it 'executes importer for both releases' do
|
||||
expect_next_instances_of(Gitlab::GithubImport::Importer::NoteAttachmentsImporter, 2) do |importer|
|
||||
expect(importer).to receive(:execute)
|
||||
end
|
||||
|
||||
importer.sequential_import
|
||||
end
|
||||
end
|
||||
|
||||
context 'when release has already been processed' do
|
||||
before do
|
||||
importer.mark_as_imported(release_with_attachment)
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ require 'spec_helper'
|
|||
RSpec.describe ServicePing::ServicePingSettings do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
describe '#product_intelligence_enabled?' do
|
||||
where(:usage_ping_enabled, :requires_usage_stats_consent, :expected_product_intelligence_enabled) do
|
||||
describe '#enabled_and_consented?' do
|
||||
where(:usage_ping_enabled, :requires_usage_stats_consent, :expected_enabled_and_consented) do
|
||||
# Usage ping enabled
|
||||
true | false | true
|
||||
true | true | false
|
||||
|
|
@ -23,8 +23,8 @@ RSpec.describe ServicePing::ServicePingSettings do
|
|||
stub_config_setting(usage_ping_enabled: usage_ping_enabled)
|
||||
end
|
||||
|
||||
it 'has the correct product_intelligence_enabled?' do
|
||||
expect(described_class.product_intelligence_enabled?).to eq(expected_product_intelligence_enabled)
|
||||
it 'has the correct enabled_and_consented?' do
|
||||
expect(described_class.enabled_and_consented?).to eq(expected_enabled_and_consented)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -104,20 +104,20 @@ RSpec.describe ServicePing::SubmitService, feature_category: :service_ping do
|
|||
it_behaves_like 'does not run'
|
||||
end
|
||||
|
||||
context 'when product_intelligence_enabled is false' do
|
||||
context 'when enabled_and_consented is false' do
|
||||
before do
|
||||
allow(ServicePing::ServicePingSettings).to receive(:product_intelligence_enabled?).and_return(false)
|
||||
allow(ServicePing::ServicePingSettings).to receive(:enabled_and_consented?).and_return(false)
|
||||
end
|
||||
|
||||
it_behaves_like 'does not run'
|
||||
end
|
||||
|
||||
context 'when product_intelligence_enabled is true' do
|
||||
context 'when enabled_and_consented is true' do
|
||||
before do
|
||||
stub_usage_data_connections
|
||||
stub_database_flavor_check
|
||||
|
||||
allow(ServicePing::ServicePingSettings).to receive(:product_intelligence_enabled?).and_return(true)
|
||||
allow(ServicePing::ServicePingSettings).to receive(:enabled_and_consented?).and_return(true)
|
||||
end
|
||||
|
||||
it 'submits a service ping payload without errors', :aggregate_failures do
|
||||
|
|
|
|||
Loading…
Reference in New Issue