Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-11-18 09:10:16 +00:00
parent e9ae9c5f2e
commit f7e83434e3
23 changed files with 123 additions and 89 deletions

View file

@ -39,6 +39,7 @@ export default {
assigneeUsername,
search,
milestoneTitle,
iterationId,
types,
weight,
epicId,
@ -83,6 +84,13 @@ export default {
});
}
if (iterationId) {
filteredSearchValue.push({
type: 'iteration',
value: { data: iterationId, operator: '=' },
});
}
if (weight) {
filteredSearchValue.push({
type: 'weight',
@ -118,6 +126,13 @@ export default {
});
}
if (this.filterParams['not[iteration_id]']) {
filteredSearchValue.push({
type: 'iteration_id',
value: { data: this.filterParams['not[iteration_id]'], operator: '!=' },
});
}
if (this.filterParams['not[weight]']) {
filteredSearchValue.push({
type: 'weight',
@ -179,8 +194,8 @@ export default {
weight,
epicId,
myReactionEmoji,
iterationId,
} = this.filterParams;
let notParams = {};
if (Object.prototype.hasOwnProperty.call(this.filterParams, 'not')) {
@ -194,6 +209,7 @@ export default {
'not[weight]': this.filterParams.not.weight,
'not[epic_id]': this.filterParams.not.epicId,
'not[my_reaction_emoji]': this.filterParams.not.myReactionEmoji,
'not[iteration_id]': this.filterParams.not.iterationId,
},
undefined,
);
@ -205,6 +221,7 @@ export default {
'label_name[]': labelName,
assignee_username: assigneeUsername,
milestone_title: milestoneTitle,
iteration_id: iterationId,
search,
types,
weight,
@ -261,6 +278,9 @@ export default {
case 'milestone_title':
filterParams.milestoneTitle = filter.value.data;
break;
case 'iteration':
filterParams.iterationId = filter.value.data;
break;
case 'weight':
filterParams.weight = filter.value.data;
break;

View file

@ -12,6 +12,7 @@ import { __ } from '~/locale';
import {
DEFAULT_MILESTONES_GRAPHQL,
TOKEN_TITLE_MY_REACTION,
OPERATOR_IS_AND_IS_NOT,
} from '~/vue_shared/components/filtered_search_bar/constants';
import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
import EmojiToken from '~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue';
@ -35,8 +36,6 @@ export default {
issue: __('Issue'),
milestone: __('Milestone'),
weight: __('Weight'),
is: __('is'),
isNot: __('is not'),
},
components: { BoardFilteredSearch },
inject: ['isSignedIn'],
@ -62,8 +61,6 @@ export default {
tokensCE() {
const {
label,
is,
isNot,
author,
assignee,
issue,
@ -84,10 +81,7 @@ export default {
icon: 'user',
title: assignee,
type: 'assignee_username',
operators: [
{ value: '=', description: is },
{ value: '!=', description: isNot },
],
operators: OPERATOR_IS_AND_IS_NOT,
token: AuthorToken,
unique: true,
fetchAuthors,
@ -97,10 +91,7 @@ export default {
icon: 'pencil',
title: author,
type: 'author_username',
operators: [
{ value: '=', description: is },
{ value: '!=', description: isNot },
],
operators: OPERATOR_IS_AND_IS_NOT,
symbol: '@',
token: AuthorToken,
unique: true,
@ -111,10 +102,7 @@ export default {
icon: 'labels',
title: label,
type: 'label_name',
operators: [
{ value: '=', description: is },
{ value: '!=', description: isNot },
],
operators: OPERATOR_IS_AND_IS_NOT,
token: LabelToken,
unique: false,
symbol: '~',

View file

@ -373,7 +373,6 @@ export default {
commit(types.REQUEST_ITEMS_FOR_LIST, { listId, fetchNext });
const { fullPath, fullBoardId, boardType, filterParams } = state;
const variables = {
fullPath,
boardId: fullBoardId,

View file

@ -1,7 +1,6 @@
<script>
import { GlDropdownDivider, GlDropdownSectionHeader, GlFilteredSearchSuggestion } from '@gitlab/ui';
import createFlash from '~/flash';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { __ } from '~/locale';
import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
import { formatDate } from '~/lib/utils/datetime_utility';
@ -43,7 +42,7 @@ export default {
},
methods: {
getActiveIteration(iterations, data) {
return iterations.find((iteration) => this.getValue(iteration) === data);
return iterations.find((iteration) => iteration.id === data);
},
groupIterationsByCadence(iterations) {
const cadences = [];
@ -80,9 +79,6 @@ export default {
this.loading = false;
});
},
getValue(iteration) {
return String(getIdFromGraphQLId(iteration.id));
},
/**
* TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/344619
* This method also exists as a utility function in ee/../iterations/utils.js
@ -125,7 +121,7 @@ export default {
<gl-filtered-search-suggestion
v-for="iteration in cadence.iterations"
:key="iteration.id"
:value="getValue(iteration)"
:value="iteration.id"
>
{{ iteration.title }}
<div v-if="glFeatures.iterationCadences" class="gl-text-gray-400">

View file

@ -179,6 +179,8 @@ export default {
document.addEventListener('mousedown', this.handleDocumentMousedown);
document.addEventListener('click', this.handleDocumentClick);
this.updateLabelsSetState();
},
beforeDestroy() {
document.removeEventListener('mousedown', this.handleDocumentMousedown);

View file

@ -341,3 +341,5 @@ module NotesActions
noteable.discussions_rendered_on_frontend?
end
end
NotesActions.prepend_mod_with('NotesActions')

View file

@ -1,11 +0,0 @@
# frozen_string_literal: true
module Admin
class PropagateServiceTemplate
include PropagateService
def propagate
# TODO: Remove this as part of https://gitlab.com/gitlab-org/gitlab/-/issues/335178
end
end
end

View file

@ -1,37 +0,0 @@
# frozen_string_literal: true
module Admin
module PropagateService
extend ActiveSupport::Concern
BATCH_SIZE = 10_000
class_methods do
def propagate(integration)
new(integration).propagate
end
end
def initialize(integration)
@integration = integration
end
private
attr_reader :integration
def create_integration_for_projects_without_integration
propagate_integrations(
Project.without_integration(integration),
PropagateIntegrationProjectWorker
)
end
def propagate_integrations(relation, worker_class)
relation.each_batch(of: BATCH_SIZE) do |records|
min_id, max_id = records.pick("MIN(#{relation.table_name}.id), MAX(#{relation.table_name}.id)")
worker_class.perform_async(integration.id, min_id, max_id)
end
end
end
end

View file

@ -1,8 +1,12 @@
# frozen_string_literal: true
module Admin
class PropagateIntegrationService
include PropagateService
module Integrations
class PropagateService
BATCH_SIZE = 10_000
def initialize(integration)
@integration = integration
end
def propagate
if integration.instance_level?
@ -16,8 +20,21 @@ module Admin
end
end
def self.propagate(integration)
new(integration).propagate
end
private
attr_reader :integration
def create_integration_for_projects_without_integration
propagate_integrations(
Project.without_integration(integration),
PropagateIntegrationProjectWorker
)
end
def update_inherited_integrations
propagate_integrations(
Integration.by_type(integration.type).inherit_from_id(integration.id),
@ -52,5 +69,12 @@ module Admin
PropagateIntegrationProjectWorker
)
end
def propagate_integrations(relation, worker_class)
relation.each_batch(of: BATCH_SIZE) do |records|
min_id, max_id = records.pick("MIN(#{relation.table_name}.id), MAX(#{relation.table_name}.id)")
worker_class.perform_async(integration.id, min_id, max_id)
end
end
end
end

View file

@ -0,0 +1,10 @@
# frozen_string_literal: true
module Integrations
# TODO: Remove this as part of https://gitlab.com/gitlab-org/gitlab/-/issues/335178
class PropagateTemplateService
def self.propagate(_integration)
# no-op
end
end
end

View file

@ -5,7 +5,7 @@
- div_class = no_issuable_templates ? 'col-sm-10' : 'col-sm-7 col-lg-8'
- toggle_wip_link_start = '<a href="" class="js-toggle-wip">'
- toggle_wip_link_end = '</a>'
- add_wip_text = (_('%{link_start}Start the title with %{draft_snippet}%{link_end} to prevent a merge request that is a work in progress from being merged before it\'s ready.') % { link_start: toggle_wip_link_start, link_end: toggle_wip_link_end, draft_snippet: '<code>Draft:</code>'.html_safe } ).html_safe
- add_wip_text = (_('%{link_start}Start the title with %{draft_snippet}%{link_end} to prevent a merge request draft from merging before it\'s ready.') % { link_start: toggle_wip_link_start, link_end: toggle_wip_link_end, draft_snippet: '<code>Draft:</code>'.html_safe } ).html_safe
- remove_wip_text = (_('%{link_start}Remove the %{draft_snippet} prefix%{link_end} from the title to allow this merge request to be merged when it\'s ready.' ) % { link_start: toggle_wip_link_start, link_end: toggle_wip_link_end, draft_snippet: '<code>Draft</code>'.html_safe } ).html_safe
%div{ class: div_class }

View file

@ -12,6 +12,6 @@ class PropagateIntegrationWorker
idempotent!
def perform(integration_id)
Admin::PropagateIntegrationService.propagate(Integration.find(integration_id))
::Integrations::PropagateService.propagate(Integration.find(integration_id))
end
end

View file

@ -16,7 +16,7 @@ class PropagateServiceTemplateWorker # rubocop:disable Scalability/IdempotentWor
def perform(template_id)
return unless try_obtain_lease_for(template_id)
Admin::PropagateServiceTemplate.propagate(Integration.find_by_id(template_id))
::Integrations::PropagateTemplateService.propagate(Integration.find_by_id(template_id))
end
private

View file

@ -97,7 +97,7 @@ From there, you can see the following actions:
- 2FA enforcement or grace period changed.
- Roles allowed to create project changed.
- Group CI/CD variable added, removed, or protected status changed. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30857) in GitLab 13.3.
- Compliance framework created, updated, or deleted. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/340649) in GitLab 14.6.
- Compliance framework created, updated, or deleted. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/340649) in GitLab 14.5.
Group events can also be accessed via the [Group Audit Events API](../api/audit_events.md#group-audit-events)

View file

@ -52,7 +52,8 @@ Users are considered inactive in LDAP when they:
Status is checked for all LDAP users:
- When signing in using any authentication provider.
- When signing in using any authentication provider. [In GitLab 14.4 and earlier](https://gitlab.com/gitlab-org/gitlab/-/issues/343298), status was
checked only when signing in using LDAP directly.
- Once per hour for active web sessions or Git requests using tokens or SSH keys.
- When performing Git over HTTP requests using LDAP username and password.
- Once per day during [User Sync](ldap_synchronization.md#user-sync).

View file

@ -180,3 +180,11 @@ end
NOTE:
This example is unlikely in GitLab, because we usually look up the parent models to perform
permission checks.
## A note on `dependent: :destroy` and `dependent: :nullify`
We considered using these Rails features as an alternative to foreign keys but there are several problems which include:
1. These run on a different connection in the context of a transaction [which we do not allow](multiple_databases.md#removing-cross-database-transactions).
1. These can lead to severe performance degredation as we load all records from PostgreSQL, loop over them in Ruby, and call individual `DELETE` queries.
1. These can miss data as they only cover the case when the `destroy` method is called directly on the model. There are other cases including `delete_all` and cascading deletes from another parent table that could mean these are missed.

View file

@ -557,6 +557,22 @@ Don't hesitate to reach out to the
[sharding group](https://about.gitlab.com/handbook/engineering/development/enablement/sharding/)
for advice.
##### Avoid `dependent: :nullify` and `dependent: :destroy` across databases
There may be cases where we want to use `dependent: :nullify` or `dependent: :destroy`
across databases. This is technically possible, but it's problematic because
these hooks run in the context of an outer transaction from the call to
`#destroy`, which creates a cross-database transaction and we are trying to
avoid that. Cross-database transactions caused this way could lead to confusing
outcomes when we switch to decomposed, because now you have some queries
happening outside the transaction and they may be partially applied while the
outer transaction fails, which could lead to surprising bugs.
If you need to do some cleanup after a `destroy` you will need to choose
from some of the options above. If all you need to do is cleanup the child
records themselves from PostgreSQL then you could consider using ["loose foreign
keys"](loose_foreign_keys.md).
## `config/database.yml`
GitLab will support running multiple databases in the future, for example to [separate tables for the continuous integration features](https://gitlab.com/groups/gitlab-org/-/epics/6167) from the main database. In order to prepare for this change, we [validate the structure of the configuration](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67877) in `database.yml` to ensure that only known databases are used.
@ -603,3 +619,15 @@ production:
database: gitlabhq_production_ci
...
```
## Foreign keys that cross databases
There are many places where we use foreign keys that reference across the two
databases. This is not possible to do with two separate PostgreSQL
databases, so we need to replicate the behavior we get from PostgreSQL in a
performant way. We can't, and shouldn't, try to replicate the data guarantees
given by PostgreSQL which prevent creating invalid references, but we still need a
way to replace cascading deletes so we don't end up with orphaned data
or records that point to nowhere, which might lead to bugs. As such we created
["loose foreign keys"](loose_foreign_keys.md) which is an asynchronous
process of cleaning up orphaned records.

View file

@ -15,15 +15,18 @@ For the latest updates, check the [Tasks Roadmap](https://gitlab.com/groups/gitl
FLAG:
On self-managed GitLab, by default this feature is not available. To make it available,
ask an administrator to [enable the feature flag](../administration/feature_flags.md) named `work_items`.
On GitLab.com, this feature is not available.
The feature is not ready for production use.
Use tasks to track steps needed for the [issue](project/issues/index.md) to be closed.
When planning an issue, you need a way to capture and break down technical
requirements or steps necessary to complete it. An issue with tasks is better defined,
requirements or steps necessary to complete it. An issue with related tasks is better defined,
and so you can provide a more accurate issue weight and completion criteria.
To see the roadmap for migrating issues and [epics](group/epics/index.md)
Tasks are a type of work item, a step towards [default issue types](https://gitlab.com/gitlab-org/gitlab/-/issues/323404)
in GitLab.
For the roadmap of migrating issues and [epics](group/epics/index.md)
to work items and adding custom work item types, visit
[epic 6033](https://gitlab.com/groups/gitlab-org/-/epics/6033) or
[Plan direction page](https://about.gitlab.com/direction/plan/).
@ -31,4 +34,4 @@ to work items and adding custom work item types, visit
## View a task
The only way to view a task is to open it with a deep link,
for example: `/<group_name>/<project_name>/-/work_item/1`
for example: `/<group_name>/<project_name>/-/work_item/1`.

View file

@ -717,7 +717,7 @@ msgstr ""
msgid "%{link_start}Remove the %{draft_snippet} prefix%{link_end} from the title to allow this merge request to be merged when it's ready."
msgstr ""
msgid "%{link_start}Start the title with %{draft_snippet}%{link_end} to prevent a merge request that is a work in progress from being merged before it's ready."
msgid "%{link_start}Start the title with %{draft_snippet}%{link_end} to prevent a merge request draft from merging before it's ready."
msgstr ""
msgid "%{link_start}What information does GitLab Inc. collect?%{link_end}"

View file

@ -46,8 +46,8 @@ RSpec.describe 'Merge request > User sees draft help message' do
'It looks like you have some draft commits in this branch'
)
expect(page).to have_text(
"Start the title with Draft: to prevent a merge request that is a \
work in progress from being merged before it's ready."
"Start the title with Draft: to prevent a merge request draft \
from merging before it's ready."
)
end
end

View file

@ -123,6 +123,7 @@ describe('BoardFilteredSearch', () => {
{ type: 'milestone_title', value: { data: 'New Milestone', operator: '=' } },
{ type: 'types', value: { data: 'INCIDENT', operator: '=' } },
{ type: 'weight', value: { data: '2', operator: '=' } },
{ type: 'iteration', value: { data: '3341', operator: '=' } },
];
jest.spyOn(urlUtility, 'updateHistory');
findFilteredSearch().vm.$emit('onFilter', mockFilters);
@ -131,7 +132,7 @@ describe('BoardFilteredSearch', () => {
title: '',
replace: true,
url:
'http://test.host/?author_username=root&label_name[]=label&label_name[]=label2&milestone_title=New+Milestone&types=INCIDENT&weight=2',
'http://test.host/?author_username=root&label_name[]=label&label_name[]=label2&milestone_title=New+Milestone&iteration_id=3341&types=INCIDENT&weight=2',
});
});
});

View file

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe Admin::PropagateIntegrationService do
RSpec.describe Integrations::PropagateService do
describe '.propagate' do
include JiraServiceHelper

View file

@ -18,7 +18,7 @@ RSpec.describe PropagateIntegrationWorker do
end
it 'calls the propagate service with the integration' do
expect(Admin::PropagateIntegrationService).to receive(:propagate).with(integration)
expect(Integrations::PropagateService).to receive(:propagate).with(integration)
subject.perform(integration.id)
end