Commit 8e94dad3 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent ab128cc1
import Vue from 'vue';
import ConfirmModal from '~/vue_shared/components/confirm_modal.vue';
const mountConfirmModal = button => {
const props = {
path: button.dataset.path,
method: button.dataset.method,
modalAttributes: JSON.parse(button.dataset.modalAttributes),
};
const mountConfirmModal = () => {
return new Vue({
render(h) {
return h(ConfirmModal, { props });
data() {
return {
path: '',
method: '',
modalAttributes: null,
showModal: false,
};
},
}).$mount();
};
export default () => {
document.getElementsByClassName('js-confirm-modal-button').forEach(button => {
mounted() {
document.querySelectorAll('.js-confirm-modal-button').forEach(button => {
button.addEventListener('click', e => {
e.preventDefault();
mountConfirmModal(button);
this.path = button.dataset.path;
this.method = button.dataset.method;
this.modalAttributes = JSON.parse(button.dataset.modalAttributes);
this.showModal = true;
});
});
},
methods: {
dismiss() {
this.showModal = false;
},
},
render(h) {
return h(ConfirmModal, {
props: {
path: this.path,
method: this.method,
modalAttributes: this.modalAttributes,
showModal: this.showModal,
},
on: { dismiss: this.dismiss },
});
},
}).$mount();
};
export default () => mountConfirmModal();
import _ from 'underscore';
import { last } from 'lodash';
import FilteredSearchContainer from './container';
import FilteredSearchTokenizer from './filtered_search_tokenizer';
import FilteredSearchDropdownManager from './filtered_search_dropdown_manager';
......@@ -70,11 +70,11 @@ export default class DropdownUtils {
if (!allowMultiple && itemInExistingTokens) {
updatedItem.droplab_hidden = true;
} else if (!isSearchItem && (!lastKey || _.last(searchInput.split('')) === ' ')) {
} else if (!isSearchItem && (!lastKey || last(searchInput.split('')) === ' ')) {
updatedItem.droplab_hidden = false;
} else if (lastKey) {
const split = lastKey.split(':');
const tokenName = _.last(split[0].split(' '));
const tokenName = last(split[0].split(' '));
const match = isSearchItem
? allowedKeys.some(key => key.startsWith(tokenName.toLowerCase()))
......@@ -129,7 +129,7 @@ export default class DropdownUtils {
const values = [];
if (untilInput) {
const inputIndex = _.findIndex(tokens, t => t.classList.contains('input-token'));
const inputIndex = tokens.findIndex(t => t.classList.contains('input-token'));
// Add one to include input-token to the tokens array
tokens.splice(inputIndex + 1);
}
......
import { last } from 'lodash';
import AvailableDropdownMappings from 'ee_else_ce/filtered_search/available_dropdown_mappings';
import _ from 'underscore';
import DropLab from '~/droplab/drop_lab';
import FilteredSearchContainer from './container';
import FilteredSearchTokenKeys from './filtered_search_token_keys';
......@@ -184,8 +184,8 @@ export default class FilteredSearchDropdownManager {
// Eg. token = 'label:'
const split = lastToken.split(':');
const dropdownName = _.last(split[0].split(' '));
const possibleOperatorToken = _.last(split[1]);
const dropdownName = last(split[0].split(' '));
const possibleOperatorToken = last(split[1]);
const hasOperator = FilteredSearchVisualTokens.permissibleOperatorValues.includes(
possibleOperatorToken && possibleOperatorToken.trim(),
......
import _ from 'underscore';
import { last } from 'lodash';
import recentSearchesStorageKeys from 'ee_else_ce/filtered_search/recent_searches_storage_keys';
import { getParameterByName, getUrlParamsArray } from '~/lib/utils/common_utils';
import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
......@@ -456,7 +456,7 @@ export default class FilteredSearchManager {
if (fragments.length > 1) {
const inputValues = fragments[0].split(' ');
const tokenKey = _.last(inputValues);
const tokenKey = last(inputValues);
if (inputValues.length > 1) {
inputValues.pop();
......
import { flatten } from 'underscore';
import { flattenDeep } from 'lodash';
import FilteredSearchTokenKeys from './filtered_search_token_keys';
import { __ } from '~/locale';
......@@ -73,7 +73,7 @@ export const alternativeTokenKeys = [
},
];
export const conditions = flatten(
export const conditions = flattenDeep(
[
{
url: 'assignee_id=None',
......
import _ from 'underscore';
import { uniq } from 'lodash';
class RecentSearchesStore {
constructor(initialState = {}, allowedKeys) {
......@@ -20,7 +20,7 @@ class RecentSearchesStore {
setRecentSearches(searches = []) {
const trimmedSearches = searches.map(search => search.trim());
this.state.recentSearches = _.uniq(trimmedSearches).slice(0, 5);
this.state.recentSearches = uniq(trimmedSearches).slice(0, 5);
return this.state.recentSearches;
}
}
......
import _ from 'underscore';
import { escape as esc } from 'lodash';
import { USER_TOKEN_TYPES } from 'ee_else_ce/filtered_search/constants';
import FilteredSearchContainer from '~/filtered_search/container';
import FilteredSearchVisualTokens from '~/filtered_search/filtered_search_visual_tokens';
......@@ -48,7 +48,7 @@ export default class VisualTokenValue {
tokenValueContainer.dataset.originalValue = tokenValue;
tokenValueElement.innerHTML = `
<img class="avatar s20" src="${user.avatar_url}" alt="">
${_.escape(user.name)}
${esc(user.name)}
`;
/* eslint-enable no-param-reassign */
})
......
......@@ -9,34 +9,43 @@ export default {
props: {
modalAttributes: {
type: Object,
required: true,
required: false,
default: () => {
return {};
},
},
path: {
type: String,
required: true,
required: false,
default: '',
},
method: {
type: String,
required: true,
required: false,
default: '',
},
showModal: {
type: Boolean,
required: false,
default: false,
},
data() {
return {
isDismissed: false,
};
},
mounted() {
watch: {
showModal(val) {
if (val) {
// Wait for v-if to render
this.$nextTick(() => {
this.openModal();
});
}
},
},
methods: {
openModal() {
this.$refs.modal.show();
},
submitModal() {
this.$refs.form.requestSubmit();
},
dismiss() {
this.isDismissed = true;
this.$refs.form.submit();
},
},
csrf,
......@@ -45,11 +54,11 @@ export default {
<template>
<gl-modal
v-if="!isDismissed"
v-if="showModal"
ref="modal"
v-bind="modalAttributes"
@primary="submitModal"
@canceled="dismiss"
@canceled="$emit('dismiss')"
>
<form ref="form" :action="path" method="post">
<!-- Rails workaround for <form method="delete" />
......
......@@ -3,8 +3,8 @@
module Clusters
module Applications
class CertManager < ApplicationRecord
VERSION = 'v0.9.1'
CRD_VERSION = '0.9'
VERSION = 'v0.10.1'
CRD_VERSION = '0.10'
self.table_name = 'clusters_applications_cert_managers'
......
---
title: Ensure RepositoryLinkFilter handles Gitaly failures gracefully
merge_request: 26531
author:
type: performance
---
title: Fixed bug where processing NuGet packages are returned from the Packages API
merge_request: 26270
author:
type: fixed
---
title: Support Rails 6 `insert_all!`
merge_request: 26595
author:
type: fixed
---
title: Use cert-manager 0.10 instead of 0.9 for new chart installations
merge_request: 26345
author:
type: changed
......@@ -70,14 +70,9 @@ module API
params do
requires :repository_id, type: Integer, desc: 'The ID of the repository'
optional :name_regex_delete, type: String, desc: 'The tag name regexp to delete, specify .* to delete all'
# require either name_regex (deprecated) or name_regex_delete, it is ok to have both
given name_regex_delete: ->(val) { val.nil? } do
requires :name_regex, type: String, desc: 'The tag name regexp to delete, specify .* to delete all'
end
optional :name_regex, type: String, desc: 'The tag name regexp to delete, specify .* to delete all'
given name_regex: ->(val) { val.nil? } do
requires :name_regex_delete, type: String, desc: 'The tag name regexp to delete, specify .* to delete all'
end
# require either name_regex (deprecated) or name_regex_delete, it is ok to have both
at_least_one_of :name_regex, :name_regex_delete
optional :name_regex_keep, type: String, desc: 'The tag name regexp to retain'
optional :keep_n, type: Integer, desc: 'Keep n of latest tags with matching name'
optional :older_than, type: String, desc: 'Delete older than: 1h, 1d, 1month'
......
......@@ -80,6 +80,13 @@ module Banzai
end
Gitlab::GitalyClient::BlobService.new(repository).get_blob_types(revision_paths, 1)
rescue GRPC::Unavailable, GRPC::DeadlineExceeded => e
# Handle Gitaly connection issues gracefully
Gitlab::ErrorTracking.track_exception(e, project_id: project.id)
# Return all links as blob types
paths.collect do |path|
[path, :blob]
end
end
def get_uri(html_attr)
......
......@@ -9250,6 +9250,12 @@ msgstr ""
msgid "Geo|Remove"
msgstr ""
msgid "Geo|Remove entry"
msgstr ""
msgid "Geo|Remove tracking database entry"
msgstr ""
msgid "Geo|Repository sync capacity"
msgstr ""
......@@ -9301,13 +9307,13 @@ msgstr ""
msgid "Geo|This is a primary node"
msgstr ""
msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
msgid "Geo|Tracking database entry will be removed. Are you sure?"
msgstr ""
msgid "Geo|Tracking entry for upload (%{type}/%{id}) was successfully removed."
msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
msgstr ""
msgid "Geo|Tracking entry will be removed. Are you sure?"
msgid "Geo|Tracking entry for upload (%{type}/%{id}) was successfully removed."
msgstr ""
msgid "Geo|URL"
......
HTMLFormElement.prototype.submit = jest.fn();
import './element_scroll_into_view';
import './form_element';
import './get_client_rects';
import './inner_text';
import './window_scroll_to';
......
......@@ -3,6 +3,8 @@ import { GlModal } from '@gitlab/ui';
import { TEST_HOST } from 'helpers/test_constants';
import ConfirmModal from '~/vue_shared/components/confirm_modal.vue';
jest.mock('~/lib/utils/csrf', () => ({ token: 'test-csrf-token' }));
describe('vue_shared/components/confirm_modal', () => {
const testModalProps = {
path: `${TEST_HOST}/1`,
......@@ -39,45 +41,61 @@ describe('vue_shared/components/confirm_modal', () => {
});
const findModal = () => wrapper.find(GlModal);
const findForm = () => wrapper.find('form');
const findFormData = () =>
findForm()
.findAll('input')
.wrappers.map(x => ({ name: x.attributes('name'), value: x.attributes('value') }));
describe('template', () => {
describe('when showModal is false', () => {
beforeEach(() => {
createComponent();
});
it('calls openModal on mount', () => {
expect(actionSpies.openModal).toHaveBeenCalled();
it('does not render GlModal', () => {
expect(findModal().exists()).toBeFalsy();
});
});
describe('when showModal is true', () => {
beforeEach(() => {
createComponent({ showModal: true });
});
it('renders GlModal', () => {
expect(findModal().exists()).toBeTruthy();
expect(findModal().attributes()).toEqual(
expect.objectContaining({
modalid: testModalProps.modalAttributes.modalId,
oktitle: testModalProps.modalAttributes.okTitle,
okvariant: testModalProps.modalAttributes.okVariant,
}),
);
});
});
});
describe('methods', () => {
beforeEach(() => {
createComponent();
createComponent({ showModal: true });
});
describe('submitModal', () => {
beforeEach(() => {
wrapper.vm.$refs.form.requestSubmit = jest.fn();
it('does not submit form', () => {
expect(findForm().element.submit).not.toHaveBeenCalled();
});
it('calls requestSubmit', () => {
wrapper.vm.submitModal();
expect(wrapper.vm.$refs.form.requestSubmit).toHaveBeenCalled();
});
describe('when modal submitted', () => {
beforeEach(() => {
findModal().vm.$emit('primary');
});
describe('dismiss', () => {
it('removes gl-modal', () => {
expect(findModal().exists()).toBeTruthy();
wrapper.vm.dismiss();
return wrapper.vm.$nextTick(() => {
expect(findModal().exists()).toBeFalsy();
});
it('submits form', () => {
expect(findFormData()).toEqual([
{ name: '_method', value: testModalProps.method },
{ name: 'authenticity_token', value: 'test-csrf-token' },
]);
expect(findForm().element.submit).toHaveBeenCalled();
});
});
});
......
......@@ -149,6 +149,34 @@ describe Banzai::Filter::RepositoryLinkFilter do
end
shared_examples :valid_repository do
it 'handles Gitaly unavailable exceptions gracefully' do
allow_next_instance_of(Gitlab::GitalyClient::BlobService) do |blob_service|
allow(blob_service).to receive(:get_blob_types).and_raise(GRPC::Unavailable)
end
expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
an_instance_of(GRPC::Unavailable), project_id: project.id
)
doc = ""
expect { doc = filter(link('doc/api/README.md')) }.not_to raise_error
expect(doc.at_css('a')['href'])
.to eq "/#{project_path}/-/blob/#{ref}/doc/api/README.md"
end
it 'handles Gitaly timeout exceptions gracefully' do
allow_next_instance_of(Gitlab::GitalyClient::BlobService) do |blob_service|
allow(blob_service).to receive(:get_blob_types).and_raise(GRPC::DeadlineExceeded)
end
expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
an_instance_of(GRPC::DeadlineExceeded), project_id: project.id
)
doc = ""
expect { doc = filter(link('doc/api/README.md')) }.not_to raise_error
expect(doc.at_css('a')['href'])
.to eq "/#{project_path}/-/blob/#{ref}/doc/api/README.md"
end
it 'rebuilds absolute URL for a file in the repo' do
doc = filter(link('/doc/api/README.md'))
expect(doc.at_css('a')['href'])
......
......@@ -46,11 +46,11 @@ describe Clusters::Applications::CertManager do
expect(subject.name).to eq('certmanager')
expect(subject.chart).to eq('certmanager/cert-manager')
expect(subject.repository).to eq('https://charts.jetstack.io')
expect(subject.version).to eq('v0.9.1')
expect(subject.version).to eq('v0.10.1')
expect(subject).to be_rbac
expect(subject.files).to eq(cert_manager.files.merge(cluster_issuer_file))
expect(subject.preinstall).to eq([
'kubectl apply -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.9/deploy/manifests/00-crds.yaml',
'kubectl apply -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.10/deploy/manifests/00-crds.yaml',
'kubectl label --overwrite namespace gitlab-managed-apps certmanager.k8s.io/disable-validation=true'
])
expect(subject.postinstall).to eq([
......@@ -82,7 +82,7 @@ describe Clusters::Applications::CertManager do
let(:cert_manager) { create(:clusters_applications_cert_manager, :errored, version: '0.0.1') }
it 'is initialized with the locked version' do
expect(subject.version).to eq('v0.9.1')
expect(subject.version).to eq('v0.10.1')
end
end
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment