Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
6724a6ee6b
commit
adf76f8f1d
71 changed files with 1039 additions and 309 deletions
|
@ -729,8 +729,6 @@ Rails/SaveBang:
|
|||
- 'ee/spec/models/merge_train_spec.rb'
|
||||
- 'spec/models/packages/package_spec.rb'
|
||||
- 'ee/spec/models/project_ci_cd_setting_spec.rb'
|
||||
- 'ee/spec/models/project_services/github_service_spec.rb'
|
||||
- 'ee/spec/models/project_services/jenkins_service_spec.rb'
|
||||
- 'ee/spec/models/project_spec.rb'
|
||||
- 'ee/spec/models/protected_environment_spec.rb'
|
||||
- 'ee/spec/models/repository_spec.rb'
|
||||
|
@ -1113,12 +1111,6 @@ Rails/SaveBang:
|
|||
- 'spec/models/pages_domain_spec.rb'
|
||||
- 'spec/models/project_auto_devops_spec.rb'
|
||||
- 'spec/models/project_feature_spec.rb'
|
||||
- 'spec/models/project_services/bamboo_service_spec.rb'
|
||||
- 'spec/models/project_services/buildkite_service_spec.rb'
|
||||
- 'spec/models/project_services/jira_service_spec.rb'
|
||||
- 'spec/models/project_services/packagist_service_spec.rb'
|
||||
- 'spec/models/project_services/pipelines_email_service_spec.rb'
|
||||
- 'spec/models/project_services/teamcity_service_spec.rb'
|
||||
- 'spec/models/project_spec.rb'
|
||||
- 'spec/models/project_team_spec.rb'
|
||||
- 'spec/models/protectable_dropdown_spec.rb'
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
import $ from 'jquery';
|
||||
|
||||
export default class AjaxLoadingSpinner {
|
||||
static init() {
|
||||
const $elements = $('.js-ajax-loading-spinner');
|
||||
|
||||
$elements.on('ajax:beforeSend', AjaxLoadingSpinner.ajaxBeforeSend);
|
||||
$elements.on('ajax:complete', AjaxLoadingSpinner.ajaxComplete);
|
||||
}
|
||||
|
||||
static ajaxBeforeSend(e) {
|
||||
e.target.setAttribute('disabled', '');
|
||||
const iconElement = e.target.querySelector('i');
|
||||
// get first fa- icon
|
||||
const originalIcon = iconElement.className.match(/(fa-)([^\s]+)/g)[0];
|
||||
iconElement.dataset.icon = originalIcon;
|
||||
AjaxLoadingSpinner.toggleLoadingIcon(iconElement);
|
||||
$(e.target).off('ajax:beforeSend', AjaxLoadingSpinner.ajaxBeforeSend);
|
||||
}
|
||||
|
||||
static ajaxComplete(e) {
|
||||
e.target.removeAttribute('disabled');
|
||||
const iconElement = e.target.querySelector('i');
|
||||
AjaxLoadingSpinner.toggleLoadingIcon(iconElement);
|
||||
$(e.target).off('ajax:complete', AjaxLoadingSpinner.ajaxComplete);
|
||||
}
|
||||
|
||||
static toggleLoadingIcon(iconElement) {
|
||||
const { classList } = iconElement;
|
||||
classList.toggle(iconElement.dataset.icon);
|
||||
classList.toggle('gl-spinner');
|
||||
classList.toggle('gl-spinner-orange');
|
||||
classList.toggle('gl-spinner-sm');
|
||||
}
|
||||
}
|
31
app/assets/javascripts/branches/ajax_loading_spinner.js
Normal file
31
app/assets/javascripts/branches/ajax_loading_spinner.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
import $ from 'jquery';
|
||||
|
||||
export default class AjaxLoadingSpinner {
|
||||
static init() {
|
||||
const $elements = $('.js-ajax-loading-spinner');
|
||||
$elements.on('ajax:beforeSend', AjaxLoadingSpinner.ajaxBeforeSend);
|
||||
}
|
||||
|
||||
static ajaxBeforeSend(e) {
|
||||
const button = e.target;
|
||||
const newButton = document.createElement('button');
|
||||
newButton.classList.add('btn', 'btn-default', 'disabled', 'gl-button');
|
||||
newButton.setAttribute('disabled', 'disabled');
|
||||
|
||||
const spinner = document.createElement('span');
|
||||
spinner.classList.add('align-text-bottom', 'gl-spinner', 'gl-spinner-sm', 'gl-spinner-orange');
|
||||
newButton.appendChild(spinner);
|
||||
|
||||
button.classList.add('hidden');
|
||||
button.parentNode.insertBefore(newButton, button.nextSibling);
|
||||
|
||||
$(button).one('ajax:error', () => {
|
||||
newButton.remove();
|
||||
button.classList.remove('hidden');
|
||||
});
|
||||
|
||||
$(button).one('ajax:success', () => {
|
||||
$(button).off('ajax:beforeSend', AjaxLoadingSpinner.ajaxBeforeSend);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { GlLink, GlSprintf } from '@gitlab/ui';
|
||||
import { s__ } from '~/locale';
|
||||
import DetailsRow from '~/registry/shared/components/details_row.vue';
|
||||
import DetailsRow from '~/vue_shared/components/registry/details_row.vue';
|
||||
import { generateConanRecipe } from '../utils';
|
||||
import { PackageType } from '../../shared/constants';
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { GlLink, GlSprintf } from '@gitlab/ui';
|
||||
import { s__ } from '~/locale';
|
||||
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import HistoryElement from './history_element.vue';
|
||||
import HistoryItem from '~/vue_shared/components/registry/history_item.vue';
|
||||
|
||||
export default {
|
||||
name: 'PackageHistory',
|
||||
|
@ -16,7 +16,7 @@ export default {
|
|||
components: {
|
||||
GlLink,
|
||||
GlSprintf,
|
||||
HistoryElement,
|
||||
HistoryItem,
|
||||
TimeAgoTooltip,
|
||||
},
|
||||
props: {
|
||||
|
@ -46,7 +46,7 @@ export default {
|
|||
<div class="issuable-discussion">
|
||||
<h3 class="gl-font-lg" data-testid="title">{{ __('History') }}</h3>
|
||||
<ul class="timeline main-notes-list notes gl-mb-4" data-testid="timeline">
|
||||
<history-element icon="clock" data-testid="created-on">
|
||||
<history-item icon="clock" data-testid="created-on">
|
||||
<gl-sprintf :message="$options.i18n.createdOn">
|
||||
<template #name>
|
||||
<strong>{{ packageEntity.name }}</strong>
|
||||
|
@ -58,8 +58,8 @@ export default {
|
|||
<time-ago-tooltip :time="packageEntity.created_at" />
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</history-element>
|
||||
<history-element icon="pencil" data-testid="updated-at">
|
||||
</history-item>
|
||||
<history-item icon="pencil" data-testid="updated-at">
|
||||
<gl-sprintf :message="$options.i18n.updatedAtText">
|
||||
<template #name>
|
||||
<strong>{{ packageEntity.name }}</strong>
|
||||
|
@ -71,9 +71,9 @@ export default {
|
|||
<time-ago-tooltip :time="packageEntity.updated_at" />
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</history-element>
|
||||
</history-item>
|
||||
<template v-if="packagePipeline">
|
||||
<history-element icon="commit" data-testid="commit">
|
||||
<history-item icon="commit" data-testid="commit">
|
||||
<gl-sprintf :message="$options.i18n.commitText">
|
||||
<template #link>
|
||||
<gl-link :href="packagePipeline.project.commit_url">{{
|
||||
|
@ -84,8 +84,8 @@ export default {
|
|||
<strong>{{ packagePipeline.ref }}</strong>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</history-element>
|
||||
<history-element icon="pipeline" data-testid="pipeline">
|
||||
</history-item>
|
||||
<history-item icon="pipeline" data-testid="pipeline">
|
||||
<gl-sprintf :message="$options.i18n.pipelineText">
|
||||
<template #link>
|
||||
<gl-link :href="packagePipeline.project.pipeline_url"
|
||||
|
@ -97,9 +97,9 @@ export default {
|
|||
</template>
|
||||
<template #author>{{ packagePipeline.user.name }}</template>
|
||||
</gl-sprintf>
|
||||
</history-element>
|
||||
</history-item>
|
||||
</template>
|
||||
<history-element icon="package" data-testid="published">
|
||||
<history-item icon="package" data-testid="published">
|
||||
<gl-sprintf :message="$options.i18n.publishText">
|
||||
<template #project>
|
||||
<strong>{{ projectName }}</strong>
|
||||
|
@ -108,7 +108,7 @@ export default {
|
|||
<time-ago-tooltip :time="packageEntity.created_at" />
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</history-element>
|
||||
</history-item>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import AjaxLoadingSpinner from '~/ajax_loading_spinner';
|
||||
import AjaxLoadingSpinner from '~/branches/ajax_loading_spinner';
|
||||
import DeleteModal from '~/branches/branches_delete_modal';
|
||||
import initDiverganceGraph from '~/branches/divergence_graph';
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
|||
import { formatDate } from '~/lib/utils/datetime_utility';
|
||||
import ListItem from '~/vue_shared/components/registry/list_item.vue';
|
||||
import DeleteButton from '../delete_button.vue';
|
||||
import DetailsRow from '~/registry/shared/components/details_row.vue';
|
||||
import DetailsRow from '~/vue_shared/components/registry/details_row.vue';
|
||||
import {
|
||||
REMOVE_TAG_BUTTON_TITLE,
|
||||
DIGEST_LABEL,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<script>
|
||||
/* eslint-disable vue/no-v-html */
|
||||
import { GlLoadingIcon } from '@gitlab/ui';
|
||||
import { sprintf, s__ } from '~/locale';
|
||||
import statusCodes from '~/lib/utils/http_status';
|
||||
import { bytesToMiB } from '~/lib/utils/number_utils';
|
||||
|
@ -11,6 +12,7 @@ export default {
|
|||
name: 'MemoryUsage',
|
||||
components: {
|
||||
MemoryGraph,
|
||||
GlLoadingIcon,
|
||||
},
|
||||
props: {
|
||||
metricsUrl: {
|
||||
|
@ -156,8 +158,9 @@ export default {
|
|||
<template>
|
||||
<div class="mr-info-list clearfix mr-memory-usage js-mr-memory-usage">
|
||||
<p v-if="shouldShowLoading" class="usage-info js-usage-info usage-info-loading">
|
||||
<i class="fa fa-spinner fa-spin usage-info-load-spinner" aria-hidden="true"> </i
|
||||
>{{ s__('mrWidget|Loading deployment statistics') }}
|
||||
<gl-loading-icon class="usage-info-load-spinner" />{{
|
||||
s__('mrWidget|Loading deployment statistics')
|
||||
}}
|
||||
</p>
|
||||
<p
|
||||
v-if="shouldShowMemoryGraph"
|
||||
|
|
|
@ -3,12 +3,11 @@ import { GlIcon } from '@gitlab/ui';
|
|||
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
|
||||
|
||||
export default {
|
||||
name: 'HistoryElement',
|
||||
name: 'HistoryItem',
|
||||
components: {
|
||||
GlIcon,
|
||||
TimelineEntryItem,
|
||||
},
|
||||
|
||||
props: {
|
||||
icon: {
|
||||
type: String,
|
||||
|
@ -29,7 +28,9 @@ export default {
|
|||
<slot></slot>
|
||||
</span>
|
||||
</div>
|
||||
<div class="note-body"></div>
|
||||
<div class="note-body">
|
||||
<slot name="body"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</timeline-entry-item>
|
||||
</template>
|
|
@ -919,12 +919,12 @@
|
|||
}
|
||||
|
||||
.issuable-todo-btn {
|
||||
.fa-spinner {
|
||||
.gl-spinner {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.is-loading {
|
||||
.fa-spinner {
|
||||
.gl-spinner {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
|
|
|
@ -55,8 +55,7 @@ module SendFileUpload
|
|||
def image_scaling_request?(file_upload)
|
||||
avatar_safe_for_scaling?(file_upload) &&
|
||||
scaling_allowed_by_feature_flags?(file_upload) &&
|
||||
valid_image_scaling_width? &&
|
||||
current_user
|
||||
valid_image_scaling_width?
|
||||
end
|
||||
|
||||
def avatar_safe_for_scaling?(file_upload)
|
||||
|
|
|
@ -4,7 +4,7 @@ module IssuesHelper
|
|||
def issue_css_classes(issue)
|
||||
classes = ["issue"]
|
||||
classes << "closed" if issue.closed?
|
||||
classes << "today" if issue.today?
|
||||
classes << "today" if issue.new?
|
||||
classes << "user-can-drag" if @sort == 'relative_position'
|
||||
classes.join(' ')
|
||||
end
|
||||
|
|
|
@ -385,8 +385,12 @@ module Issuable
|
|||
Date.today == created_at.to_date
|
||||
end
|
||||
|
||||
def created_hours_ago
|
||||
(Time.now.utc.to_i - created_at.utc.to_i) / 3600
|
||||
end
|
||||
|
||||
def new?
|
||||
today? && created_at == updated_at
|
||||
created_hours_ago < 24
|
||||
end
|
||||
|
||||
def open?
|
||||
|
|
|
@ -17,8 +17,6 @@ module Issues
|
|||
Issues::CloseService
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
NO_REBALANCING_NEEDED = ((RelativePositioning::MIN_POSITION * 0.9999)..(RelativePositioning::MAX_POSITION * 0.9999)).freeze
|
||||
|
||||
def rebalance_if_needed(issue)
|
||||
|
@ -32,6 +30,8 @@ module Issues
|
|||
IssueRebalancingWorker.perform_async(nil, issue.project_id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_assignee_note(issue, old_assignees)
|
||||
SystemNoteService.change_issuable_assignees(
|
||||
issue, issue.project, current_user, old_assignees)
|
||||
|
|
|
@ -16,12 +16,12 @@ module Issues
|
|||
|
||||
def before_create(issue)
|
||||
spam_check(issue, current_user, action: :create)
|
||||
issue.move_to_end
|
||||
|
||||
# current_user (defined in BaseService) is not available within run_after_commit block
|
||||
user = current_user
|
||||
issue.run_after_commit do
|
||||
NewIssueWorker.perform_async(issue.id, user.id)
|
||||
IssuePlacementWorker.perform_async(issue.id)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -30,7 +30,6 @@ module Issues
|
|||
user_agent_detail_service.create
|
||||
resolve_discussions_with_issue(issuable)
|
||||
delete_milestone_total_issue_counter_cache(issuable.milestone)
|
||||
rebalance_if_needed(issuable)
|
||||
|
||||
super
|
||||
end
|
||||
|
|
|
@ -50,25 +50,25 @@
|
|||
|
||||
- if can?(current_user, :push_code, @project)
|
||||
- if branch.name == @project.repository.root_ref
|
||||
%button{ class: "btn btn-remove remove-row js-ajax-loading-spinner has-tooltip disabled",
|
||||
%button{ class: "btn btn-remove remove-row has-tooltip disabled",
|
||||
disabled: true,
|
||||
title: s_('Branches|The default branch cannot be deleted') }
|
||||
= icon("trash-o")
|
||||
= sprite_icon("remove")
|
||||
- elsif protected_branch?(@project, branch)
|
||||
- if can?(current_user, :push_to_delete_protected_branch, @project)
|
||||
%button{ class: "btn btn-remove remove-row js-ajax-loading-spinner has-tooltip",
|
||||
%button{ class: "btn btn-remove remove-row has-tooltip",
|
||||
title: s_('Branches|Delete protected branch'),
|
||||
data: { toggle: "modal",
|
||||
target: "#modal-delete-branch",
|
||||
delete_path: project_branch_path(@project, branch.name),
|
||||
branch_name: branch.name,
|
||||
is_merged: ("true" if merged) } }
|
||||
= icon("trash-o")
|
||||
= sprite_icon("remove")
|
||||
- else
|
||||
%button{ class: "btn btn-remove remove-row js-ajax-loading-spinner has-tooltip disabled",
|
||||
%button{ class: "btn btn-remove remove-row has-tooltip disabled",
|
||||
disabled: true,
|
||||
title: s_('Branches|Only a project maintainer or owner can delete a protected branch') }
|
||||
= icon("trash-o")
|
||||
= sprite_icon("remove")
|
||||
- else
|
||||
= link_to project_branch_path(@project, branch.name),
|
||||
class: "btn btn-remove remove-row qa-remove-btn js-ajax-loading-spinner has-tooltip",
|
||||
|
@ -77,4 +77,4 @@
|
|||
data: { confirm: s_("Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?") % { branch_name: branch.name } },
|
||||
remote: true,
|
||||
'aria-label' => s_('Branches|Delete branch') do
|
||||
= icon("trash-o")
|
||||
= sprite_icon("remove")
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
// Fallback while content is loading
|
||||
.title.hide-collapsed
|
||||
= _('Time tracking')
|
||||
= icon('spinner spin', 'aria-hidden': 'true')
|
||||
= loading_icon
|
||||
- if issuable_sidebar.has_key?(:due_date)
|
||||
.block.due_date
|
||||
.sidebar-collapsed-icon.has-tooltip{ data: { placement: 'left', container: 'body', html: 'true', boundary: 'viewport' }, title: sidebar_due_date_tooltip_label(issuable_sidebar[:due_date]) }
|
||||
|
|
|
@ -12,4 +12,4 @@
|
|||
data: todo_button_data }
|
||||
%span.issuable-todo-inner.js-issuable-todo-inner<
|
||||
= is_collapsed ? button_icon : button_title
|
||||
= icon('spin spinner', 'aria-hidden': 'true')
|
||||
= loading_icon
|
||||
|
|
|
@ -1452,6 +1452,14 @@
|
|||
:weight: 1
|
||||
:idempotent:
|
||||
:tags: []
|
||||
- :name: issue_placement
|
||||
:feature_category: :issue_tracking
|
||||
:has_external_dependencies:
|
||||
:urgency: :high
|
||||
:resource_boundary: :cpu
|
||||
:weight: 2
|
||||
:idempotent: true
|
||||
:tags: []
|
||||
- :name: issue_rebalancing
|
||||
:feature_category: :issue_tracking
|
||||
:has_external_dependencies:
|
||||
|
|
36
app/workers/issue_placement_worker.rb
Normal file
36
app/workers/issue_placement_worker.rb
Normal file
|
@ -0,0 +1,36 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class IssuePlacementWorker
|
||||
include ApplicationWorker
|
||||
|
||||
idempotent!
|
||||
feature_category :issue_tracking
|
||||
urgency :high
|
||||
worker_resource_boundary :cpu
|
||||
weight 2
|
||||
|
||||
# Move at most the most recent 100 issues
|
||||
QUERY_LIMIT = 100
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def perform(issue_id)
|
||||
issue = Issue.id_in(issue_id).first
|
||||
return unless issue
|
||||
|
||||
# Move the most recent 100 unpositioned items to the end.
|
||||
# This is to deal with out-of-order execution of the worker,
|
||||
# while preserving creation order.
|
||||
to_place = Issue
|
||||
.relative_positioning_query_base(issue)
|
||||
.where(relative_position: nil)
|
||||
.order({ created_at: :desc }, { id: :desc })
|
||||
.limit(QUERY_LIMIT)
|
||||
|
||||
Issue.move_nulls_to_end(to_place.to_a.reverse)
|
||||
Issues::BaseService.new(nil).rebalance_if_needed(to_place.max_by(&:relative_position))
|
||||
rescue RelativePositioning::NoSpaceLeft => e
|
||||
Gitlab::ErrorTracking.log_exception(e, issue_id: issue_id)
|
||||
IssueRebalancingWorker.perform_async(nil, issue.project_id)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
|
@ -12,8 +12,8 @@ class NewIssueWorker # rubocop:disable Scalability/IdempotentWorker
|
|||
def perform(issue_id, user_id)
|
||||
return unless objects_found?(issue_id, user_id)
|
||||
|
||||
EventCreateService.new.open_issue(issuable, user)
|
||||
NotificationService.new.new_issue(issuable, user)
|
||||
::EventCreateService.new.open_issue(issuable, user)
|
||||
::NotificationService.new.new_issue(issuable, user)
|
||||
issuable.create_cross_references!(user)
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Migrate '.fa-spinner' to '.spinner' for 'app/assets/javascripts/vue_merge_request_widget/components/deployment/memory_usage.vue'
|
||||
merge_request: 41142
|
||||
author: Gilang Gumilar
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Migrate '.fa-spinner' to '.spinner' for 'app/views/shared/issuable'
|
||||
merge_request: 41132
|
||||
author: Gilang Gumilar
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Adds monthly package data to usage ping
|
||||
merge_request: 40452
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Ensure issue creation is not blocked by positioning
|
||||
merge_request: 41313
|
||||
author:
|
||||
type: fixed
|
5
changelogs/unreleased/dz-improve-new-issue-highlight.yml
Normal file
5
changelogs/unreleased/dz-improve-new-issue-highlight.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Change logic behind new issues highlight
|
||||
merge_request: 41150
|
||||
author:
|
||||
type: changed
|
5
changelogs/unreleased/leipert-ajax-spinner-icons.yml
Normal file
5
changelogs/unreleased/leipert-ajax-spinner-icons.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Change icon for branch delete button
|
||||
merge_request: 39968
|
||||
author:
|
||||
type: changed
|
5
changelogs/unreleased/rails-save-bang-21.yml
Normal file
5
changelogs/unreleased/rails-save-bang-21.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix Rails/SaveBang offenses for *spec/models/project_services*
|
||||
merge_request: 41320
|
||||
author: Rajendra Kadam
|
||||
type: fixed
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
name: security-on-demand-scans-site-validation
|
||||
name: security_on_demand_scans_site_validation
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40685
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/241815
|
||||
group: group::dynamic analysis
|
|
@ -7,7 +7,9 @@ require "carrierwave/storage/fog"
|
|||
#
|
||||
# This patch also incorporates
|
||||
# https://github.com/carrierwaveuploader/carrierwave/pull/2375 to
|
||||
# provide Azure support. This is already in CarrierWave v2.1.x, but
|
||||
# provide Azure support
|
||||
# and https://github.com/carrierwaveuploader/carrierwave/pull/2397 to
|
||||
# support custom expire_at. This is already in CarrierWave v2.1.x, but
|
||||
# upgrading this gem is a significant task:
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/216067
|
||||
module CarrierWave
|
||||
|
@ -28,7 +30,7 @@ module CarrierWave
|
|||
# avoid a get by using local references
|
||||
local_directory = connection.directories.new(key: @uploader.fog_directory)
|
||||
local_file = local_directory.files.new(key: path)
|
||||
expire_at = ::Fog::Time.now + @uploader.fog_authenticated_url_expiration
|
||||
expire_at = options[:expire_at] || ::Fog::Time.now + @uploader.fog_authenticated_url_expiration
|
||||
case @uploader.fog_credentials[:provider]
|
||||
when 'AWS', 'Google'
|
||||
# Older versions of fog-google do not support options as a parameter
|
||||
|
|
|
@ -138,6 +138,8 @@
|
|||
- 2
|
||||
- - irker
|
||||
- 1
|
||||
- - issue_placement
|
||||
- 2
|
||||
- - issue_rebalancing
|
||||
- 1
|
||||
- - jira_connect
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddIdCreatedAtIndexToPackages < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
INDEX_NAME = 'index_packages_packages_on_id_and_created_at'
|
||||
|
||||
def up
|
||||
add_concurrent_index :packages_packages, [:id, :created_at], name: INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name(:packages_packages, INDEX_NAME)
|
||||
end
|
||||
end
|
1
db/schema_migrations/20200901203055
Normal file
1
db/schema_migrations/20200901203055
Normal file
|
@ -0,0 +1 @@
|
|||
eb13fb285ac9af83bbc66397a5352a824575ad4af93178b98fbfc1be2e11ce8b
|
|
@ -20463,6 +20463,8 @@ CREATE INDEX index_packages_package_files_on_package_id_and_file_name ON public.
|
|||
|
||||
CREATE INDEX index_packages_packages_on_creator_id ON public.packages_packages USING btree (creator_id);
|
||||
|
||||
CREATE INDEX index_packages_packages_on_id_and_created_at ON public.packages_packages USING btree (id, created_at);
|
||||
|
||||
CREATE INDEX index_packages_packages_on_name_trigram ON public.packages_packages USING gin (name public.gin_trgm_ops);
|
||||
|
||||
CREATE INDEX index_packages_packages_on_project_id_and_created_at ON public.packages_packages USING btree (project_id, created_at);
|
||||
|
|
|
@ -6016,7 +6016,7 @@ type GeoNode {
|
|||
name: String
|
||||
|
||||
"""
|
||||
Package file registries of the GeoNode. Available only when feature flag `geo_self_service_framework` is enabled
|
||||
Package file registries of the GeoNode. Available only when feature flag `geo_package_file_replication` is enabled
|
||||
"""
|
||||
packageFileRegistries(
|
||||
"""
|
||||
|
@ -6095,6 +6095,37 @@ type GeoNode {
|
|||
"""
|
||||
syncObjectStorage: Boolean
|
||||
|
||||
"""
|
||||
Find terraform state registries on this Geo node. Available only when feature
|
||||
flag `geo_terraform_state_replication` is enabled
|
||||
"""
|
||||
terraformStateRegistries(
|
||||
"""
|
||||
Returns the elements in the list that come after the specified cursor.
|
||||
"""
|
||||
after: String
|
||||
|
||||
"""
|
||||
Returns the elements in the list that come before the specified cursor.
|
||||
"""
|
||||
before: String
|
||||
|
||||
"""
|
||||
Returns the first _n_ elements from the list.
|
||||
"""
|
||||
first: Int
|
||||
|
||||
"""
|
||||
Filters registries by their ID
|
||||
"""
|
||||
ids: [ID!]
|
||||
|
||||
"""
|
||||
Returns the last _n_ elements from the list.
|
||||
"""
|
||||
last: Int
|
||||
): TerraformStateRegistryConnection
|
||||
|
||||
"""
|
||||
The user-facing URL for this Geo node
|
||||
"""
|
||||
|
@ -15712,6 +15743,86 @@ type TaskCompletionStatus {
|
|||
count: Int!
|
||||
}
|
||||
|
||||
"""
|
||||
Represents the sync and verification state of a terraform state
|
||||
"""
|
||||
type TerraformStateRegistry {
|
||||
"""
|
||||
Timestamp when the TerraformStateRegistry was created
|
||||
"""
|
||||
createdAt: Time
|
||||
|
||||
"""
|
||||
ID of the TerraformStateRegistry
|
||||
"""
|
||||
id: ID!
|
||||
|
||||
"""
|
||||
Error message during sync of the TerraformStateRegistry
|
||||
"""
|
||||
lastSyncFailure: String
|
||||
|
||||
"""
|
||||
Timestamp of the most recent successful sync of the TerraformStateRegistry
|
||||
"""
|
||||
lastSyncedAt: Time
|
||||
|
||||
"""
|
||||
Timestamp after which the TerraformStateRegistry should be resynced
|
||||
"""
|
||||
retryAt: Time
|
||||
|
||||
"""
|
||||
Number of consecutive failed sync attempts of the TerraformStateRegistry
|
||||
"""
|
||||
retryCount: Int
|
||||
|
||||
"""
|
||||
Sync state of the TerraformStateRegistry
|
||||
"""
|
||||
state: RegistryState
|
||||
|
||||
"""
|
||||
ID of the TerraformState
|
||||
"""
|
||||
terraformStateId: ID!
|
||||
}
|
||||
|
||||
"""
|
||||
The connection type for TerraformStateRegistry.
|
||||
"""
|
||||
type TerraformStateRegistryConnection {
|
||||
"""
|
||||
A list of edges.
|
||||
"""
|
||||
edges: [TerraformStateRegistryEdge]
|
||||
|
||||
"""
|
||||
A list of nodes.
|
||||
"""
|
||||
nodes: [TerraformStateRegistry]
|
||||
|
||||
"""
|
||||
Information to aid in pagination.
|
||||
"""
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
"""
|
||||
An edge in a connection.
|
||||
"""
|
||||
type TerraformStateRegistryEdge {
|
||||
"""
|
||||
A cursor for use in pagination.
|
||||
"""
|
||||
cursor: String!
|
||||
|
||||
"""
|
||||
The item at the end of the edge.
|
||||
"""
|
||||
node: TerraformStateRegistry
|
||||
}
|
||||
|
||||
"""
|
||||
Represents a requirement test report.
|
||||
"""
|
||||
|
|
|
@ -16819,7 +16819,7 @@
|
|||
},
|
||||
{
|
||||
"name": "packageFileRegistries",
|
||||
"description": "Package file registries of the GeoNode. Available only when feature flag `geo_self_service_framework` is enabled",
|
||||
"description": "Package file registries of the GeoNode. Available only when feature flag `geo_package_file_replication` is enabled",
|
||||
"args": [
|
||||
{
|
||||
"name": "ids",
|
||||
|
@ -17019,6 +17019,77 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "terraformStateRegistries",
|
||||
"description": "Find terraform state registries on this Geo node. Available only when feature flag `geo_terraform_state_replication` is enabled",
|
||||
"args": [
|
||||
{
|
||||
"name": "ids",
|
||||
"description": "Filters registries by their ID",
|
||||
"type": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "ID",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "after",
|
||||
"description": "Returns the elements in the list that come after the specified cursor.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "before",
|
||||
"description": "Returns the elements in the list that come before the specified cursor.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "first",
|
||||
"description": "Returns the first _n_ elements from the list.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "last",
|
||||
"description": "Returns the last _n_ elements from the list.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "TerraformStateRegistryConnection",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"description": "The user-facing URL for this Geo node",
|
||||
|
@ -46322,6 +46393,251 @@
|
|||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "TerraformStateRegistry",
|
||||
"description": "Represents the sync and verification state of a terraform state",
|
||||
"fields": [
|
||||
{
|
||||
"name": "createdAt",
|
||||
"description": "Timestamp when the TerraformStateRegistry was created",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Time",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"description": "ID of the TerraformStateRegistry",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "ID",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "lastSyncFailure",
|
||||
"description": "Error message during sync of the TerraformStateRegistry",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "lastSyncedAt",
|
||||
"description": "Timestamp of the most recent successful sync of the TerraformStateRegistry",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Time",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "retryAt",
|
||||
"description": "Timestamp after which the TerraformStateRegistry should be resynced",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Time",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "retryCount",
|
||||
"description": "Number of consecutive failed sync attempts of the TerraformStateRegistry",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "state",
|
||||
"description": "Sync state of the TerraformStateRegistry",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "ENUM",
|
||||
"name": "RegistryState",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "terraformStateId",
|
||||
"description": "ID of the TerraformState",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "ID",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [
|
||||
|
||||
],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "TerraformStateRegistryConnection",
|
||||
"description": "The connection type for TerraformStateRegistry.",
|
||||
"fields": [
|
||||
{
|
||||
"name": "edges",
|
||||
"description": "A list of edges.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "TerraformStateRegistryEdge",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "nodes",
|
||||
"description": "A list of nodes.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "TerraformStateRegistry",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "pageInfo",
|
||||
"description": "Information to aid in pagination.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "PageInfo",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [
|
||||
|
||||
],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "TerraformStateRegistryEdge",
|
||||
"description": "An edge in a connection.",
|
||||
"fields": [
|
||||
{
|
||||
"name": "cursor",
|
||||
"description": "A cursor for use in pagination.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "node",
|
||||
"description": "The item at the end of the edge.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "TerraformStateRegistry",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [
|
||||
|
||||
],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "TestReport",
|
||||
|
|
|
@ -2317,6 +2317,21 @@ Completion status of tasks
|
|||
| `completedCount` | Int! | Number of completed tasks |
|
||||
| `count` | Int! | Number of total tasks |
|
||||
|
||||
## TerraformStateRegistry
|
||||
|
||||
Represents the sync and verification state of a terraform state
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | ---- | ---------- |
|
||||
| `createdAt` | Time | Timestamp when the TerraformStateRegistry was created |
|
||||
| `id` | ID! | ID of the TerraformStateRegistry |
|
||||
| `lastSyncFailure` | String | Error message during sync of the TerraformStateRegistry |
|
||||
| `lastSyncedAt` | Time | Timestamp of the most recent successful sync of the TerraformStateRegistry |
|
||||
| `retryAt` | Time | Timestamp after which the TerraformStateRegistry should be resynced |
|
||||
| `retryCount` | Int | Number of consecutive failed sync attempts of the TerraformStateRegistry |
|
||||
| `state` | RegistryState | Sync state of the TerraformStateRegistry |
|
||||
| `terraformStateId` | ID! | ID of the TerraformState |
|
||||
|
||||
## TestReport
|
||||
|
||||
Represents a requirement test report.
|
||||
|
|
|
@ -566,7 +566,7 @@ the Admin Area UI, and Prometheus!
|
|||
null: true,
|
||||
resolver: ::Resolvers::Geo::WidgetRegistriesResolver,
|
||||
description: 'Find widget registries on this Geo node',
|
||||
feature_flag: :geo_self_service_framework
|
||||
feature_flag: :geo_widget_replication
|
||||
```
|
||||
|
||||
1. Add the new `widget_registries` field name to the `expected_fields` array in
|
||||
|
|
|
@ -138,7 +138,90 @@ const label = __('Subscribe');
|
|||
```
|
||||
|
||||
In order to test JavaScript translations you have to change the GitLab
|
||||
localization to other language than English and you have to generate JSON files
|
||||
localization to another language than English and you have to generate JSON files
|
||||
using `bin/rake gettext:po_to_json` or `bin/rake gettext:compile`.
|
||||
|
||||
### Vue files
|
||||
|
||||
In Vue files we make both the `__()` (double underscore parenthesis) function and the `s__()` (namespaced double underscore parenthesis) function available that you can import from the `~/locale` file. For instance:
|
||||
|
||||
```javascript
|
||||
import { __, s__ } from '~/locale';
|
||||
const label = __('Subscribe');
|
||||
const nameSpacedlabel = __('Plan|Subscribe');
|
||||
```
|
||||
|
||||
For the static text strings we suggest two patterns for using these translations in Vue files:
|
||||
|
||||
- External constants file:
|
||||
|
||||
```javascript
|
||||
javascripts
|
||||
│
|
||||
└───alert_settings
|
||||
│ │ constants.js
|
||||
│ └───components
|
||||
│ │ alert_settings_form.vue
|
||||
|
||||
|
||||
// constants.js
|
||||
|
||||
import { s__ } from '~/locale';
|
||||
|
||||
/* Integration constants */
|
||||
|
||||
export const I18N_ALERT_SETTINGS_FORM = {
|
||||
saveBtnLabel: __('Save changes'),
|
||||
};
|
||||
|
||||
|
||||
// alert_settings_form.vue
|
||||
|
||||
import {
|
||||
I18N_ALERT_SETTINGS_FORM,
|
||||
} from '../constants';
|
||||
|
||||
<script>
|
||||
export default {
|
||||
i18n: {
|
||||
I18N_ALERT_SETTINGS_FORM,
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-button
|
||||
ref="submitBtn"
|
||||
variant="success"
|
||||
type="submit"
|
||||
>
|
||||
{{ $options.i18n.I18N_ALERT_SETTINGS_FORM }}
|
||||
</gl-button>
|
||||
</template>
|
||||
```
|
||||
|
||||
When possible, you should opt for this pattern, as this allows you to import these strings directly into your component specs for re-use during testing.
|
||||
|
||||
- Internal component `$options` object `:
|
||||
|
||||
```javascript
|
||||
<script>
|
||||
export default {
|
||||
i18n: {
|
||||
buttonLabel: s__('Plan|Button Label')
|
||||
}
|
||||
},
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-button :aria-label="$options.i18n.buttonLabel">
|
||||
{{ $options.i18n.buttonLabel }}
|
||||
</gl-button>
|
||||
</template>
|
||||
```
|
||||
|
||||
In order to visually test the Vue translations you have to change the GitLab
|
||||
localization to another language than English and you have to generate JSON files
|
||||
using `bin/rake gettext:po_to_json` or `bin/rake gettext:compile`.
|
||||
|
||||
### Dynamic translations
|
||||
|
|
|
@ -147,8 +147,7 @@ ldd $(command -v git) | grep pcre2
|
|||
|
||||
The output should contain `libpcre2-8.so.0`.
|
||||
|
||||
Is the system packaged Git too old, or not compiled with pcre2?
|
||||
Remove it:
|
||||
If the system packaged Git is too old or not compiled with `pcre2`, remove it:
|
||||
|
||||
```shell
|
||||
sudo apt-get remove git-core
|
||||
|
|
|
@ -70,7 +70,7 @@ This view allows you to:
|
|||
- Filter image repositories by their name.
|
||||
- [Delete](#delete-images-from-within-gitlab) one or more image repository.
|
||||
- Navigate to the image repository details page.
|
||||
- Show a **Quick start** dropdown with the most common commands to log in, build and push
|
||||
- Show a **Quick start** dropdown with the most common commands to log in, build and push.
|
||||
- Show a banner if the optional [cleanup policy](#cleanup-policy) is enabled for this project.
|
||||
|
||||
### Control Container Registry for your group
|
||||
|
|
|
@ -181,6 +181,7 @@ module Gitlab
|
|||
successful_deployments: deployment_count(Deployment.success.where(last_28_days_time_period)),
|
||||
failed_deployments: deployment_count(Deployment.failed.where(last_28_days_time_period)),
|
||||
# rubocop: enable UsageData/LargeTable:
|
||||
packages: count(::Packages::Package.where(last_28_days_time_period)),
|
||||
personal_snippets: count(PersonalSnippet.where(last_28_days_time_period)),
|
||||
project_snippets: count(ProjectSnippet.where(last_28_days_time_period))
|
||||
}.tap do |data|
|
||||
|
|
|
@ -7779,6 +7779,9 @@ msgstr ""
|
|||
msgid "DastProfiles|Do you want to discard your changes?"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Download validation text file"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Edit feature will come soon. Please create a new profile if changes needed"
|
||||
msgstr ""
|
||||
|
||||
|
@ -7839,21 +7842,57 @@ msgstr ""
|
|||
msgid "DastProfiles|Site Profiles"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Site is not validated yet, please follow the steps."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Site must be validated to run an active scan."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Spider timeout"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Step 1 - Choose site validation method"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Step 2 - Add following text to the target site"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Step 3 - Confirm text file location and validate"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Target URL"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Target timeout"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Text file validation"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|The maximum number of seconds allowed for the spider to traverse the site."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Validate"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Validate target site"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Validating..."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Validation must be turned off to change the target URL"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
|
||||
msgstr ""
|
||||
|
||||
msgid "Data is still calculating..."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
"@gitlab/ui": "20.18.1",
|
||||
"@gitlab/visual-review-tools": "1.6.1",
|
||||
"@rails/actioncable": "^6.0.3-1",
|
||||
"@sentry/browser": "^5.10.2",
|
||||
"@sentry/browser": "^5.22.3",
|
||||
"@sourcegraph/code-host-integration": "0.0.50",
|
||||
"@toast-ui/editor": "^2.3.1",
|
||||
"@toast-ui/vue-editor": "^2.3.1",
|
||||
|
|
|
@ -79,6 +79,23 @@ RSpec.describe SendFileUpload do
|
|||
it_behaves_like 'handles image resize requests allowed by FFs'
|
||||
end
|
||||
|
||||
context 'when boths FFs are enabled globally' do
|
||||
before do
|
||||
stub_feature_flags(dynamic_image_resizing_requester: true)
|
||||
stub_feature_flags(dynamic_image_resizing_owner: true)
|
||||
end
|
||||
|
||||
it_behaves_like 'handles image resize requests allowed by FFs'
|
||||
|
||||
context 'when current_user is nil' do
|
||||
before do
|
||||
allow(controller).to receive(:current_user).and_return(nil)
|
||||
end
|
||||
|
||||
it_behaves_like 'handles image resize requests allowed by FFs'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when only FF based on content requester is enabled for current user' do
|
||||
before do
|
||||
stub_feature_flags(dynamic_image_resizing_requester: image_requester)
|
||||
|
|
|
@ -101,6 +101,7 @@ FactoryBot.define do
|
|||
create(:package, project: projects[0])
|
||||
create(:package, project: projects[0])
|
||||
create(:package, project: projects[1])
|
||||
create(:package, created_at: 2.months.ago, project: projects[1])
|
||||
|
||||
ProjectFeature.first.update_attribute('repository_access_level', 0)
|
||||
|
||||
|
|
2
spec/fixtures/api/schemas/issue.json
vendored
2
spec/fixtures/api/schemas/issue.json
vendored
|
@ -12,7 +12,7 @@
|
|||
"title": { "type": "string" },
|
||||
"confidential": { "type": "boolean" },
|
||||
"due_date": { "type": ["date", "null"] },
|
||||
"relative_position": { "type": "integer" },
|
||||
"relative_position": { "type": ["integer", "null"] },
|
||||
"time_estimate": { "type": "integer" },
|
||||
"issue_sidebar_endpoint": { "type": "string" },
|
||||
"toggle_subscription_endpoint": { "type": "string" },
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
import $ from 'jquery';
|
||||
import AjaxLoadingSpinner from '~/ajax_loading_spinner';
|
||||
|
||||
describe('Ajax Loading Spinner', () => {
|
||||
const fixtureTemplate = 'static/ajax_loading_spinner.html';
|
||||
preloadFixtures(fixtureTemplate);
|
||||
|
||||
beforeEach(() => {
|
||||
loadFixtures(fixtureTemplate);
|
||||
AjaxLoadingSpinner.init();
|
||||
});
|
||||
|
||||
it('change current icon with spinner icon and disable link while waiting ajax response', done => {
|
||||
jest.spyOn($, 'ajax').mockImplementation(req => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
const ajaxLoadingSpinner = document.querySelector('.js-ajax-loading-spinner');
|
||||
const icon = ajaxLoadingSpinner.querySelector('i');
|
||||
|
||||
req.beforeSend(xhr, { dataType: 'text/html' });
|
||||
|
||||
expect(icon).not.toHaveClass('fa-trash-o');
|
||||
expect(icon).toHaveClass('gl-spinner');
|
||||
expect(icon).toHaveClass('gl-spinner-orange');
|
||||
expect(icon).toHaveClass('gl-spinner-sm');
|
||||
expect(icon.dataset.icon).toEqual('fa-trash-o');
|
||||
expect(ajaxLoadingSpinner.getAttribute('disabled')).toEqual('');
|
||||
|
||||
req.complete({});
|
||||
|
||||
done();
|
||||
const deferred = $.Deferred();
|
||||
return deferred.promise();
|
||||
});
|
||||
document.querySelector('.js-ajax-loading-spinner').click();
|
||||
});
|
||||
|
||||
it('use original icon again and enabled the link after complete the ajax request', done => {
|
||||
jest.spyOn($, 'ajax').mockImplementation(req => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
const ajaxLoadingSpinner = document.querySelector('.js-ajax-loading-spinner');
|
||||
|
||||
req.beforeSend(xhr, { dataType: 'text/html' });
|
||||
req.complete({});
|
||||
|
||||
const icon = ajaxLoadingSpinner.querySelector('i');
|
||||
|
||||
expect(icon).toHaveClass('fa-trash-o');
|
||||
expect(icon).not.toHaveClass('gl-spinner');
|
||||
expect(icon).not.toHaveClass('gl-spinner-orange');
|
||||
expect(icon).not.toHaveClass('gl-spinner-sm');
|
||||
expect(ajaxLoadingSpinner.getAttribute('disabled')).toEqual(null);
|
||||
|
||||
done();
|
||||
const deferred = $.Deferred();
|
||||
return deferred.promise();
|
||||
});
|
||||
document.querySelector('.js-ajax-loading-spinner').click();
|
||||
});
|
||||
});
|
32
spec/frontend/branches/ajax_loading_spinner_spec.js
Normal file
32
spec/frontend/branches/ajax_loading_spinner_spec.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
import AjaxLoadingSpinner from '~/branches/ajax_loading_spinner';
|
||||
|
||||
describe('Ajax Loading Spinner', () => {
|
||||
let ajaxLoadingSpinnerElement;
|
||||
let fauxEvent;
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML = `
|
||||
<div>
|
||||
<a class="js-ajax-loading-spinner"
|
||||
data-remote
|
||||
href="http://goesnowhere.nothing/whereami">
|
||||
<i class="fa fa-trash-o"></i>
|
||||
</a></div>`;
|
||||
AjaxLoadingSpinner.init();
|
||||
ajaxLoadingSpinnerElement = document.querySelector('.js-ajax-loading-spinner');
|
||||
fauxEvent = { target: ajaxLoadingSpinnerElement };
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
document.body.innerHTML = '';
|
||||
});
|
||||
|
||||
it('`ajaxBeforeSend` event handler sets current icon to spinner and disables link', () => {
|
||||
expect(ajaxLoadingSpinnerElement.parentNode.querySelector('.gl-spinner')).toBeNull();
|
||||
expect(ajaxLoadingSpinnerElement.classList.contains('hidden')).toBe(false);
|
||||
|
||||
AjaxLoadingSpinner.ajaxBeforeSend(fauxEvent);
|
||||
|
||||
expect(ajaxLoadingSpinnerElement.parentNode.querySelector('.gl-spinner')).not.toBeNull();
|
||||
expect(ajaxLoadingSpinnerElement.classList.contains('hidden')).toBe(true);
|
||||
});
|
||||
});
|
|
@ -1,3 +0,0 @@
|
|||
<a class="js-ajax-loading-spinner" data-remote href="http://goesnowhere.nothing/whereami">
|
||||
<i class="fa fa-trash-o"></i>
|
||||
</a>
|
|
@ -1,6 +1,6 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlLink, GlSprintf } from '@gitlab/ui';
|
||||
import DetailsRow from '~/registry/shared/components/details_row.vue';
|
||||
import DetailsRow from '~/vue_shared/components/registry/details_row.vue';
|
||||
import component from '~/packages/details/components/additional_metadata.vue';
|
||||
|
||||
import { mavenPackage, conanPackage, nugetPackage, npmPackage } from '../../mock_data';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlLink, GlSprintf } from '@gitlab/ui';
|
||||
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import HistoryElement from '~/packages/details/components/history_element.vue';
|
||||
import HistoryItem from '~/vue_shared/components/registry/history_item.vue';
|
||||
import component from '~/packages/details/components/package_history.vue';
|
||||
|
||||
import { mavenPackage, mockPipelineInfo } from '../../mock_data';
|
||||
|
@ -17,8 +17,8 @@ describe('Package History', () => {
|
|||
wrapper = shallowMount(component, {
|
||||
propsData: { ...defaultProps, ...props },
|
||||
stubs: {
|
||||
HistoryElement: {
|
||||
props: HistoryElement.props,
|
||||
HistoryItem: {
|
||||
props: HistoryItem.props,
|
||||
template: '<div data-testid="history-element"><slot></slot></div>',
|
||||
},
|
||||
GlSprintf,
|
||||
|
|
|
@ -6,7 +6,7 @@ import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
|
|||
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import component from '~/registry/explorer/components/details_page/tags_list_row.vue';
|
||||
import DeleteButton from '~/registry/explorer/components/delete_button.vue';
|
||||
import DetailsRow from '~/registry/shared/components/details_row.vue';
|
||||
import DetailsRow from '~/vue_shared/components/registry/details_row.vue';
|
||||
import {
|
||||
REMOVE_TAG_BUTTON_TITLE,
|
||||
REMOVE_TAG_BUTTON_DISABLE_TOOLTIP,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`History Element renders the correct markup 1`] = `
|
||||
exports[`History Item renders the correct markup 1`] = `
|
||||
<li
|
||||
class="timeline-entry system-note note-wrapper gl-mb-6!"
|
||||
>
|
||||
|
@ -31,7 +31,11 @@ exports[`History Element renders the correct markup 1`] = `
|
|||
|
||||
<div
|
||||
class="note-body"
|
||||
/>
|
||||
>
|
||||
<div
|
||||
data-testid="body-slot"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
|
@ -1,6 +1,6 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlIcon } from '@gitlab/ui';
|
||||
import component from '~/registry/shared/components/details_row.vue';
|
||||
import component from '~/vue_shared/components/registry/details_row.vue';
|
||||
|
||||
describe('DetailsRow', () => {
|
||||
let wrapper;
|
|
@ -1,9 +1,9 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlIcon } from '@gitlab/ui';
|
||||
import component from '~/packages/details/components/history_element.vue';
|
||||
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
|
||||
import component from '~/vue_shared/components/registry/history_item.vue';
|
||||
|
||||
describe('History Element', () => {
|
||||
describe('History Item', () => {
|
||||
let wrapper;
|
||||
const defaultProps = {
|
||||
icon: 'pencil',
|
||||
|
@ -17,6 +17,7 @@ describe('History Element', () => {
|
|||
},
|
||||
slots: {
|
||||
default: '<div data-testid="default-slot"></div>',
|
||||
body: '<div data-testid="body-slot"></div>',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
@ -29,6 +30,7 @@ describe('History Element', () => {
|
|||
const findTimelineEntry = () => wrapper.find(TimelineEntryItem);
|
||||
const findGlIcon = () => wrapper.find(GlIcon);
|
||||
const findDefaultSlot = () => wrapper.find('[data-testid="default-slot"]');
|
||||
const findBodySlot = () => wrapper.find('[data-testid="body-slot"]');
|
||||
|
||||
it('renders the correct markup', () => {
|
||||
mountComponent();
|
||||
|
@ -41,11 +43,19 @@ describe('History Element', () => {
|
|||
|
||||
expect(findDefaultSlot().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('has a body slot', () => {
|
||||
mountComponent();
|
||||
|
||||
expect(findBodySlot().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('has a timeline entry', () => {
|
||||
mountComponent();
|
||||
|
||||
expect(findTimelineEntry().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('has an icon', () => {
|
||||
mountComponent();
|
||||
|
|
@ -28,5 +28,17 @@ RSpec.describe 'CarrierWave::Storage::Fog::File' do
|
|||
expect(subject.authenticated_url).to eq("https://sa.blob.core.windows.net/test_container/test_blob?token")
|
||||
end
|
||||
end
|
||||
|
||||
context 'with custom expire_at' do
|
||||
it 'properly sets expires param' do
|
||||
expire_at = 24.hours.from_now
|
||||
|
||||
expect_next_instance_of(Fog::Storage::AzureRM::File) do |file|
|
||||
expect(file).to receive(:url).with(expire_at).and_call_original
|
||||
end
|
||||
|
||||
expect(subject.authenticated_url(expire_at: expire_at)).to eq("https://sa.blob.core.windows.net/test_container/test_blob?token")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -479,7 +479,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
|
|||
expect(count_data[:project_snippets]).to eq(4)
|
||||
|
||||
expect(count_data[:projects_with_packages]).to eq(2)
|
||||
expect(count_data[:packages]).to eq(3)
|
||||
expect(count_data[:packages]).to eq(4)
|
||||
end
|
||||
|
||||
it 'gathers object store usage correctly' do
|
||||
|
@ -572,6 +572,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
|
|||
expect(counts_monthly[:snippets]).to eq(3)
|
||||
expect(counts_monthly[:personal_snippets]).to eq(1)
|
||||
expect(counts_monthly[:project_snippets]).to eq(2)
|
||||
expect(counts_monthly[:packages]).to eq(3)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -295,21 +295,15 @@ RSpec.describe Issuable do
|
|||
end
|
||||
|
||||
describe "#new?" do
|
||||
it "returns true when created today and record hasn't been updated" do
|
||||
allow(issue).to receive(:today?).and_return(true)
|
||||
it "returns false when created 30 hours ago" do
|
||||
allow(issue).to receive(:created_at).and_return(Time.current - 30.hours)
|
||||
expect(issue.new?).to be_falsey
|
||||
end
|
||||
|
||||
it "returns true when created 20 hours ago" do
|
||||
allow(issue).to receive(:created_at).and_return(Time.current - 20.hours)
|
||||
expect(issue.new?).to be_truthy
|
||||
end
|
||||
|
||||
it "returns false when not created today" do
|
||||
allow(issue).to receive(:today?).and_return(false)
|
||||
expect(issue.new?).to be_falsey
|
||||
end
|
||||
|
||||
it "returns false when record has been updated" do
|
||||
allow(issue).to receive(:today?).and_return(true)
|
||||
issue.update_attribute(:updated_at, 1.hour.ago)
|
||||
expect(issue.new?).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
describe "#sort_by_attribute" do
|
||||
|
|
|
@ -11,7 +11,7 @@ RSpec.describe BambooService, :use_clean_rails_memory_store_caching do
|
|||
let_it_be(:project) { create(:project) }
|
||||
|
||||
subject(:service) do
|
||||
described_class.create(
|
||||
described_class.create!(
|
||||
project: project,
|
||||
properties: {
|
||||
bamboo_url: bamboo_url,
|
||||
|
@ -85,7 +85,7 @@ RSpec.describe BambooService, :use_clean_rails_memory_store_caching do
|
|||
bamboo_service = service
|
||||
|
||||
bamboo_service.bamboo_url = 'http://gitlab1.com'
|
||||
bamboo_service.save
|
||||
bamboo_service.save!
|
||||
|
||||
expect(bamboo_service.password).to be_nil
|
||||
end
|
||||
|
@ -94,7 +94,7 @@ RSpec.describe BambooService, :use_clean_rails_memory_store_caching do
|
|||
bamboo_service = service
|
||||
|
||||
bamboo_service.username = 'some_name'
|
||||
bamboo_service.save
|
||||
bamboo_service.save!
|
||||
|
||||
expect(bamboo_service.password).to eq('password')
|
||||
end
|
||||
|
@ -104,7 +104,7 @@ RSpec.describe BambooService, :use_clean_rails_memory_store_caching do
|
|||
|
||||
bamboo_service.bamboo_url = 'http://gitlab_edited.com'
|
||||
bamboo_service.password = 'password'
|
||||
bamboo_service.save
|
||||
bamboo_service.save!
|
||||
|
||||
expect(bamboo_service.password).to eq('password')
|
||||
expect(bamboo_service.bamboo_url).to eq('http://gitlab_edited.com')
|
||||
|
@ -117,7 +117,7 @@ RSpec.describe BambooService, :use_clean_rails_memory_store_caching do
|
|||
|
||||
bamboo_service.bamboo_url = 'http://gitlab_edited.com'
|
||||
bamboo_service.password = 'password'
|
||||
bamboo_service.save
|
||||
bamboo_service.save!
|
||||
|
||||
expect(bamboo_service.password).to eq('password')
|
||||
expect(bamboo_service.bamboo_url).to eq('http://gitlab_edited.com')
|
||||
|
|
|
@ -9,7 +9,7 @@ RSpec.describe BuildkiteService, :use_clean_rails_memory_store_caching do
|
|||
let(:project) { create(:project) }
|
||||
|
||||
subject(:service) do
|
||||
described_class.create(
|
||||
described_class.create!(
|
||||
project: project,
|
||||
properties: {
|
||||
service_hook: true,
|
||||
|
|
|
@ -28,7 +28,7 @@ RSpec.describe JiraService do
|
|||
}
|
||||
end
|
||||
|
||||
let(:service) { described_class.create(options) }
|
||||
let(:service) { described_class.create!(options) }
|
||||
|
||||
it 'sets the URL properly' do
|
||||
# jira-ruby gem parses the URI and handles trailing slashes fine:
|
||||
|
@ -102,7 +102,7 @@ RSpec.describe JiraService do
|
|||
}
|
||||
end
|
||||
|
||||
subject { described_class.create(params) }
|
||||
subject { described_class.create!(params) }
|
||||
|
||||
it 'does not store data into properties' do
|
||||
expect(subject.properties).to be_nil
|
||||
|
@ -189,7 +189,7 @@ RSpec.describe JiraService do
|
|||
let_it_be(:new_url) { 'http://jira-new.example.com' }
|
||||
|
||||
before do
|
||||
service.update(username: new_username, url: new_url)
|
||||
service.update!(username: new_username, url: new_url)
|
||||
end
|
||||
|
||||
it 'leaves properties field emtpy' do
|
||||
|
@ -209,12 +209,12 @@ RSpec.describe JiraService do
|
|||
|
||||
context 'when updating the url, api_url, username, or password' do
|
||||
it 'updates deployment type' do
|
||||
service.update(url: 'http://first.url')
|
||||
service.jira_tracker_data.update(deployment_type: 'server')
|
||||
service.update!(url: 'http://first.url')
|
||||
service.jira_tracker_data.update!(deployment_type: 'server')
|
||||
|
||||
expect(service.jira_tracker_data.deployment_server?).to be_truthy
|
||||
|
||||
service.update(api_url: 'http://another.url')
|
||||
service.update!(api_url: 'http://another.url')
|
||||
service.jira_tracker_data.reload
|
||||
|
||||
expect(service.jira_tracker_data.deployment_cloud?).to be_truthy
|
||||
|
@ -222,25 +222,25 @@ RSpec.describe JiraService do
|
|||
end
|
||||
|
||||
it 'calls serverInfo for url' do
|
||||
service.update(url: 'http://first.url')
|
||||
service.update!(url: 'http://first.url')
|
||||
|
||||
expect(WebMock).to have_requested(:get, /serverInfo/)
|
||||
end
|
||||
|
||||
it 'calls serverInfo for api_url' do
|
||||
service.update(api_url: 'http://another.url')
|
||||
service.update!(api_url: 'http://another.url')
|
||||
|
||||
expect(WebMock).to have_requested(:get, /serverInfo/)
|
||||
end
|
||||
|
||||
it 'calls serverInfo for username' do
|
||||
service.update(username: 'test-user')
|
||||
service.update!(username: 'test-user')
|
||||
|
||||
expect(WebMock).to have_requested(:get, /serverInfo/)
|
||||
end
|
||||
|
||||
it 'calls serverInfo for password' do
|
||||
service.update(password: 'test-password')
|
||||
service.update!(password: 'test-password')
|
||||
|
||||
expect(WebMock).to have_requested(:get, /serverInfo/)
|
||||
end
|
||||
|
@ -248,7 +248,7 @@ RSpec.describe JiraService do
|
|||
|
||||
context 'when not updating the url, api_url, username, or password' do
|
||||
it 'does not update deployment type' do
|
||||
service.update(jira_issue_transition_id: 'jira_issue_transition_id')
|
||||
expect {service.update!(jira_issue_transition_id: 'jira_issue_transition_id')}.to raise_error(ActiveRecord::RecordInvalid)
|
||||
|
||||
expect(WebMock).not_to have_requested(:get, /serverInfo/)
|
||||
end
|
||||
|
@ -268,7 +268,7 @@ RSpec.describe JiraService do
|
|||
it 'resets password if url changed' do
|
||||
service
|
||||
service.url = 'http://jira_edited.example.com'
|
||||
service.save
|
||||
service.save!
|
||||
|
||||
expect(service.reload.url).to eq('http://jira_edited.example.com')
|
||||
expect(service.password).to be_nil
|
||||
|
@ -276,7 +276,7 @@ RSpec.describe JiraService do
|
|||
|
||||
it 'does not reset password if url "changed" to the same url as before' do
|
||||
service.url = 'http://jira.example.com'
|
||||
service.save
|
||||
service.save!
|
||||
|
||||
expect(service.reload.url).to eq('http://jira.example.com')
|
||||
expect(service.password).not_to be_nil
|
||||
|
@ -284,7 +284,7 @@ RSpec.describe JiraService do
|
|||
|
||||
it 'resets password if url not changed but api url added' do
|
||||
service.api_url = 'http://jira_edited.example.com/rest/api/2'
|
||||
service.save
|
||||
service.save!
|
||||
|
||||
expect(service.reload.api_url).to eq('http://jira_edited.example.com/rest/api/2')
|
||||
expect(service.password).to be_nil
|
||||
|
@ -293,7 +293,7 @@ RSpec.describe JiraService do
|
|||
it 'does not reset password if new url is set together with password, even if it\'s the same password' do
|
||||
service.url = 'http://jira_edited.example.com'
|
||||
service.password = password
|
||||
service.save
|
||||
service.save!
|
||||
|
||||
expect(service.password).to eq(password)
|
||||
expect(service.url).to eq('http://jira_edited.example.com')
|
||||
|
@ -302,14 +302,14 @@ RSpec.describe JiraService do
|
|||
it 'resets password if url changed, even if setter called multiple times' do
|
||||
service.url = 'http://jira1.example.com/rest/api/2'
|
||||
service.url = 'http://jira1.example.com/rest/api/2'
|
||||
service.save
|
||||
service.save!
|
||||
|
||||
expect(service.password).to be_nil
|
||||
end
|
||||
|
||||
it 'does not reset password if username changed' do
|
||||
service.username = 'some_name'
|
||||
service.save
|
||||
service.save!
|
||||
|
||||
expect(service.reload.password).to eq(password)
|
||||
end
|
||||
|
@ -317,7 +317,7 @@ RSpec.describe JiraService do
|
|||
it 'does not reset password if password changed' do
|
||||
service.url = 'http://jira_edited.example.com'
|
||||
service.password = 'new_password'
|
||||
service.save
|
||||
service.save!
|
||||
|
||||
expect(service.reload.password).to eq('new_password')
|
||||
end
|
||||
|
@ -325,7 +325,7 @@ RSpec.describe JiraService do
|
|||
it 'does not reset password if the password is touched and same as before' do
|
||||
service.url = 'http://jira_edited.example.com'
|
||||
service.password = password
|
||||
service.save
|
||||
service.save!
|
||||
|
||||
expect(service.reload.password).to eq(password)
|
||||
end
|
||||
|
@ -342,20 +342,20 @@ RSpec.describe JiraService do
|
|||
|
||||
it 'resets password if api url changed' do
|
||||
service.api_url = 'http://jira_edited.example.com/rest/api/2'
|
||||
service.save
|
||||
service.save!
|
||||
|
||||
expect(service.password).to be_nil
|
||||
end
|
||||
|
||||
it 'does not reset password if url changed' do
|
||||
service.url = 'http://jira_edited.example.com'
|
||||
service.save
|
||||
service.save!
|
||||
|
||||
expect(service.password).to eq(password)
|
||||
end
|
||||
|
||||
it 'resets password if api url set to empty' do
|
||||
service.update(api_url: '')
|
||||
service.update!(api_url: '')
|
||||
|
||||
expect(service.reload.password).to be_nil
|
||||
end
|
||||
|
@ -372,7 +372,7 @@ RSpec.describe JiraService do
|
|||
it 'saves password if new url is set together with password' do
|
||||
service.url = 'http://jira_edited.example.com/rest/api/2'
|
||||
service.password = 'password'
|
||||
service.save
|
||||
service.save!
|
||||
expect(service.reload.password).to eq('password')
|
||||
expect(service.reload.url).to eq('http://jira_edited.example.com/rest/api/2')
|
||||
end
|
||||
|
@ -441,7 +441,7 @@ RSpec.describe JiraService do
|
|||
allow_any_instance_of(JIRA::Resource::Issue).to receive(:key).and_return('JIRA-123')
|
||||
allow(JIRA::Resource::Remotelink).to receive(:all).and_return([])
|
||||
|
||||
@jira_service.save
|
||||
@jira_service.save!
|
||||
|
||||
project_issues_url = 'http://jira.example.com/rest/api/2/issue/JIRA-123'
|
||||
@transitions_url = 'http://jira.example.com/rest/api/2/issue/JIRA-123/transitions'
|
||||
|
@ -709,9 +709,11 @@ RSpec.describe JiraService do
|
|||
|
||||
describe '#test' do
|
||||
let(:server_info_results) { { 'url' => 'http://url', 'deploymentType' => 'Cloud' } }
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let(:jira_service) do
|
||||
described_class.new(
|
||||
url: url,
|
||||
project: project,
|
||||
username: username,
|
||||
password: password
|
||||
)
|
||||
|
@ -728,7 +730,7 @@ RSpec.describe JiraService do
|
|||
end
|
||||
|
||||
it 'gets Jira project with API URL if set' do
|
||||
jira_service.update(api_url: 'http://jira.api.com')
|
||||
jira_service.update!(api_url: 'http://jira.api.com')
|
||||
|
||||
expect(server_info).to eq(success: true, result: server_info_results)
|
||||
expect(WebMock).to have_requested(:get, /jira.api.com/)
|
||||
|
|
|
@ -33,7 +33,7 @@ RSpec.describe PackagistService do
|
|||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:push_sample_data) { Gitlab::DataBuilder::Push.build_sample(project, user) }
|
||||
let(:packagist_service) { described_class.create(packagist_params) }
|
||||
let(:packagist_service) { described_class.create!(packagist_params) }
|
||||
|
||||
before do
|
||||
stub_request(:post, packagist_hook_url)
|
||||
|
|
|
@ -81,7 +81,7 @@ RSpec.describe PipelinesEmailService, :mailer do
|
|||
context 'when pipeline is succeeded' do
|
||||
before do
|
||||
data[:object_attributes][:status] = 'success'
|
||||
pipeline.update(status: 'success')
|
||||
pipeline.update!(status: 'success')
|
||||
end
|
||||
|
||||
it_behaves_like 'sending email'
|
||||
|
@ -91,7 +91,7 @@ RSpec.describe PipelinesEmailService, :mailer do
|
|||
context 'on default branch' do
|
||||
before do
|
||||
data[:object_attributes][:ref] = project.default_branch
|
||||
pipeline.update(ref: project.default_branch)
|
||||
pipeline.update!(ref: project.default_branch)
|
||||
end
|
||||
|
||||
context 'notifications are enabled only for default branch' do
|
||||
|
@ -115,7 +115,7 @@ RSpec.describe PipelinesEmailService, :mailer do
|
|||
before do
|
||||
create(:protected_branch, project: project, name: 'a-protected-branch')
|
||||
data[:object_attributes][:ref] = 'a-protected-branch'
|
||||
pipeline.update(ref: 'a-protected-branch')
|
||||
pipeline.update!(ref: 'a-protected-branch')
|
||||
end
|
||||
|
||||
context 'notifications are enabled only for default branch' do
|
||||
|
@ -138,7 +138,7 @@ RSpec.describe PipelinesEmailService, :mailer do
|
|||
context 'on a neither protected nor default branch' do
|
||||
before do
|
||||
data[:object_attributes][:ref] = 'a-random-branch'
|
||||
pipeline.update(ref: 'a-random-branch')
|
||||
pipeline.update!(ref: 'a-random-branch')
|
||||
end
|
||||
|
||||
context 'notifications are enabled only for default branch' do
|
||||
|
@ -177,7 +177,7 @@ RSpec.describe PipelinesEmailService, :mailer do
|
|||
context 'with succeeded pipeline' do
|
||||
before do
|
||||
data[:object_attributes][:status] = 'success'
|
||||
pipeline.update(status: 'success')
|
||||
pipeline.update!(status: 'success')
|
||||
end
|
||||
|
||||
it_behaves_like 'not sending email'
|
||||
|
@ -195,7 +195,7 @@ RSpec.describe PipelinesEmailService, :mailer do
|
|||
context 'with succeeded pipeline' do
|
||||
before do
|
||||
data[:object_attributes][:status] = 'success'
|
||||
pipeline.update(status: 'success')
|
||||
pipeline.update!(status: 'success')
|
||||
end
|
||||
|
||||
it_behaves_like 'not sending email'
|
||||
|
@ -206,7 +206,7 @@ RSpec.describe PipelinesEmailService, :mailer do
|
|||
context 'on default branch' do
|
||||
before do
|
||||
data[:object_attributes][:ref] = project.default_branch
|
||||
pipeline.update(ref: project.default_branch)
|
||||
pipeline.update!(ref: project.default_branch)
|
||||
end
|
||||
|
||||
context 'notifications are enabled only for default branch' do
|
||||
|
@ -230,7 +230,7 @@ RSpec.describe PipelinesEmailService, :mailer do
|
|||
before do
|
||||
create(:protected_branch, project: project, name: 'a-protected-branch')
|
||||
data[:object_attributes][:ref] = 'a-protected-branch'
|
||||
pipeline.update(ref: 'a-protected-branch')
|
||||
pipeline.update!(ref: 'a-protected-branch')
|
||||
end
|
||||
|
||||
context 'notifications are enabled only for default branch' do
|
||||
|
@ -253,7 +253,7 @@ RSpec.describe PipelinesEmailService, :mailer do
|
|||
context 'on a neither protected nor default branch' do
|
||||
before do
|
||||
data[:object_attributes][:ref] = 'a-random-branch'
|
||||
pipeline.update(ref: 'a-random-branch')
|
||||
pipeline.update!(ref: 'a-random-branch')
|
||||
end
|
||||
|
||||
context 'notifications are enabled only for default branch' do
|
||||
|
@ -281,7 +281,7 @@ RSpec.describe PipelinesEmailService, :mailer do
|
|||
context 'with failed pipeline' do
|
||||
before do
|
||||
data[:object_attributes][:status] = 'failed'
|
||||
pipeline.update(status: 'failed')
|
||||
pipeline.update!(status: 'failed')
|
||||
end
|
||||
|
||||
it_behaves_like 'not sending email'
|
||||
|
@ -295,7 +295,7 @@ RSpec.describe PipelinesEmailService, :mailer do
|
|||
context 'with failed pipeline' do
|
||||
before do
|
||||
data[:object_attributes][:status] = 'failed'
|
||||
pipeline.update(status: 'failed')
|
||||
pipeline.update!(status: 'failed')
|
||||
end
|
||||
|
||||
it_behaves_like 'sending email'
|
||||
|
|
|
@ -11,7 +11,7 @@ RSpec.describe TeamcityService, :use_clean_rails_memory_store_caching do
|
|||
let(:project) { create(:project) }
|
||||
|
||||
subject(:service) do
|
||||
described_class.create(
|
||||
described_class.create!(
|
||||
project: project,
|
||||
properties: {
|
||||
teamcity_url: teamcity_url,
|
||||
|
@ -85,7 +85,7 @@ RSpec.describe TeamcityService, :use_clean_rails_memory_store_caching do
|
|||
teamcity_service = service
|
||||
|
||||
teamcity_service.teamcity_url = 'http://gitlab1.com'
|
||||
teamcity_service.save
|
||||
teamcity_service.save!
|
||||
|
||||
expect(teamcity_service.password).to be_nil
|
||||
end
|
||||
|
@ -94,7 +94,7 @@ RSpec.describe TeamcityService, :use_clean_rails_memory_store_caching do
|
|||
teamcity_service = service
|
||||
|
||||
teamcity_service.username = 'some_name'
|
||||
teamcity_service.save
|
||||
teamcity_service.save!
|
||||
|
||||
expect(teamcity_service.password).to eq('password')
|
||||
end
|
||||
|
@ -104,7 +104,7 @@ RSpec.describe TeamcityService, :use_clean_rails_memory_store_caching do
|
|||
|
||||
teamcity_service.teamcity_url = 'http://gitlab_edited.com'
|
||||
teamcity_service.password = 'password'
|
||||
teamcity_service.save
|
||||
teamcity_service.save!
|
||||
|
||||
expect(teamcity_service.password).to eq('password')
|
||||
expect(teamcity_service.teamcity_url).to eq('http://gitlab_edited.com')
|
||||
|
@ -117,7 +117,7 @@ RSpec.describe TeamcityService, :use_clean_rails_memory_store_caching do
|
|||
|
||||
teamcity_service.teamcity_url = 'http://gitlab_edited.com'
|
||||
teamcity_service.password = 'password'
|
||||
teamcity_service.save
|
||||
teamcity_service.save!
|
||||
|
||||
expect(teamcity_service.password).to eq('password')
|
||||
expect(teamcity_service.teamcity_url).to eq('http://gitlab_edited.com')
|
||||
|
|
|
@ -75,35 +75,10 @@ RSpec.describe Issues::CreateService do
|
|||
expect(Todo.where(attributes).count).to eq 1
|
||||
end
|
||||
|
||||
it 'rebalances if needed' do
|
||||
create(:issue, project: project, relative_position: RelativePositioning::MAX_POSITION)
|
||||
expect(IssueRebalancingWorker).to receive(:perform_async).with(nil, project.id)
|
||||
it 'moves the issue to the end, in an asynchronous worker' do
|
||||
expect(IssuePlacementWorker).to receive(:perform_async).with(Integer)
|
||||
|
||||
expect(issue.relative_position).to eq(project.issues.maximum(:relative_position))
|
||||
end
|
||||
|
||||
it 'does not rebalance if the flag is disabled' do
|
||||
stub_feature_flags(rebalance_issues: false)
|
||||
|
||||
create(:issue, project: project, relative_position: RelativePositioning::MAX_POSITION)
|
||||
expect(IssueRebalancingWorker).not_to receive(:perform_async)
|
||||
|
||||
expect(issue.relative_position).to eq(project.issues.maximum(:relative_position))
|
||||
end
|
||||
|
||||
it 'does rebalance if the flag is enabled for the project' do
|
||||
stub_feature_flags(rebalance_issues: project)
|
||||
|
||||
create(:issue, project: project, relative_position: RelativePositioning::MAX_POSITION)
|
||||
expect(IssueRebalancingWorker).to receive(:perform_async).with(nil, project.id)
|
||||
|
||||
expect(issue.relative_position).to eq(project.issues.maximum(:relative_position))
|
||||
end
|
||||
|
||||
it 'does not rebalance unless needed' do
|
||||
expect(IssueRebalancingWorker).not_to receive(:perform_async)
|
||||
|
||||
expect(issue.relative_position).to eq(project.issues.maximum(:relative_position))
|
||||
described_class.new(project, user, opts).execute
|
||||
end
|
||||
|
||||
context 'when label belongs to project group' do
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Issues::ReorderService do
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:user) { create_default(:user) }
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:project, reload: true) { create(:project, namespace: group) }
|
||||
|
||||
shared_examples 'issues reorder service' do
|
||||
context 'when reordering issues' do
|
||||
|
@ -14,7 +14,7 @@ RSpec.describe Issues::ReorderService do
|
|||
end
|
||||
|
||||
it 'returns false with both invalid params' do
|
||||
params = { move_after_id: nil, move_before_id: 1 }
|
||||
params = { move_after_id: nil, move_before_id: non_existing_record_id }
|
||||
|
||||
expect(service(params).execute(issue1)).to be_falsey
|
||||
end
|
||||
|
@ -27,27 +27,39 @@ RSpec.describe Issues::ReorderService do
|
|||
expect(issue1.relative_position)
|
||||
.to be_between(issue2.relative_position, issue3.relative_position)
|
||||
end
|
||||
|
||||
it 'sorts issues if only given one neighbour, on the left' do
|
||||
params = { move_before_id: issue3.id }
|
||||
|
||||
service(params).execute(issue1)
|
||||
|
||||
expect(issue1.relative_position).to be > issue3.relative_position
|
||||
end
|
||||
|
||||
it 'sorts issues if only given one neighbour, on the right' do
|
||||
params = { move_after_id: issue1.id }
|
||||
|
||||
service(params).execute(issue3)
|
||||
|
||||
expect(issue3.relative_position).to be < issue1.relative_position
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#execute' do
|
||||
let(:issue1) { create(:issue, project: project, relative_position: 10) }
|
||||
let(:issue2) { create(:issue, project: project, relative_position: 20) }
|
||||
let(:issue3) { create(:issue, project: project, relative_position: 30) }
|
||||
let_it_be(:issue1, reload: true) { create(:issue, project: project, relative_position: 10) }
|
||||
let_it_be(:issue2) { create(:issue, project: project, relative_position: 20) }
|
||||
let_it_be(:issue3, reload: true) { create(:issue, project: project, relative_position: 30) }
|
||||
|
||||
context 'when ordering issues in a project' do
|
||||
let(:parent) { project }
|
||||
|
||||
before do
|
||||
parent.add_developer(user)
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
it_behaves_like 'issues reorder service'
|
||||
end
|
||||
|
||||
context 'when ordering issues in a group' do
|
||||
let(:project) { create(:project, namespace: group) }
|
||||
|
||||
before do
|
||||
group.add_developer(user)
|
||||
end
|
||||
|
|
69
spec/workers/issue_placement_worker_spec.rb
Normal file
69
spec/workers/issue_placement_worker_spec.rb
Normal file
|
@ -0,0 +1,69 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe IssuePlacementWorker do
|
||||
describe '#perform' do
|
||||
let_it_be(:time) { Time.now.utc }
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:author) { create(:user) }
|
||||
let_it_be(:common_attrs) { { author: author, project: project } }
|
||||
let_it_be(:unplaced) { common_attrs.merge(relative_position: nil) }
|
||||
let_it_be(:issue) { create(:issue, **unplaced, created_at: time) }
|
||||
let_it_be(:issue_a) { create(:issue, **unplaced, created_at: time - 1.minute) }
|
||||
let_it_be(:issue_b) { create(:issue, **unplaced, created_at: time - 2.minutes) }
|
||||
let_it_be(:issue_c) { create(:issue, **unplaced, created_at: time + 1.minute) }
|
||||
let_it_be(:issue_d) { create(:issue, **unplaced, created_at: time + 2.minutes) }
|
||||
let_it_be(:issue_e) { create(:issue, **common_attrs, relative_position: 10, created_at: time + 1.minute) }
|
||||
let_it_be(:issue_f) { create(:issue, **unplaced, created_at: time + 1.minute) }
|
||||
|
||||
let_it_be(:irrelevant) { create(:issue, relative_position: nil, created_at: time) }
|
||||
|
||||
it 'places all issues created at most 5 minutes before this one at the end, most recent last' do
|
||||
expect do
|
||||
described_class.new.perform(issue.id)
|
||||
end.not_to change { irrelevant.reset.relative_position }
|
||||
|
||||
expect(project.issues.order_relative_position_asc)
|
||||
.to eq([issue_e, issue_b, issue_a, issue, issue_c, issue_f, issue_d])
|
||||
expect(project.issues.where(relative_position: nil)).not_to exist
|
||||
end
|
||||
|
||||
it 'schedules rebalancing if needed' do
|
||||
issue_a.update!(relative_position: RelativePositioning::MAX_POSITION)
|
||||
|
||||
expect(IssueRebalancingWorker).to receive(:perform_async).with(nil, project.id)
|
||||
|
||||
described_class.new.perform(issue.id)
|
||||
end
|
||||
|
||||
it 'limits the sweep to QUERY_LIMIT records' do
|
||||
# Ensure there are more than N issues in this set
|
||||
n = described_class::QUERY_LIMIT
|
||||
create_list(:issue, n - 5, **unplaced)
|
||||
|
||||
expect(Issue).to receive(:move_nulls_to_end).with(have_attributes(count: n)).and_call_original
|
||||
|
||||
described_class.new.perform(issue.id)
|
||||
|
||||
expect(project.issues.where(relative_position: nil)).to exist
|
||||
end
|
||||
|
||||
it 'anticipates the failure to find the issue' do
|
||||
id = non_existing_record_id
|
||||
|
||||
expect { described_class.new.perform(id) }.not_to raise_error
|
||||
end
|
||||
|
||||
it 'anticipates the failure to place the issues, and schedules rebalancing' do
|
||||
allow(Issue).to receive(:move_nulls_to_end) { raise RelativePositioning::NoSpaceLeft }
|
||||
|
||||
expect(IssueRebalancingWorker).to receive(:perform_async).with(nil, project.id)
|
||||
expect(Gitlab::ErrorTracking)
|
||||
.to receive(:log_exception)
|
||||
.with(RelativePositioning::NoSpaceLeft, issue_id: issue.id)
|
||||
|
||||
described_class.new.perform(issue.id)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -39,10 +39,10 @@ RSpec.describe NewIssueWorker do
|
|||
end
|
||||
|
||||
context 'when everything is ok' do
|
||||
let(:project) { create(:project, :public) }
|
||||
let(:mentioned) { create(:user) }
|
||||
let(:user) { create(:user) }
|
||||
let(:issue) { create(:issue, project: project, description: "issue for #{mentioned.to_reference}") }
|
||||
let_it_be(:user) { create_default(:user) }
|
||||
let_it_be(:project) { create(:project, :public) }
|
||||
let_it_be(:mentioned) { create(:user) }
|
||||
let_it_be(:issue) { create(:issue, project: project, description: "issue for #{mentioned.to_reference}") }
|
||||
|
||||
it 'creates a new event record' do
|
||||
expect { worker.perform(issue.id, user.id) }.to change { Event.count }.from(0).to(1)
|
||||
|
|
72
yarn.lock
72
yarn.lock
|
@ -1076,56 +1076,56 @@
|
|||
resolved "https://registry.yarnpkg.com/@rails/actioncable/-/actioncable-6.0.3-1.tgz#9b9eb8858a6507162911007d355d9a206e1c5caa"
|
||||
integrity sha512-szFhWD+V5TAxVNVIG16klgq+ypqA5k5AecLarTTrXgOG8cawVbQdOAwLbCmzkwiQ60rGSxAFoC1u2LrzxSK2Aw==
|
||||
|
||||
"@sentry/browser@^5.10.2":
|
||||
version "5.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-5.10.2.tgz#0bbb05505c58ea998c833cffec3f922fe4b4fa58"
|
||||
integrity sha512-r3eyBu2ln7odvWtXARCZPzpuGrKsD6U9F3gKTu4xdFkA0swSLUvS7AC2FUksj/1BE23y+eB/zzPT+RYJ58tidA==
|
||||
"@sentry/browser@^5.22.3":
|
||||
version "5.22.3"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-5.22.3.tgz#7a64bd1cf01bf393741a3e4bf35f82aa927f5b4e"
|
||||
integrity sha512-2TzE/CoBa5ZkvxJizDdi1Iz1ldmXSJpFQ1mL07PIXBjCt0Wxf+WOuFSj5IP4L40XHfJE5gU8wEvSH0VDR8nXtA==
|
||||
dependencies:
|
||||
"@sentry/core" "5.10.2"
|
||||
"@sentry/types" "5.10.0"
|
||||
"@sentry/utils" "5.10.2"
|
||||
"@sentry/core" "5.22.3"
|
||||
"@sentry/types" "5.22.3"
|
||||
"@sentry/utils" "5.22.3"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/core@5.10.2":
|
||||
version "5.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.10.2.tgz#1cb64489e6f8363c3249415b49d3f1289814825f"
|
||||
integrity sha512-sKVeFH3v8K8xw2vM5MKMnnyAAwih+JSE3pbNL0CcCCA+/SwX+3jeAo2BhgXev2SAR/TjWW+wmeC9TdIW7KyYbg==
|
||||
"@sentry/core@5.22.3":
|
||||
version "5.22.3"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.22.3.tgz#030f435f2b518f282ba8bd954dac90cd70888bd7"
|
||||
integrity sha512-eGL5uUarw3o4i9QUb9JoFHnhriPpWCaqeaIBB06HUpdcvhrjoowcKZj1+WPec5lFg5XusE35vez7z/FPzmJUDw==
|
||||
dependencies:
|
||||
"@sentry/hub" "5.10.2"
|
||||
"@sentry/minimal" "5.10.2"
|
||||
"@sentry/types" "5.10.0"
|
||||
"@sentry/utils" "5.10.2"
|
||||
"@sentry/hub" "5.22.3"
|
||||
"@sentry/minimal" "5.22.3"
|
||||
"@sentry/types" "5.22.3"
|
||||
"@sentry/utils" "5.22.3"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/hub@5.10.2":
|
||||
version "5.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.10.2.tgz#25d9f36b8f7c5cb65cf486737fa61dc9bf69b7e3"
|
||||
integrity sha512-hSlZIiu3hcR/I5yEhlpN9C0nip+U7hiRzRzUQaBiHO4YG4TC58NqnOPR89D/ekiuHIXzFpjW9OQmqtAMRoSUYA==
|
||||
"@sentry/hub@5.22.3":
|
||||
version "5.22.3"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.22.3.tgz#08309a70d2ea8d5e313d05840c1711f34f2fffe5"
|
||||
integrity sha512-INo47m6N5HFEs/7GMP9cqxOIt7rmRxdERunA3H2L37owjcr77MwHVeeJ9yawRS6FMtbWXplgWTyTIWIYOuqVbw==
|
||||
dependencies:
|
||||
"@sentry/types" "5.10.0"
|
||||
"@sentry/utils" "5.10.2"
|
||||
"@sentry/types" "5.22.3"
|
||||
"@sentry/utils" "5.22.3"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/minimal@5.10.2":
|
||||
version "5.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.10.2.tgz#267c2f3aa6877a0fe7a86971942e83f3ee616580"
|
||||
integrity sha512-GalixiM9sckYfompH5HHTp9XT2BcjawBkcl1DMEKUBEi37+kUq0bivOBmnN1G/I4/wWOUdnAI/kagDWaWpbZPg==
|
||||
"@sentry/minimal@5.22.3":
|
||||
version "5.22.3"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.22.3.tgz#706e4029ae5494123d3875c658ba8911aa5cc440"
|
||||
integrity sha512-HoINpYnVYCpNjn2XIPIlqH5o4BAITpTljXjtAftOx6Hzj+Opjg8tR8PWliyKDvkXPpc4kXK9D6TpEDw8MO0wZA==
|
||||
dependencies:
|
||||
"@sentry/hub" "5.10.2"
|
||||
"@sentry/types" "5.10.0"
|
||||
"@sentry/hub" "5.22.3"
|
||||
"@sentry/types" "5.22.3"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/types@5.10.0":
|
||||
version "5.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.10.0.tgz#4f0ba31b6e4d5371112c38279f11f66c73b43746"
|
||||
integrity sha512-TW20GzkCWsP6uAxR2JIpIkiitCKyIOfkyDsKBeLqYj4SaZjfvBPnzgNCcYR0L0UsP1/Es6oHooZfIGSkp6GGxQ==
|
||||
"@sentry/types@5.22.3":
|
||||
version "5.22.3"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.22.3.tgz#d1d547b30ee8bd7771fa893af74c4f3d71f0fd18"
|
||||
integrity sha512-cv+VWK0YFgCVDvD1/HrrBWOWYG3MLuCUJRBTkV/Opdy7nkdNjhCAJQrEyMM9zX0sac8FKWKOHT0sykNh8KgmYw==
|
||||
|
||||
"@sentry/utils@5.10.2":
|
||||
version "5.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.10.2.tgz#261f575079d30aaf604e59f5f4de0aa21db22252"
|
||||
integrity sha512-UcbbaFpYrGSV448lQ16Cr+W/MPuKUflQQUdrMCt5vgaf5+M7kpozlcji4GGGZUCXIA7oRP93ABoXj55s1OM9zw==
|
||||
"@sentry/utils@5.22.3":
|
||||
version "5.22.3"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.22.3.tgz#e3bda3e789239eb16d436f768daa12829f33d18f"
|
||||
integrity sha512-AHNryXMBvIkIE+GQxTlmhBXD0Ksh+5w1SwM5qi6AttH+1qjWLvV6WB4+4pvVvEoS8t5F+WaVUZPQLmCCWp6zKw==
|
||||
dependencies:
|
||||
"@sentry/types" "5.10.0"
|
||||
"@sentry/types" "5.22.3"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sindresorhus/is@^0.14.0":
|
||||
|
|
Loading…
Reference in a new issue