Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
df9e161ad4
commit
f42c4be1c0
|
@ -2,8 +2,6 @@ import { DEFAULT_PER_PAGE } from '~/api';
|
|||
import axios from '../lib/utils/axios_utils';
|
||||
import { buildApiUrl } from './api_utils';
|
||||
|
||||
export * from './alert_management_alerts_api';
|
||||
|
||||
const PROJECTS_PATH = '/api/:version/projects.json';
|
||||
const PROJECT_IMPORT_MEMBERS_PATH = '/api/:version/projects/:id/import_project_members/:project_id';
|
||||
const PROJECT_REPOSITORY_SIZE_PATH = '/api/:version/projects/:id/repository_size';
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<script>
|
||||
import { GlLoadingIcon } from '@gitlab/ui';
|
||||
import { throttle } from 'lodash';
|
||||
import { isLoggedIn } from '~/lib/utils/common_utils';
|
||||
import DesignOverlay from './design_overlay.vue';
|
||||
|
@ -10,6 +11,7 @@ export default {
|
|||
components: {
|
||||
DesignImage,
|
||||
DesignOverlay,
|
||||
GlLoadingIcon,
|
||||
},
|
||||
props: {
|
||||
image: {
|
||||
|
@ -40,6 +42,10 @@ export default {
|
|||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
isLoading: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -299,7 +305,12 @@ export default {
|
|||
@touchend="onPresentationMouseup"
|
||||
@touchcancel="onPresentationMouseup"
|
||||
>
|
||||
<div class="gl-h-full gl-w-full gl-display-flex gl-align-items-center gl-relative">
|
||||
<gl-loading-icon
|
||||
v-if="isLoading"
|
||||
size="xl"
|
||||
class="gl-display-flex gl-h-full gl-align-items-center"
|
||||
/>
|
||||
<div v-else class="gl-h-full gl-w-full gl-display-flex gl-align-items-center gl-relative">
|
||||
<design-image
|
||||
v-if="image"
|
||||
:image="image"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { GlCollapse, GlButton, GlPopover } from '@gitlab/ui';
|
||||
import { GlCollapse, GlButton, GlPopover, GlSkeletonLoader } from '@gitlab/ui';
|
||||
import { getCookie, setCookie, parseBoolean, isLoggedIn } from '~/lib/utils/common_utils';
|
||||
|
||||
import { s__ } from '~/locale';
|
||||
|
@ -20,6 +20,7 @@ export default {
|
|||
GlCollapse,
|
||||
GlButton,
|
||||
GlPopover,
|
||||
GlSkeletonLoader,
|
||||
DesignTodoButton,
|
||||
},
|
||||
mixins: [glFeatureFlagsMixin()],
|
||||
|
@ -50,6 +51,10 @@ export default {
|
|||
type: String,
|
||||
required: true,
|
||||
},
|
||||
isLoading: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -65,11 +70,11 @@ export default {
|
|||
issue() {
|
||||
return {
|
||||
...this.design.issue,
|
||||
webPath: this.design.issue.webPath.substr(1),
|
||||
webPath: this.design.issue?.webPath.substr(1),
|
||||
};
|
||||
},
|
||||
discussionParticipants() {
|
||||
return extractParticipants(this.issue.participants.nodes);
|
||||
return extractParticipants(this.issue.participants?.nodes || []);
|
||||
},
|
||||
resolvedDiscussions() {
|
||||
return this.discussions.filter((discussion) => discussion.resolved);
|
||||
|
@ -142,91 +147,94 @@ export default {
|
|||
:show-participant-label="false"
|
||||
class="gl-mb-4"
|
||||
/>
|
||||
<h2
|
||||
v-if="isLoggedIn && unresolvedDiscussions.length === 0"
|
||||
class="new-discussion-disclaimer gl-font-base gl-m-0 gl-mb-4"
|
||||
data-testid="new-discussion-disclaimer"
|
||||
>
|
||||
{{ s__("DesignManagement|Click the image where you'd like to start a new discussion") }}
|
||||
</h2>
|
||||
<design-note-signed-out
|
||||
v-if="!isLoggedIn"
|
||||
class="gl-mb-4"
|
||||
:register-path="registerPath"
|
||||
:sign-in-path="signInPath"
|
||||
:is-add-discussion="true"
|
||||
/>
|
||||
<design-discussion
|
||||
v-for="discussion in unresolvedDiscussions"
|
||||
:key="discussion.id"
|
||||
:discussion="discussion"
|
||||
:design-id="$route.params.id"
|
||||
:noteable-id="design.id"
|
||||
:markdown-preview-path="markdownPreviewPath"
|
||||
:register-path="registerPath"
|
||||
:sign-in-path="signInPath"
|
||||
:resolved-discussions-expanded="resolvedDiscussionsExpanded"
|
||||
:discussion-with-open-form="discussionWithOpenForm"
|
||||
data-testid="unresolved-discussion"
|
||||
@create-note-error="$emit('onDesignDiscussionError', $event)"
|
||||
@update-note-error="$emit('updateNoteError', $event)"
|
||||
@resolve-discussion-error="$emit('resolveDiscussionError', $event)"
|
||||
@click.native.stop="updateActiveDiscussion(discussion.notes[0].id)"
|
||||
@open-form="updateDiscussionWithOpenForm"
|
||||
/>
|
||||
<template v-if="resolvedDiscussions.length > 0">
|
||||
<gl-button
|
||||
id="resolved-comments"
|
||||
ref="resolvedComments"
|
||||
data-testid="resolved-comments"
|
||||
:icon="resolvedCommentsToggleIcon"
|
||||
variant="link"
|
||||
class="link-inherit-color gl-text-body gl-text-decoration-none gl-font-weight-bold gl-mb-4"
|
||||
@click="$emit('toggleResolvedComments')"
|
||||
>{{ $options.resolveCommentsToggleText }} ({{ resolvedDiscussions.length }})
|
||||
</gl-button>
|
||||
<gl-popover
|
||||
v-if="!isResolvedCommentsPopoverHidden"
|
||||
:show="!isResolvedCommentsPopoverHidden"
|
||||
target="resolved-comments"
|
||||
container="popovercontainer"
|
||||
placement="top"
|
||||
:title="s__('DesignManagement|Resolved Comments')"
|
||||
<gl-skeleton-loader v-if="isLoading" />
|
||||
<template v-else>
|
||||
<h2
|
||||
v-if="isLoggedIn && unresolvedDiscussions.length === 0"
|
||||
class="new-discussion-disclaimer gl-font-base gl-m-0 gl-mb-4"
|
||||
data-testid="new-discussion-disclaimer"
|
||||
>
|
||||
<p>
|
||||
{{
|
||||
s__(
|
||||
'DesignManagement|Comments you resolve can be viewed and unresolved by going to the "Resolved Comments" section below',
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<a
|
||||
href="https://docs.gitlab.com/ee/user/project/issues/design_management.html#resolve-design-threads"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>{{ s__('DesignManagement|Learn more about resolving comments') }}</a
|
||||
{{ s__("DesignManagement|Click the image where you'd like to start a new discussion") }}
|
||||
</h2>
|
||||
<design-note-signed-out
|
||||
v-if="!isLoggedIn"
|
||||
class="gl-mb-4"
|
||||
:register-path="registerPath"
|
||||
:sign-in-path="signInPath"
|
||||
:is-add-discussion="true"
|
||||
/>
|
||||
<design-discussion
|
||||
v-for="discussion in unresolvedDiscussions"
|
||||
:key="discussion.id"
|
||||
:discussion="discussion"
|
||||
:design-id="$route.params.id"
|
||||
:noteable-id="design.id"
|
||||
:markdown-preview-path="markdownPreviewPath"
|
||||
:register-path="registerPath"
|
||||
:sign-in-path="signInPath"
|
||||
:resolved-discussions-expanded="resolvedDiscussionsExpanded"
|
||||
:discussion-with-open-form="discussionWithOpenForm"
|
||||
data-testid="unresolved-discussion"
|
||||
@create-note-error="$emit('onDesignDiscussionError', $event)"
|
||||
@update-note-error="$emit('updateNoteError', $event)"
|
||||
@resolve-discussion-error="$emit('resolveDiscussionError', $event)"
|
||||
@click.native.stop="updateActiveDiscussion(discussion.notes[0].id)"
|
||||
@open-form="updateDiscussionWithOpenForm"
|
||||
/>
|
||||
<template v-if="resolvedDiscussions.length > 0">
|
||||
<gl-button
|
||||
id="resolved-comments"
|
||||
ref="resolvedComments"
|
||||
data-testid="resolved-comments"
|
||||
:icon="resolvedCommentsToggleIcon"
|
||||
variant="link"
|
||||
class="link-inherit-color gl-text-body gl-text-decoration-none gl-font-weight-bold gl-mb-4"
|
||||
@click="$emit('toggleResolvedComments')"
|
||||
>{{ $options.resolveCommentsToggleText }} ({{ resolvedDiscussions.length }})
|
||||
</gl-button>
|
||||
<gl-popover
|
||||
v-if="!isResolvedCommentsPopoverHidden"
|
||||
:show="!isResolvedCommentsPopoverHidden"
|
||||
target="resolved-comments"
|
||||
container="popovercontainer"
|
||||
placement="top"
|
||||
:title="s__('DesignManagement|Resolved Comments')"
|
||||
>
|
||||
</gl-popover>
|
||||
<gl-collapse :visible="resolvedDiscussionsExpanded" class="gl-mt-3">
|
||||
<design-discussion
|
||||
v-for="discussion in resolvedDiscussions"
|
||||
:key="discussion.id"
|
||||
:discussion="discussion"
|
||||
:design-id="$route.params.id"
|
||||
:noteable-id="design.id"
|
||||
:markdown-preview-path="markdownPreviewPath"
|
||||
:register-path="registerPath"
|
||||
:sign-in-path="signInPath"
|
||||
:resolved-discussions-expanded="resolvedDiscussionsExpanded"
|
||||
:discussion-with-open-form="discussionWithOpenForm"
|
||||
data-testid="resolved-discussion"
|
||||
@error="$emit('onDesignDiscussionError', $event)"
|
||||
@updateNoteError="$emit('updateNoteError', $event)"
|
||||
@open-form="updateDiscussionWithOpenForm"
|
||||
@click.native.stop="updateActiveDiscussion(discussion.notes[0].id)"
|
||||
/>
|
||||
</gl-collapse>
|
||||
<p>
|
||||
{{
|
||||
s__(
|
||||
'DesignManagement|Comments you resolve can be viewed and unresolved by going to the "Resolved Comments" section below',
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<a
|
||||
href="https://docs.gitlab.com/ee/user/project/issues/design_management.html#resolve-design-threads"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>{{ s__('DesignManagement|Learn more about resolving comments') }}</a
|
||||
>
|
||||
</gl-popover>
|
||||
<gl-collapse :visible="resolvedDiscussionsExpanded" class="gl-mt-3">
|
||||
<design-discussion
|
||||
v-for="discussion in resolvedDiscussions"
|
||||
:key="discussion.id"
|
||||
:discussion="discussion"
|
||||
:design-id="$route.params.id"
|
||||
:noteable-id="design.id"
|
||||
:markdown-preview-path="markdownPreviewPath"
|
||||
:register-path="registerPath"
|
||||
:sign-in-path="signInPath"
|
||||
:resolved-discussions-expanded="resolvedDiscussionsExpanded"
|
||||
:discussion-with-open-form="discussionWithOpenForm"
|
||||
data-testid="resolved-discussion"
|
||||
@error="$emit('onDesignDiscussionError', $event)"
|
||||
@updateNoteError="$emit('updateNoteError', $event)"
|
||||
@open-form="updateDiscussionWithOpenForm"
|
||||
@click.native.stop="updateActiveDiscussion(discussion.notes[0].id)"
|
||||
/>
|
||||
</gl-collapse>
|
||||
</template>
|
||||
<slot name="reply-form"></slot>
|
||||
</template>
|
||||
<slot name="reply-form"></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { GlButton, GlIcon, GlTooltipDirective } from '@gitlab/ui';
|
||||
import { GlButton, GlIcon, GlTooltipDirective, GlSkeletonLoader } from '@gitlab/ui';
|
||||
import permissionsQuery from 'shared_queries/design_management/design_permissions.query.graphql';
|
||||
import { __, s__, sprintf } from '~/locale';
|
||||
import timeagoMixin from '~/vue_shared/mixins/timeago';
|
||||
|
@ -14,6 +14,7 @@ export default {
|
|||
components: {
|
||||
GlButton,
|
||||
GlIcon,
|
||||
GlSkeletonLoader,
|
||||
DesignNavigation,
|
||||
DeleteButton,
|
||||
},
|
||||
|
@ -61,6 +62,10 @@ export default {
|
|||
type: String,
|
||||
required: true,
|
||||
},
|
||||
isLoading: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -113,7 +118,8 @@ export default {
|
|||
<gl-icon name="close" />
|
||||
</router-link>
|
||||
<div class="gl-overflow-hidden gl-display-flex gl-align-items-center">
|
||||
<h2 class="gl-m-0 str-truncated-100 gl-font-base">{{ filename }}</h2>
|
||||
<gl-skeleton-loader v-if="isLoading" :lines="1" />
|
||||
<h2 v-else class="gl-m-0 str-truncated-100 gl-font-base">{{ filename }}</h2>
|
||||
<small v-if="updatedAt" class="gl-text-gray-500">{{ updatedText }}</small>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { GlLoadingIcon, GlAlert } from '@gitlab/ui';
|
||||
import { GlAlert } from '@gitlab/ui';
|
||||
import { isNull } from 'lodash';
|
||||
import Mousetrap from 'mousetrap';
|
||||
import { ApolloMutation } from 'vue-apollo';
|
||||
|
@ -56,7 +56,6 @@ export default {
|
|||
DesignScaler,
|
||||
DesignDestroyer,
|
||||
Toolbar,
|
||||
GlLoadingIcon,
|
||||
GlAlert,
|
||||
DesignSidebar,
|
||||
},
|
||||
|
@ -118,10 +117,8 @@ export default {
|
|||
},
|
||||
},
|
||||
computed: {
|
||||
isFirstLoading() {
|
||||
// We only want to show spinner on initial design load (when opened from a deep link to design)
|
||||
// If we already have cached a design, loading shouldn't be indicated to user
|
||||
return this.$apollo.queries.design.loading && !this.design.filename;
|
||||
isLoading() {
|
||||
return this.$apollo.queries.design.loading;
|
||||
},
|
||||
discussions() {
|
||||
if (!this.design.discussions) {
|
||||
|
@ -343,88 +340,88 @@ export default {
|
|||
<div
|
||||
class="design-detail js-design-detail fixed-top gl-w-full gl-bottom-0 gl-display-flex gl-justify-content-center gl-flex-direction-column gl-lg-flex-direction-row"
|
||||
>
|
||||
<gl-loading-icon v-if="isFirstLoading" size="xl" class="gl-align-self-center" />
|
||||
<template v-else>
|
||||
<div
|
||||
class="gl-display-flex gl-overflow-hidden gl-flex-grow-1 gl-flex-direction-column gl-relative"
|
||||
<div
|
||||
class="gl-display-flex gl-overflow-hidden gl-flex-grow-1 gl-flex-direction-column gl-relative"
|
||||
>
|
||||
<design-destroyer
|
||||
:filenames="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [
|
||||
design.filename,
|
||||
] /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
|
||||
:project-path="projectPath"
|
||||
:iid="issueIid"
|
||||
@done="$router.push({ name: $options.DESIGNS_ROUTE_NAME })"
|
||||
@error="onDesignDeleteError"
|
||||
>
|
||||
<design-destroyer
|
||||
:filenames="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [
|
||||
design.filename,
|
||||
] /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
|
||||
:project-path="projectPath"
|
||||
:iid="issueIid"
|
||||
@done="$router.push({ name: $options.DESIGNS_ROUTE_NAME })"
|
||||
@error="onDesignDeleteError"
|
||||
>
|
||||
<template #default="{ mutate, loading }">
|
||||
<toolbar
|
||||
:id="id"
|
||||
:is-deleting="loading"
|
||||
:is-latest-version="isLatestVersion"
|
||||
v-bind="design"
|
||||
@delete="mutate"
|
||||
/>
|
||||
</template>
|
||||
</design-destroyer>
|
||||
<template #default="{ mutate, loading }">
|
||||
<toolbar
|
||||
:id="id"
|
||||
:is-deleting="loading"
|
||||
:is-latest-version="isLatestVersion"
|
||||
:is-loading="isLoading"
|
||||
v-bind="design"
|
||||
@delete="mutate"
|
||||
/>
|
||||
</template>
|
||||
</design-destroyer>
|
||||
|
||||
<div v-if="errorMessage" class="gl-p-5">
|
||||
<gl-alert variant="danger" @dismiss="errorMessage = null">
|
||||
{{ errorMessage }}
|
||||
</gl-alert>
|
||||
</div>
|
||||
<design-presentation
|
||||
:image="design.image"
|
||||
:image-name="design.filename"
|
||||
:discussions="discussions"
|
||||
:is-annotating="isAnnotating"
|
||||
:scale="scale"
|
||||
:resolved-discussions-expanded="resolvedDiscussionsExpanded"
|
||||
@openCommentForm="openCommentForm"
|
||||
@closeCommentForm="closeCommentForm"
|
||||
@moveNote="onMoveNote"
|
||||
@setMaxScale="setMaxScale"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="design-scaler-wrapper gl-absolute gl-mb-6 gl-display-flex gl-justify-content-center gl-align-items-center"
|
||||
>
|
||||
<design-scaler :max-scale="maxScale" @scale="scale = $event" />
|
||||
</div>
|
||||
<div v-if="errorMessage" class="gl-p-5">
|
||||
<gl-alert variant="danger" @dismiss="errorMessage = null">
|
||||
{{ errorMessage }}
|
||||
</gl-alert>
|
||||
</div>
|
||||
<design-sidebar
|
||||
:design="design"
|
||||
<design-presentation
|
||||
:image="design.image"
|
||||
:image-name="design.filename"
|
||||
:discussions="discussions"
|
||||
:is-annotating="isAnnotating"
|
||||
:scale="scale"
|
||||
:resolved-discussions-expanded="resolvedDiscussionsExpanded"
|
||||
:markdown-preview-path="markdownPreviewPath"
|
||||
@onDesignDiscussionError="onDesignDiscussionError"
|
||||
@onCreateImageDiffNoteError="onCreateImageDiffNoteError"
|
||||
@updateNoteError="onUpdateNoteError"
|
||||
@resolveDiscussionError="onResolveDiscussionError"
|
||||
@toggleResolvedComments="toggleResolvedComments"
|
||||
@todoError="onTodoError"
|
||||
:is-loading="isLoading"
|
||||
@openCommentForm="openCommentForm"
|
||||
@closeCommentForm="closeCommentForm"
|
||||
@moveNote="onMoveNote"
|
||||
@setMaxScale="setMaxScale"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="design-scaler-wrapper gl-absolute gl-mb-6 gl-display-flex gl-justify-content-center gl-align-items-center"
|
||||
>
|
||||
<template #reply-form>
|
||||
<apollo-mutation
|
||||
v-if="isAnnotating"
|
||||
#default="{ mutate, loading }"
|
||||
:mutation="$options.createImageDiffNoteMutation"
|
||||
:variables="{
|
||||
input: mutationPayload,
|
||||
}"
|
||||
:update="addImageDiffNoteToStore"
|
||||
@done="closeCommentForm"
|
||||
@error="onCreateImageDiffNoteError"
|
||||
>
|
||||
<design-reply-form
|
||||
ref="newDiscussionForm"
|
||||
v-model="comment"
|
||||
:is-saving="loading"
|
||||
:markdown-preview-path="markdownPreviewPath"
|
||||
@submit-form="mutate"
|
||||
@cancel-form="closeCommentForm"
|
||||
/> </apollo-mutation
|
||||
></template>
|
||||
</design-sidebar>
|
||||
</template>
|
||||
<design-scaler :max-scale="maxScale" @scale="scale = $event" />
|
||||
</div>
|
||||
</div>
|
||||
<design-sidebar
|
||||
:design="design"
|
||||
:resolved-discussions-expanded="resolvedDiscussionsExpanded"
|
||||
:markdown-preview-path="markdownPreviewPath"
|
||||
:is-loading="isLoading"
|
||||
@onDesignDiscussionError="onDesignDiscussionError"
|
||||
@onCreateImageDiffNoteError="onCreateImageDiffNoteError"
|
||||
@updateNoteError="onUpdateNoteError"
|
||||
@resolveDiscussionError="onResolveDiscussionError"
|
||||
@toggleResolvedComments="toggleResolvedComments"
|
||||
@todoError="onTodoError"
|
||||
>
|
||||
<template #reply-form>
|
||||
<apollo-mutation
|
||||
v-if="isAnnotating"
|
||||
#default="{ mutate, loading }"
|
||||
:mutation="$options.createImageDiffNoteMutation"
|
||||
:variables="{
|
||||
input: mutationPayload,
|
||||
}"
|
||||
:update="addImageDiffNoteToStore"
|
||||
@done="closeCommentForm"
|
||||
@error="onCreateImageDiffNoteError"
|
||||
>
|
||||
<design-reply-form
|
||||
ref="newDiscussionForm"
|
||||
v-model="comment"
|
||||
:is-saving="loading"
|
||||
:markdown-preview-path="markdownPreviewPath"
|
||||
@submit-form="mutate"
|
||||
@cancel-form="closeCommentForm"
|
||||
/> </apollo-mutation
|
||||
></template>
|
||||
</design-sidebar>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -5,6 +5,7 @@ export * from './api/markdown_api';
|
|||
export * from './api/bulk_imports_api';
|
||||
export * from './api/namespaces_api';
|
||||
export * from './api/tags_api';
|
||||
export * from './api/alert_management_alerts_api';
|
||||
|
||||
// Note: It's not possible to spy on methods imported from this file in
|
||||
// Jest tests.
|
||||
|
|
|
@ -17,6 +17,5 @@ tier:
|
|||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
performance_indicator_type:
|
||||
- paid_gmau
|
||||
performance_indicator_type: []
|
||||
milestone: "<13.9"
|
||||
|
|
|
@ -17,9 +17,7 @@ tier:
|
|||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
performance_indicator_type:
|
||||
- gmau
|
||||
- paid_gmau
|
||||
performance_indicator_type: []
|
||||
milestone: "<13.9"
|
||||
milestone_removed: "15.1"
|
||||
removed_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/88485"
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RemoveInvalidIntegrations < Gitlab::Database::Migration[2.0]
|
||||
disable_ddl_transaction!
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||
|
||||
BATCH_SIZE = 100
|
||||
|
||||
def up
|
||||
loop do
|
||||
deleted = Integration.where(type_new: nil).limit(BATCH_SIZE).delete_all
|
||||
|
||||
break if deleted < BATCH_SIZE
|
||||
end
|
||||
end
|
||||
|
||||
# Isolated version of the Integration model
|
||||
class Integration < ApplicationRecord
|
||||
self.table_name = 'integrations'
|
||||
self.inheritance_column = :_type_disabled
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
cbbcc9c2439ff583489239afaadb6b18fc86286360745565af52b9baebbf436e
|
|
@ -61,7 +61,7 @@ Some of these services have their own environment variables to override the log
|
|||
| GitLab Export | `INFO` | `EXPORT_DEBUG` |
|
||||
| GitLab Geo | `INFO` | |
|
||||
| GitLab Import | `INFO` | `IMPORT_DEBUG` |
|
||||
| GitLab QA Runtime | `ERROR` | `QA_DEBUG` |
|
||||
| GitLab QA Runtime | `INFO` | `QA_LOG_LEVEL` |
|
||||
| Google APIs | `INFO` | |
|
||||
| Rack Timeout | `ERROR` | |
|
||||
| Sidekiq (server) | `INFO` | |
|
||||
|
@ -144,7 +144,8 @@ Line breaks were added to examples for legibility:
|
|||
"db_duration_s":0.08,
|
||||
"view_duration_s":2.39,
|
||||
"duration_s":20.54,
|
||||
"pid": 81836
|
||||
"pid": 81836,
|
||||
"worker_id":"puma_0"
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -167,7 +168,8 @@ seconds:
|
|||
- `redis_<instance>_duration_s`: Total time to retrieve data from a Redis instance
|
||||
- `redis_<instance>_read_bytes`: Total bytes read from a Redis instance
|
||||
- `redis_<instance>_write_bytes`: Total bytes written to a Redis instance
|
||||
- `pid`: Process ID of the Puma worker
|
||||
- `pid`: The worker's Linux process ID (changes when workers restart)
|
||||
- `worker_id`: The worker's logical ID (does not change when workers restart)
|
||||
|
||||
User clone and fetch activity using HTTP transport appears in the log as `action: git_upload_pack`.
|
||||
|
||||
|
@ -240,6 +242,7 @@ Starting with GitLab 12.5, if an error occurs, an
|
|||
"view_duration_s":2.39,
|
||||
"duration_s":20.54,
|
||||
"pid": 81836,
|
||||
"worker_id": "puma_0",
|
||||
"exception.class": "NameError",
|
||||
"exception.message": "undefined local variable or method `adsf' for #<Admin::DashboardController:0x00007ff3c9648588>",
|
||||
"exception.backtrace": [
|
||||
|
@ -319,7 +322,10 @@ It helps you see requests made directly to the API. For example:
|
|||
"username":"root",
|
||||
"queue_duration":100.31,
|
||||
"gitaly_calls":30,
|
||||
"gitaly_duration":5.36
|
||||
"gitaly_duration":5.36,
|
||||
"pid": 81836,
|
||||
"worker_id": "puma_0",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -546,6 +552,7 @@ Sidekiq. For example:
|
|||
"created_at":"2018-04-03T22:57:21.930Z",
|
||||
"enqueued_at":"2018-04-03T22:57:21.931Z",
|
||||
"pid":10077,
|
||||
"worker_id":"sidekiq_0",
|
||||
"message":"UpdateAllMirrorsWorker JID-06aeaa3b0aadacf9981f368e: done: 0.139 sec",
|
||||
"job_status":"done",
|
||||
"duration":0.139,
|
||||
|
|
|
@ -180,13 +180,13 @@ could be merged sooner.
|
|||
|
||||
### Get your changes reviewed
|
||||
|
||||
When your merge request is ready for reviews you must assign
|
||||
reviewers and then maintainers. Depending on the complexity of a change, you
|
||||
might want to involve the people that know the most about the codebase area you are
|
||||
changing. We do have many domain experts and maintainers in Verify and it is absolutely acceptable to
|
||||
ask them to review your code when you are not certain if a reviewer or
|
||||
maintainer assigned by the Reviewer Roulette has enough context about the
|
||||
change.
|
||||
When your merge request is ready for reviews you must assign reviewers and then
|
||||
maintainers. Depending on the complexity of a change, you might want to involve
|
||||
the people that know the most about the codebase area you are changing. We do
|
||||
have many domain experts and maintainers in Verify and it is absolutely
|
||||
acceptable to ask them to review your code when you are not certain if a
|
||||
reviewer or maintainer assigned by the Reviewer Roulette has enough context
|
||||
about the change.
|
||||
|
||||
The reviewer roulette offers useful suggestions, but as assigning the right
|
||||
reviewers is important it should not be done automatically every time. It might
|
||||
|
@ -195,9 +195,19 @@ updating, because their feedback might be limited to code style and syntax.
|
|||
Depending on the complexity and impact of a change, assigning the right people
|
||||
to review your changes might be very important.
|
||||
|
||||
If you don't know who to assign, consult `git blame` or ask in the `#verify`
|
||||
If you don't know who to assign, consult `git blame` or ask in the `#s_verify`
|
||||
Slack channel (GitLab team members only).
|
||||
|
||||
There are two kinds of changes / merge requests that require additional
|
||||
attention from reviews and an additional reviewer:
|
||||
|
||||
1. Merge requests changing code around pipelines / stages / builds statuses.
|
||||
1. Merge requests changing code around authentication / security features.
|
||||
|
||||
In both cases engineers are expected to request a review from a maintainer and
|
||||
a domain expert. If maintainer is the domain expert, involving another person
|
||||
is recommended.
|
||||
|
||||
### Incremental rollouts
|
||||
|
||||
After your merge request is merged by a maintainer, it is time to release it to
|
||||
|
|
|
@ -43,7 +43,7 @@ This is a partial list of the [RSpec metadata](https://relishapp.com/rspec/rspec
|
|||
| `:requires_git_protocol_v2` | The test requires that Git protocol version 2 is enabled on the server. It's assumed to be enabled by default but if not the test can be skipped by setting `QA_CAN_TEST_GIT_PROTOCOL_V2` to `false`. |
|
||||
| `:requires_praefect` | The test requires that the GitLab instance uses [Gitaly Cluster](../../../administration/gitaly/praefect.md) (a.k.a. Praefect) as the repository storage . It's assumed to be used by default but if not the test can be skipped by setting `QA_CAN_TEST_PRAEFECT` to `false`. |
|
||||
| `:runner` | The test depends on and sets up a GitLab Runner instance, typically to run a pipeline. |
|
||||
| `:sanity_feature_flags` | The test run in the `Test::Sanity::FeatureFlags` scenario to verify the functioning of the feature flag handling part of the test framework |
|
||||
| `:sanity_feature_flags` | The test verifies the functioning of the feature flag handling part of the test framework |
|
||||
| `:skip_live_env` | The test is excluded when run against live deployed environments such as Staging, Canary, and Production. |
|
||||
| `:skip_fips_env` | The test is excluded when run against an environment in FIPS mode. |
|
||||
| `:skip_signup_disabled` | The test uses UI to sign up a new user and is skipped in any environment that does not allow new user registration via the UI. |
|
||||
|
|
|
@ -274,7 +274,7 @@ Geo requires an EE license. To visit the Geo sites in your browser, you need a r
|
|||
1. To run end-to-end tests from your local GDK, run the [`EE::Scenario::Test::Geo` scenario](https://gitlab.com/gitlab-org/gitlab/-/blob/f7272b77e80215c39d1ffeaed27794c220dbe03f/qa/qa/ee/scenario/test/geo.rb) from the [`gitlab/qa/` directory](https://gitlab.com/gitlab-org/gitlab/-/blob/f7272b77e80215c39d1ffeaed27794c220dbe03f/qa). Include `--without-setup` to skip the Geo configuration steps.
|
||||
|
||||
```shell
|
||||
QA_DEBUG=true GITLAB_QA_ACCESS_TOKEN=[add token here] GITLAB_QA_ADMIN_ACCESS_TOKEN=[add token here] bundle exec bin/qa QA::EE::Scenario::Test::Geo \
|
||||
QA_LOG_LEVEL=debug GITLAB_QA_ACCESS_TOKEN=[add token here] GITLAB_QA_ADMIN_ACCESS_TOKEN=[add token here] bundle exec bin/qa QA::EE::Scenario::Test::Geo \
|
||||
--primary-address http://gitlab-primary.geo \
|
||||
--secondary-address http://gitlab-secondary.geo \
|
||||
--without-setup
|
||||
|
@ -283,7 +283,7 @@ Geo requires an EE license. To visit the Geo sites in your browser, you need a r
|
|||
If the containers need to be configured first (for example, if you used the `--no-tests` option in the previous step), run the `QA::EE::Scenario::Test::Geo scenario` as shown below to first do the Geo configuration steps, and then run Geo end-to-end tests. Make sure that `EE_LICENSE` is (still) defined in your shell session.
|
||||
|
||||
```shell
|
||||
QA_DEBUG=true bundle exec bin/qa QA::EE::Scenario::Test::Geo \
|
||||
QA_LOG_LEVEL=debug bundle exec bin/qa QA::EE::Scenario::Test::Geo \
|
||||
--primary-address http://gitlab-primary.geo \
|
||||
--primary-name gitlab-primary \
|
||||
--secondary-address http://gitlab-secondary.geo \
|
||||
|
@ -354,7 +354,7 @@ To run the LDAP tests on your local with TLS enabled, follow these steps:
|
|||
1. Run an LDAP test from [`gitlab/qa`](https://gitlab.com/gitlab-org/gitlab/-/tree/d5447ebb5f99d4c72780681ddf4dc25b0738acba/qa) directory:
|
||||
|
||||
```shell
|
||||
GITLAB_LDAP_USERNAME="tanuki" GITLAB_LDAP_PASSWORD="password" QA_DEBUG=true WEBDRIVER_HEADLESS=false bin/qa Test::Instance::All https://gitlab.test qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb
|
||||
GITLAB_LDAP_USERNAME="tanuki" GITLAB_LDAP_PASSWORD="password" QA_LOG_LEVEL=debug WEBDRIVER_HEADLESS=false bin/qa Test::Instance::All https://gitlab.test qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb
|
||||
```
|
||||
|
||||
### Running LDAP tests with TLS disabled
|
||||
|
@ -382,7 +382,7 @@ To run the LDAP tests on your local with TLS disabled, follow these steps:
|
|||
1. Run an LDAP test from [`gitlab/qa`](https://gitlab.com/gitlab-org/gitlab/-/tree/d5447ebb5f99d4c72780681ddf4dc25b0738acba/qa) directory:
|
||||
|
||||
```shell
|
||||
GITLAB_LDAP_USERNAME="tanuki" GITLAB_LDAP_PASSWORD="password" QA_DEBUG=true WEBDRIVER_HEADLESS=false bin/qa Test::Instance::All http://localhost qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb
|
||||
GITLAB_LDAP_USERNAME="tanuki" GITLAB_LDAP_PASSWORD="password" QA_LOG_LEVEL=debug WEBDRIVER_HEADLESS=false bin/qa Test::Instance::All http://localhost qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb
|
||||
```
|
||||
|
||||
## Guide to the mobile suite
|
||||
|
|
|
@ -25,12 +25,12 @@ WEBDRIVER_HEADLESS=false bundle exec bin/qa Test::Instance::All http://localhost
|
|||
|
||||
Sometimes a test might fail and the failure stack trace doesn't provide enough
|
||||
information to determine what went wrong. You can get more information by enabling
|
||||
debug logs by setting `QA_DEBUG=true`, to see what the test framework is attempting.
|
||||
debug logs by setting `QA_LOG_LEVEL=debug`, to see what the test framework is attempting.
|
||||
For example:
|
||||
|
||||
```shell
|
||||
cd gitlab/qa
|
||||
QA_DEBUG=true bundle exec bin/qa Test::Instance::All http://localhost:3000
|
||||
QA_LOG_LEVEL=debug bundle exec bin/qa Test::Instance::All http://localhost:3000
|
||||
```
|
||||
|
||||
The test framework then outputs many logs showing the actions taken during
|
||||
|
|
|
@ -31,6 +31,7 @@ module Gitlab
|
|||
instrument_thread_memory_allocations(payload)
|
||||
instrument_load_balancing(payload)
|
||||
instrument_pid(payload)
|
||||
instrument_worker_id(payload)
|
||||
instrument_uploads(payload)
|
||||
instrument_rate_limiting_gates(payload)
|
||||
end
|
||||
|
@ -106,6 +107,10 @@ module Gitlab
|
|||
payload[:pid] = Process.pid
|
||||
end
|
||||
|
||||
def instrument_worker_id(payload)
|
||||
payload[:worker_id] = ::Prometheus::PidProvider.worker_id
|
||||
end
|
||||
|
||||
def instrument_thread_memory_allocations(payload)
|
||||
counters = ::Gitlab::Memory::Instrumentation.measure_thread_memory_allocations(
|
||||
::Gitlab::RequestContext.instance.thread_memory_allocations)
|
||||
|
|
|
@ -306,7 +306,7 @@ module Gitlab
|
|||
types Issue
|
||||
condition do
|
||||
current_user.can?(:set_issue_crm_contacts, quick_action_target) &&
|
||||
CustomerRelations::Contact.exists_for_group?(quick_action_target.project.root_ancestor)
|
||||
quick_action_target.customer_relations_contacts.exists?
|
||||
end
|
||||
execution_message do
|
||||
_('One or more contacts were successfully removed.')
|
||||
|
|
|
@ -118,7 +118,7 @@ GEM
|
|||
gitlab (4.18.0)
|
||||
httparty (~> 0.18)
|
||||
terminal-table (>= 1.5.1)
|
||||
gitlab-qa (7.32.0)
|
||||
gitlab-qa (7.33.0)
|
||||
activesupport (~> 6.1)
|
||||
gitlab (~> 4.18.0)
|
||||
http (~> 5.0)
|
||||
|
|
|
@ -61,10 +61,6 @@ module QA
|
|||
ENV['CI_PROJECT_NAME']
|
||||
end
|
||||
|
||||
def debug?
|
||||
enabled?(ENV['QA_DEBUG'], default: false)
|
||||
end
|
||||
|
||||
def generate_allure_report?
|
||||
enabled?(ENV['QA_GENERATE_ALLURE_REPORT'], default: false)
|
||||
end
|
||||
|
|
|
@ -14,7 +14,7 @@ module QA
|
|||
# @return [ActiveSupport::Logger]
|
||||
def self.logger
|
||||
@logger ||= Gitlab::QA::TestLogger.logger(
|
||||
level: Runtime::Env.debug? ? "DEBUG" : Gitlab::QA::Runtime::Env.log_level,
|
||||
level: Gitlab::QA::Runtime::Env.log_level,
|
||||
source: 'QA Tests',
|
||||
path: File.expand_path('../../tmp', __dir__)
|
||||
)
|
||||
|
|
|
@ -119,8 +119,6 @@ RSpec.describe QA::Resource::Base do
|
|||
end
|
||||
|
||||
it 'logs the resource and build method' do
|
||||
stub_env('QA_DEBUG', 'true')
|
||||
|
||||
subject.fabricate_via_api!('something', resource: resource, parents: [])
|
||||
|
||||
expect(QA::Runtime::Logger).to have_received(:info) do |&msg|
|
||||
|
@ -159,8 +157,6 @@ RSpec.describe QA::Resource::Base do
|
|||
end
|
||||
|
||||
it 'logs the resource and build method' do
|
||||
stub_env('QA_DEBUG', 'true')
|
||||
|
||||
subject.fabricate_via_browser_ui!('something', resource: resource, parents: [])
|
||||
|
||||
expect(QA::Runtime::Logger).to have_received(:info) do |&msg|
|
||||
|
|
|
@ -47,13 +47,6 @@ RSpec.describe QA::Runtime::Env do
|
|||
default: false
|
||||
end
|
||||
|
||||
describe '.debug?' do
|
||||
it_behaves_like 'boolean method',
|
||||
method: :debug?,
|
||||
env_key: 'QA_DEBUG',
|
||||
default: false
|
||||
end
|
||||
|
||||
describe '.webdriver_headless?' do
|
||||
it_behaves_like 'boolean method',
|
||||
method: :webdriver_headless?,
|
||||
|
|
|
@ -147,8 +147,8 @@ RSpec.describe ChaosController do
|
|||
let(:gc_stat) { GC.stat.stringify_keys }
|
||||
|
||||
it 'runs a full GC on the current web worker' do
|
||||
expect(Prometheus::PidProvider).to receive(:worker_id).and_return('worker-0')
|
||||
expect(Gitlab::Chaos).to receive(:run_gc).and_return(gc_stat)
|
||||
allow(Prometheus::PidProvider).to receive(:worker_id).and_return('worker-0')
|
||||
allow(Gitlab::Chaos).to receive(:run_gc).and_return(gc_stat)
|
||||
|
||||
post :gc
|
||||
|
||||
|
|
|
@ -94,8 +94,8 @@ RSpec.describe MetricsController, :request_store do
|
|||
end
|
||||
|
||||
it 'renders system stats JSON' do
|
||||
expect(Prometheus::PidProvider).to receive(:worker_id).and_return('worker-0')
|
||||
expect(Gitlab::Metrics::System).to receive(:summary).and_return(summary)
|
||||
allow(Prometheus::PidProvider).to receive(:worker_id).and_return('worker-0')
|
||||
allow(Gitlab::Metrics::System).to receive(:summary).and_return(summary)
|
||||
|
||||
get :system
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ describe('Design management design presentation component', () => {
|
|||
discussions,
|
||||
isAnnotating,
|
||||
resolvedDiscussionsExpanded,
|
||||
isLoading: false,
|
||||
},
|
||||
stubs,
|
||||
});
|
||||
|
|
|
@ -52,6 +52,7 @@ describe('Design management design sidebar component', () => {
|
|||
design,
|
||||
resolvedDiscussionsExpanded: false,
|
||||
markdownPreviewPath: '',
|
||||
isLoading: false,
|
||||
...props,
|
||||
},
|
||||
mocks: {
|
||||
|
|
|
@ -99,7 +99,7 @@ exports[`Design management design index page renders design index 1`] = `
|
|||
variant="link"
|
||||
>
|
||||
Resolved Comments (1)
|
||||
|
||||
|
||||
</gl-button-stub>
|
||||
|
||||
<gl-popover-stub
|
||||
|
@ -112,8 +112,8 @@ exports[`Design management design index page renders design index 1`] = `
|
|||
>
|
||||
<p>
|
||||
|
||||
Comments you resolve can be viewed and unresolved by going to the "Resolved Comments" section below
|
||||
|
||||
Comments you resolve can be viewed and unresolved by going to the "Resolved Comments" section below
|
||||
|
||||
</p>
|
||||
|
||||
<a
|
||||
|
@ -144,19 +144,6 @@ exports[`Design management design index page renders design index 1`] = `
|
|||
</div>
|
||||
`;
|
||||
|
||||
exports[`Design management design index page sets loading state 1`] = `
|
||||
<div
|
||||
class="design-detail js-design-detail fixed-top gl-w-full gl-bottom-0 gl-display-flex gl-justify-content-center gl-flex-direction-column gl-lg-flex-direction-row"
|
||||
>
|
||||
<gl-loading-icon-stub
|
||||
class="gl-align-self-center"
|
||||
color="dark"
|
||||
label="Loading"
|
||||
size="xl"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Design management design index page with error GlAlert is rendered in correct position with correct content 1`] = `
|
||||
<div
|
||||
class="design-detail js-design-detail fixed-top gl-w-full gl-bottom-0 gl-display-flex gl-justify-content-center gl-flex-direction-column gl-lg-flex-direction-row"
|
||||
|
@ -185,8 +172,8 @@ exports[`Design management design index page with error GlAlert is rendered in c
|
|||
variant="danger"
|
||||
>
|
||||
|
||||
woops
|
||||
|
||||
woops
|
||||
|
||||
</gl-alert-stub>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -91,7 +91,12 @@ describe('Design management design index page', () => {
|
|||
|
||||
function createComponent(
|
||||
{ loading = false } = {},
|
||||
{ data = {}, intialRouteOptions = {}, provide = {} } = {},
|
||||
{
|
||||
data = {},
|
||||
intialRouteOptions = {},
|
||||
provide = {},
|
||||
stubs = { ApolloMutation, DesignSidebar, DesignReplyForm },
|
||||
} = {},
|
||||
) {
|
||||
const $apollo = {
|
||||
queries: {
|
||||
|
@ -109,11 +114,7 @@ describe('Design management design index page', () => {
|
|||
wrapper = shallowMount(DesignIndex, {
|
||||
propsData: { id: '1' },
|
||||
mocks: { $apollo },
|
||||
stubs: {
|
||||
ApolloMutation,
|
||||
DesignSidebar,
|
||||
DesignReplyForm,
|
||||
},
|
||||
stubs,
|
||||
provide: {
|
||||
issueIid: '1',
|
||||
projectPath: 'project-path',
|
||||
|
@ -139,7 +140,7 @@ describe('Design management design index page', () => {
|
|||
describe('when navigating to component', () => {
|
||||
it('applies fullscreen layout class', () => {
|
||||
jest.spyOn(utils, 'getPageLayoutElement').mockReturnValue(mockPageLayoutElement);
|
||||
createComponent({ loading: true });
|
||||
createComponent({}, { stubs: {} });
|
||||
|
||||
expect(mockPageLayoutElement.classList.add).toHaveBeenCalledTimes(1);
|
||||
expect(mockPageLayoutElement.classList.add).toHaveBeenCalledWith(
|
||||
|
@ -151,7 +152,7 @@ describe('Design management design index page', () => {
|
|||
describe('when navigating within the component', () => {
|
||||
it('`scale` prop of DesignPresentation component is 1', async () => {
|
||||
jest.spyOn(utils, 'getPageLayoutElement').mockReturnValue(mockPageLayoutElement);
|
||||
createComponent({ loading: false }, { data: { design, scale: 2 } });
|
||||
createComponent({}, { data: { design, scale: 2 } });
|
||||
|
||||
await nextTick();
|
||||
expect(findDesignPresentation().props('scale')).toBe(2);
|
||||
|
@ -180,7 +181,8 @@ describe('Design management design index page', () => {
|
|||
it('sets loading state', () => {
|
||||
createComponent({ loading: true });
|
||||
|
||||
expect(wrapper.element).toMatchSnapshot();
|
||||
expect(wrapper.find(DesignPresentation).props('isLoading')).toBe(true);
|
||||
expect(wrapper.find(DesignSidebar).props('isLoading')).toBe(true);
|
||||
});
|
||||
|
||||
it('renders design index', () => {
|
||||
|
@ -197,6 +199,7 @@ describe('Design management design index page', () => {
|
|||
design,
|
||||
markdownPreviewPath: '/project-path/preview_markdown?target_type=Issue',
|
||||
resolvedDiscussionsExpanded: false,
|
||||
isLoading: false,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ function factory(routeArg) {
|
|||
|
||||
return mount(App, {
|
||||
router,
|
||||
provide: { issueIid: '1' },
|
||||
stubs: { Toolbar: true },
|
||||
mocks: {
|
||||
$apollo: {
|
||||
queries: {
|
||||
|
|
|
@ -110,6 +110,14 @@ RSpec.describe Gitlab::InstrumentationHelper do
|
|||
expect(payload).to include(:pid)
|
||||
end
|
||||
|
||||
it 'logs the worker ID' do
|
||||
expect(Prometheus::PidProvider).to receive(:worker_id).and_return('puma_1')
|
||||
|
||||
subject
|
||||
|
||||
expect(payload).to include(worker_id: 'puma_1')
|
||||
end
|
||||
|
||||
context 'when logging memory allocations' do
|
||||
include MemoryInstrumentationHelper
|
||||
|
||||
|
|
|
@ -303,7 +303,8 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do
|
|||
'duration_s' => 0.0,
|
||||
'completed_at' => timestamp.to_f,
|
||||
'cpu_s' => 1.111112,
|
||||
'rate_limiting_gates' => []
|
||||
'rate_limiting_gates' => [],
|
||||
'worker_id' => "process_#{Process.pid}"
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require_migration!
|
||||
|
||||
RSpec.describe RemoveInvalidIntegrations, :migration do
|
||||
describe '#up' do
|
||||
let!(:integrations) { table(:integrations) }
|
||||
|
||||
let!(:valid_integration) { integrations.create!(type_new: 'Foo') }
|
||||
let!(:invalid_integration) { integrations.create! }
|
||||
|
||||
it 'removes invalid integrations', :aggregate_failures do
|
||||
expect { migrate! }
|
||||
.to change { integrations.pluck(:id) }.to(contain_exactly(valid_integration.id))
|
||||
end
|
||||
|
||||
context 'when there are many invalid integrations' do
|
||||
before do
|
||||
stub_const('RemoveInvalidIntegrations::BATCH_SIZE', 3)
|
||||
5.times { integrations.create! }
|
||||
end
|
||||
|
||||
it 'removes them all' do
|
||||
migrate!
|
||||
|
||||
expect(integrations.pluck(:type_new)).to all(be_present)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2866,12 +2866,6 @@ RSpec.describe QuickActions::InterpretService do
|
|||
|
||||
expect(explanations).to be_empty
|
||||
end
|
||||
|
||||
it '/remove_contacts is not available' do
|
||||
_, explanations = service.explain(remove_contacts, issue)
|
||||
|
||||
expect(explanations).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'when group has contacts' do
|
||||
|
@ -2883,10 +2877,22 @@ RSpec.describe QuickActions::InterpretService do
|
|||
expect(explanations).to contain_exactly("Add customer relation contact(s).")
|
||||
end
|
||||
|
||||
it '/remove_contacts is available' do
|
||||
_, explanations = service.explain(remove_contacts, issue)
|
||||
context 'when issue has no contacts' do
|
||||
it '/remove_contacts is not available' do
|
||||
_, explanations = service.explain(remove_contacts, issue)
|
||||
|
||||
expect(explanations).to contain_exactly("Remove customer relation contact(s).")
|
||||
expect(explanations).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'when issue has contacts' do
|
||||
let!(:issue_contact) { create(:issue_customer_relations_contact, issue: issue, contact: contact) }
|
||||
|
||||
it '/remove_contacts is available' do
|
||||
_, explanations = service.explain(remove_contacts, issue)
|
||||
|
||||
expect(explanations).to contain_exactly("Remove customer relation contact(s).")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -64,7 +64,8 @@ RSpec.shared_context 'structured_logger' do
|
|||
'duration_s' => 0.0,
|
||||
'completed_at' => timestamp.to_f,
|
||||
'cpu_s' => 1.111112,
|
||||
'rate_limiting_gates' => []
|
||||
'rate_limiting_gates' => [],
|
||||
'worker_id' => "process_#{Process.pid}"
|
||||
)
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue