Commit b333db1a authored by Jacques Erasmus's avatar Jacques Erasmus

Merge branch...

Merge branch '221014-add-group-setting-for-default-initial-branch-name-for-new-repositories' into 'master'

Add Default Initial Branch Name for Repositories Group Setting

See merge request gitlab-org/gitlab!43290
parents 9d5a2b11 61841425
...@@ -16,7 +16,7 @@ inherit_mode: ...@@ -16,7 +16,7 @@ inherit_mode:
- Include - Include
AllCops: AllCops:
TargetRubyVersion: 2.6 TargetRubyVersion: 2.7
TargetRailsVersion: 6.0 TargetRailsVersion: 6.0
Exclude: Exclude:
- 'vendor/**/*' - 'vendor/**/*'
......
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
## 13.5.3 (2020-11-03)
- No changes.
## 13.5.2 (2020-11-02) ## 13.5.2 (2020-11-02)
### Security (4 changes) ### Security (4 changes)
...@@ -230,6 +234,13 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -230,6 +234,13 @@ Please view this file on the master branch, on stable branches it's out of date.
- Remove bootstrap class in licensed user count. !45443 - Remove bootstrap class in licensed user count. !45443
## 13.4.6 (2020-11-03)
### Fixed (1 change)
- Handle 500 error for GraphQL mutation. !43936
## 13.4.5 (2020-11-02) ## 13.4.5 (2020-11-02)
### Security (4 changes) ### Security (4 changes)
......
...@@ -2,6 +2,19 @@ ...@@ -2,6 +2,19 @@
documentation](doc/development/changelog.md) for instructions on adding your own documentation](doc/development/changelog.md) for instructions on adding your own
entry. entry.
## 13.5.3 (2020-11-03)
### Fixed (3 changes)
- Fix IDE issues with special characters. !46398
- Ensure that copy to clipboard button is visible. !46466
- Auto Deploy: fixes issues for fetching other charts from stable repo. !46531
### Added (1 change)
- Add environment variables to override backup/restore DB settings. !45855
## 13.5.2 (2020-11-02) ## 13.5.2 (2020-11-02)
### Security (9 changes) ### Security (9 changes)
...@@ -598,6 +611,17 @@ entry. ...@@ -598,6 +611,17 @@ entry.
- Bump cluster applications CI template. !45472 - Bump cluster applications CI template. !45472
## 13.4.6 (2020-11-03)
### Fixed (1 change)
- Auto Deploy: fixes issues for fetching other charts from stable repo. !46531
### Other (1 change)
- GitLab-managed apps: Use GitLab's repo as replacement for the Helm stable repo. !44875
## 13.4.5 (2020-11-02) ## 13.4.5 (2020-11-02)
### Security (9 changes) ### Security (9 changes)
......
1c4fdefdaf88730c025b5c7ba7ddc42c268043d4 020b5f709d58277c360ba409b8f8a9e81cee2781
import Vue from 'vue'; // EE-specific feature. Find the implementation in the `ee/`-folder
import DevopsAdoptionApp from './components/devops_adoption_app.vue'; export default () => {};
export default () => {
const el = document.querySelector('.js-devops-adoption');
if (!el) return false;
const { emptyStateSvgPath } = el.dataset;
return new Vue({
el,
provide: {
emptyStateSvgPath,
},
render(h) {
return h(DevopsAdoptionApp);
},
});
};
<script>
import { GlLink, GlSprintf } from '@gitlab/ui';
export default {
components: {
GlLink,
GlSprintf,
},
props: {
message: {
type: String,
required: true,
},
link: {
type: String,
required: true,
},
},
};
</script>
<template>
<span class="gl-text-gray-500">
<gl-sprintf :message="message">
<template #link="{ content }">
<gl-link class="gl-display-inline-block" :href="link" target="_blank">{{
content
}}</gl-link>
</template>
</gl-sprintf>
</span>
</template>
...@@ -81,7 +81,6 @@ export default { ...@@ -81,7 +81,6 @@ export default {
<div class="incident-management-list"> <div class="incident-management-list">
<h5 class="gl-font-lg">{{ $options.i18n.title }}</h5> <h5 class="gl-font-lg">{{ $options.i18n.title }}</h5>
<gl-table <gl-table
:empty-text="$options.i18n.emptyState"
:items="integrations" :items="integrations"
:fields="$options.fields" :fields="$options.fields"
:busy="loading" :busy="loading"
...@@ -115,6 +114,14 @@ export default { ...@@ -115,6 +114,14 @@ export default {
<template #table-busy> <template #table-busy>
<gl-loading-icon size="lg" color="dark" class="mt-3" /> <gl-loading-icon size="lg" color="dark" class="mt-3" />
</template> </template>
<template #empty>
<div
class="gl-border-t-solid gl-border-b-solid gl-border-1 gl-border gl-border-gray-100 mt-n3"
>
<p class="gl-text-gray-400 gl-py-3 gl-my-3">{{ $options.i18n.emptyState }}</p>
</div>
</template>
</gl-table> </gl-table>
</div> </div>
</template> </template>
...@@ -56,7 +56,7 @@ export default { ...@@ -56,7 +56,7 @@ export default {
data() { data() {
return { return {
loading: false, loading: false,
selectedIntegration: integrationTypes[1].value, selectedIntegration: integrationTypes[0].value,
options: integrationTypes, options: integrationTypes,
active: false, active: false,
authKey: '', authKey: '',
...@@ -88,34 +88,34 @@ export default { ...@@ -88,34 +88,34 @@ export default {
]; ];
}, },
isPrometheus() { isPrometheus() {
return this.selectedIntegration === 'prometheus'; return this.selectedIntegration === 'PROMETHEUS';
}, },
isOpsgenie() { isOpsgenie() {
return this.selectedIntegration === 'opsgenie'; return this.selectedIntegration === 'OPSGENIE';
}, },
selectedIntegrationType() { selectedIntegrationType() {
switch (this.selectedIntegration) { switch (this.selectedIntegration) {
case 'generic': { case 'HTTP': {
return { return {
url: this.generic.url, url: this.generic.url,
authKey: this.generic.authorizationKey, authKey: this.generic.authKey,
activated: this.generic.activated, active: this.generic.active,
resetKey: this.resetKey.bind(this), resetKey: this.resetKey.bind(this),
}; };
} }
case 'prometheus': { case 'PROMETHEUS': {
return { return {
url: this.prometheus.prometheusUrl, url: this.prometheus.url,
authKey: this.prometheus.authorizationKey, authKey: this.prometheus.authKey,
activated: this.prometheus.activated, active: this.prometheus.active,
resetKey: this.resetKey.bind(this, 'prometheus'), resetKey: this.resetKey.bind(this, 'PROMETHEUS'),
targetUrl: this.prometheus.prometheusApiUrl, targetUrl: this.prometheus.prometheusApiUrl,
}; };
} }
case 'opsgenie': { case 'OPSGENIE': {
return { return {
targetUrl: this.opsgenie.opsgenieMvcTargetUrl, targetUrl: this.opsgenie.opsgenieMvcTargetUrl,
activated: this.opsgenie.activated, active: this.opsgenie.active,
}; };
} }
default: { default: {
...@@ -161,16 +161,12 @@ export default { ...@@ -161,16 +161,12 @@ export default {
}, },
}, },
mounted() { mounted() {
if ( if (this.prometheus.active || this.generic.active || !this.opsgenie.opsgenieMvcIsAvailable) {
this.prometheus.activated ||
this.generic.activated ||
!this.opsgenie.opsgenieMvcIsAvailable
) {
this.removeOpsGenieOption(); this.removeOpsGenieOption();
} else if (this.opsgenie.activated) { } else if (this.opsgenie.active) {
this.setOpsgenieAsDefault(); this.setOpsgenieAsDefault();
} }
this.active = this.selectedIntegrationType.activated; this.active = this.selectedIntegrationType.active;
this.authKey = this.selectedIntegrationType.authKey ?? ''; this.authKey = this.selectedIntegrationType.authKey ?? '';
}, },
methods: { methods: {
...@@ -183,19 +179,19 @@ export default { ...@@ -183,19 +179,19 @@ export default {
}, },
setOpsgenieAsDefault() { setOpsgenieAsDefault() {
this.options = this.options.map(el => { this.options = this.options.map(el => {
if (el.value !== 'opsgenie') { if (el.value !== 'OPSGENIE') {
return { ...el, disabled: true }; return { ...el, disabled: true };
} }
return { ...el, disabled: false }; return { ...el, disabled: false };
}); });
this.selectedIntegration = this.options.find(({ value }) => value === 'opsgenie').value; this.selectedIntegration = this.options.find(({ value }) => value === 'OPSGENIE').value;
if (this.targetUrl === null) { if (this.targetUrl === null) {
this.targetUrl = this.selectedIntegrationType.targetUrl; this.targetUrl = this.selectedIntegrationType.targetUrl;
} }
}, },
removeOpsGenieOption() { removeOpsGenieOption() {
this.options = this.options.map(el => { this.options = this.options.map(el => {
if (el.value !== 'opsgenie') { if (el.value !== 'OPSGENIE') {
return { ...el, disabled: false }; return { ...el, disabled: false };
} }
return { ...el, disabled: true }; return { ...el, disabled: true };
...@@ -204,7 +200,7 @@ export default { ...@@ -204,7 +200,7 @@ export default {
resetFormValues() { resetFormValues() {
this.testAlert.json = null; this.testAlert.json = null;
this.targetUrl = this.selectedIntegrationType.targetUrl; this.targetUrl = this.selectedIntegrationType.targetUrl;
this.active = this.selectedIntegrationType.activated; this.active = this.selectedIntegrationType.active;
}, },
dismissFeedback() { dismissFeedback() {
this.serverError = null; this.serverError = null;
...@@ -212,7 +208,7 @@ export default { ...@@ -212,7 +208,7 @@ export default {
this.isFeedbackDismissed = false; this.isFeedbackDismissed = false;
}, },
resetKey(key) { resetKey(key) {
const fn = key === 'prometheus' ? this.resetPrometheusKey() : this.resetGenericKey(); const fn = key === 'PROMETHEUS' ? this.resetPrometheusKey() : this.resetGenericKey();
return fn return fn
.then(({ data: { token } }) => { .then(({ data: { token } }) => {
...@@ -242,9 +238,10 @@ export default { ...@@ -242,9 +238,10 @@ export default {
}, },
toggleActivated(value) { toggleActivated(value) {
this.loading = true; this.loading = true;
const path = this.isOpsgenie ? this.opsgenie.formPath : this.generic.formPath;
return service return service
.updateGenericActive({ .updateGenericActive({
endpoint: this[this.selectedIntegration].formPath, endpoint: path,
params: this.isOpsgenie params: this.isOpsgenie
? { service: { opsgenie_mvc_target_url: this.targetUrl, opsgenie_mvc_enabled: value } } ? { service: { opsgenie_mvc_target_url: this.targetUrl, opsgenie_mvc_enabled: value } }
: { service: { active: value } }, : { service: { active: value } },
...@@ -345,7 +342,7 @@ export default { ...@@ -345,7 +342,7 @@ export default {
if (this.canSaveForm) { if (this.canSaveForm) {
this.canSaveForm = false; this.canSaveForm = false;
this.active = this.selectedIntegrationType.activated; this.active = this.selectedIntegrationType.active;
} }
}, },
}, },
...@@ -402,9 +399,9 @@ export default { ...@@ -402,9 +399,9 @@ export default {
</gl-sprintf> </gl-sprintf>
</span> </span>
</gl-form-group> </gl-form-group>
<gl-form-group :label="$options.i18n.activeLabel" label-for="activated"> <gl-form-group :label="$options.i18n.activeLabel" label-for="active">
<toggle-button <toggle-button
id="activated" id="active"
:disabled-input="loading" :disabled-input="loading"
:is-loading="loading" :is-loading="loading"
:value="active" :value="active"
......
<script> <script>
import produce from 'immer';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { fetchPolicies } from '~/lib/graphql'; import { fetchPolicies } from '~/lib/graphql';
import createFlash, { FLASH_TYPES } from '~/flash';
import getIntegrationsQuery from '../graphql/queries/get_integrations.query.graphql'; import getIntegrationsQuery from '../graphql/queries/get_integrations.query.graphql';
import createHttpIntegrationMutation from '../graphql/mutations/create_http_integration.mutation.graphql';
import createPrometheusIntegrationMutation from '../graphql/mutations/create_prometheus_integration.mutation.graphql';
import IntegrationsList from './alerts_integrations_list.vue'; import IntegrationsList from './alerts_integrations_list.vue';
import SettingsFormOld from './alerts_settings_form_old.vue'; import SettingsFormOld from './alerts_settings_form_old.vue';
import SettingsFormNew from './alerts_settings_form_new.vue'; import SettingsFormNew from './alerts_settings_form_new.vue';
import { typeSet } from '../constants';
export default { export default {
typeSet,
i18n: {
changesSaved: s__(
'AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list.',
),
},
components: { components: {
IntegrationsList, IntegrationsList,
SettingsFormOld, SettingsFormOld,
...@@ -49,6 +60,7 @@ export default { ...@@ -49,6 +60,7 @@ export default {
data() { data() {
return { return {
errored: false, errored: false,
isUpdating: false,
integrations: {}, integrations: {},
}; };
}, },
...@@ -61,16 +73,85 @@ export default { ...@@ -61,16 +73,85 @@ export default {
{ {
name: s__('AlertSettings|HTTP endpoint'), name: s__('AlertSettings|HTTP endpoint'),
type: s__('AlertsIntegrations|HTTP endpoint'), type: s__('AlertsIntegrations|HTTP endpoint'),
active: this.generic.activated, active: this.generic.active,
}, },
{ {
name: s__('AlertSettings|External Prometheus'), name: s__('AlertSettings|External Prometheus'),
type: s__('AlertsIntegrations|Prometheus'), type: s__('AlertsIntegrations|Prometheus'),
active: this.prometheus.activated, active: this.prometheus.active,
}, },
]; ];
}, },
}, },
methods: {
onCreateNewIntegration({ type, variables }) {
this.isUpdating = true;
this.$apollo
.mutate({
mutation:
type === this.$options.typeSet.http
? createHttpIntegrationMutation
: createPrometheusIntegrationMutation,
variables: {
...variables,
projectPath: this.projectPath,
},
update: this.updateIntegrations,
})
.then(({ data: { httpIntegrationCreate, prometheusIntegrationCreate } = {} } = {}) => {
const error = httpIntegrationCreate?.errors[0] || prometheusIntegrationCreate?.errors[0];
if (error) {
return createFlash({ message: error });
}
return createFlash({
message: this.$options.i18n.changesSaved,
type: FLASH_TYPES.SUCCESS,
});
})
.catch(err => {
this.errored = true;
createFlash({ message: err });
})
.finally(() => {
this.isUpdating = false;
});
},
updateIntegrations(
store,
{
data: { httpIntegrationCreate, prometheusIntegrationCreate },
},
) {
const integration =
httpIntegrationCreate?.integration || prometheusIntegrationCreate?.integration;
if (!integration) {
return;
}
const sourceData = store.readQuery({
query: getIntegrationsQuery,
variables: {
projectPath: this.projectPath,
},
});
const data = produce(sourceData, draftData => {
// eslint-disable-next-line no-param-reassign
draftData.project.alertManagementIntegrations.nodes = [
integration,
...draftData.project.alertManagementIntegrations.nodes,
];
});
store.writeQuery({
query: getIntegrationsQuery,
variables: {
projectPath: this.projectPath,
},
data,
});
},
},
}; };
</script> </script>
...@@ -80,7 +161,11 @@ export default { ...@@ -80,7 +161,11 @@ export default {
:integrations="glFeatures.httpIntegrationsList ? integrations.list : intergrationsOptionsOld" :integrations="glFeatures.httpIntegrationsList ? integrations.list : intergrationsOptionsOld"
:loading="loading" :loading="loading"
/> />
<settings-form-new v-if="glFeatures.httpIntegrationsList" /> <settings-form-new
v-if="glFeatures.httpIntegrationsList"
:loading="loading"
@on-create-new-integration="onCreateNewIntegration"
/>
<settings-form-old v-else /> <settings-form-old v-else />
</div> </div>
</template> </template>
import { s__ } from '~/locale'; import { s__ } from '~/locale';
// TODO: Remove this as part of the form old removal
export const i18n = { export const i18n = {
usageSection: s__( usageSection: s__(
'AlertSettings|You must provide this URL and authorization key to authorize an external service to send alerts to GitLab. You can provide this URL and key to multiple services. After configuring an external service, alerts from your service will display on the GitLab %{linkStart}Alerts%{linkEnd} page.', 'AlertSettings|You must provide this URL and authorization key to authorize an external service to send alerts to GitLab. You can provide this URL and key to multiple services. After configuring an external service, alerts from your service will display on the GitLab %{linkStart}Alerts%{linkEnd} page.',
...@@ -39,13 +40,23 @@ export const i18n = { ...@@ -39,13 +40,23 @@ export const i18n = {
integration: s__('AlertSettings|Integration'), integration: s__('AlertSettings|Integration'),
}; };
// TODO: Delete as part of old form removal in 13.6
export const integrationTypes = [ export const integrationTypes = [
{ value: 'HTTP', text: s__('AlertSettings|HTTP Endpoint') },
{ value: 'PROMETHEUS', text: s__('AlertSettings|External Prometheus') },
{ value: 'OPSGENIE', text: s__('AlertSettings|Opsgenie') },
];
export const integrationTypesNew = [
{ value: '', text: s__('AlertSettings|Select integration type') }, { value: '', text: s__('AlertSettings|Select integration type') },
{ value: 'generic', text: s__('AlertSettings|HTTP Endpoint') }, ...integrationTypes,
{ value: 'prometheus', text: s__('AlertSettings|External Prometheus') },
{ value: 'opsgenie', text: s__('AlertSettings|Opsgenie') },
]; ];
export const typeSet = {
http: 'HTTP',
prometheus: 'PROMETHEUS',
};
export const JSON_VALIDATE_DELAY = 250; export const JSON_VALIDATE_DELAY = 250;
export const targetPrometheusUrlPlaceholder = 'http://prometheus.example.com/'; export const targetPrometheusUrlPlaceholder = 'http://prometheus.example.com/';
......
#import "../fragments/integration_item.fragment.graphql"
mutation createHttpIntegration($projectPath: ID!, $name: String!, $active: Boolean!) {
httpIntegrationCreate(input: { projectPath: $projectPath, name: $name, active: $active }) {
errors
integration {
...IntegrationItem
}
}
}
#import "../fragments/integration_item.fragment.graphql"
mutation createPrometheusIntegration($projectPath: ID!, $apiUrl: String!, $active: Boolean!) {
prometheusIntegrationCreate(
input: { projectPath: $projectPath, apiUrl: $apiUrl, active: $active }
) {
errors
integration {
...IntegrationItem
}
}
}
...@@ -48,9 +48,9 @@ export default el => { ...@@ -48,9 +48,9 @@ export default el => {
el, el,
provide: { provide: {
prometheus: { prometheus: {
activated: parseBoolean(prometheusActivated), active: parseBoolean(prometheusActivated),
prometheusUrl, url: prometheusUrl,
authorizationKey: prometheusAuthorizationKey, authKey: prometheusAuthorizationKey,
prometheusFormPath, prometheusFormPath,
prometheusResetKeyPath, prometheusResetKeyPath,
prometheusApiUrl, prometheusApiUrl,
...@@ -58,14 +58,14 @@ export default el => { ...@@ -58,14 +58,14 @@ export default el => {
generic: { generic: {
alertsSetupUrl, alertsSetupUrl,
alertsUsageUrl, alertsUsageUrl,
activated: parseBoolean(activatedStr), active: parseBoolean(activatedStr),
formPath, formPath,
authorizationKey, authKey: authorizationKey,
url, url,
}, },
opsgenie: { opsgenie: {
formPath: opsgenieMvcFormPath, formPath: opsgenieMvcFormPath,
activated: parseBoolean(opsgenieMvcEnabled), active: parseBoolean(opsgenieMvcEnabled),
opsgenieMvcTargetUrl, opsgenieMvcTargetUrl,
opsgenieMvcIsAvailable: parseBoolean(opsgenieMvcAvailable), opsgenieMvcIsAvailable: parseBoolean(opsgenieMvcAvailable),
}, },
......
...@@ -70,6 +70,7 @@ const Api = { ...@@ -70,6 +70,7 @@ const Api = {
featureFlagUserLists: '/api/:version/projects/:id/feature_flags_user_lists', featureFlagUserLists: '/api/:version/projects/:id/feature_flags_user_lists',
featureFlagUserList: '/api/:version/projects/:id/feature_flags_user_lists/:list_iid', featureFlagUserList: '/api/:version/projects/:id/feature_flags_user_lists/:list_iid',
billableGroupMembersPath: '/api/:version/groups/:id/billable_members', billableGroupMembersPath: '/api/:version/groups/:id/billable_members',
containerRegistryDetailsPath: '/api/:version/registry/repositories/:id/',
group(groupId, callback = () => {}) { group(groupId, callback = () => {}) {
const url = Api.buildUrl(Api.groupPath).replace(':id', groupId); const url = Api.buildUrl(Api.groupPath).replace(':id', groupId);
...@@ -106,6 +107,11 @@ const Api = { ...@@ -106,6 +107,11 @@ const Api = {
return axios.delete(url); return axios.delete(url);
}, },
containerRegistryDetails(registryId, options = {}) {
const url = Api.buildUrl(this.containerRegistryDetailsPath).replace(':id', registryId);
return axios.get(url, options);
},
groupMembers(id, options) { groupMembers(id, options) {
const url = Api.buildUrl(this.groupMembersPath).replace(':id', encodeURIComponent(id)); const url = Api.buildUrl(this.groupMembersPath).replace(':id', encodeURIComponent(id));
......
...@@ -12,11 +12,19 @@ import { getLocationHash } from '../lib/utils/url_utility'; ...@@ -12,11 +12,19 @@ import { getLocationHash } from '../lib/utils/url_utility';
$(() => { $(() => {
function toggleContainer(container, toggleState) { function toggleContainer(container, toggleState) {
const $container = $(container); const $container = $(container);
const isExpanded = $container.data('is-expanded');
$container const $collapseIcon = $container.find('.js-sidebar-collapse');
.find('.js-toggle-button .fa-chevron-up, .js-toggle-button .fa-chevron-down') const $expandIcon = $container.find('.js-sidebar-expand');
.toggleClass('fa-chevron-up', toggleState)
.toggleClass('fa-chevron-down', toggleState !== undefined ? !toggleState : undefined); if (isExpanded && !toggleState) {
$container.data('is-expanded', false);
$collapseIcon.addClass('hidden');
$expandIcon.removeClass('hidden');
} else {
$container.data('is-expanded', true);
$expandIcon.addClass('hidden');
$collapseIcon.removeClass('hidden');
}
$container.find('.js-toggle-content').toggle(toggleState); $container.find('.js-toggle-content').toggle(toggleState);
} }
......
...@@ -168,9 +168,6 @@ export default class CreateMergeRequestDropdown { ...@@ -168,9 +168,6 @@ export default class CreateMergeRequestDropdown {
disable() { disable() {
this.disableCreateAction(); this.disableCreateAction();
this.dropdownToggle.classList.add('disabled');
this.dropdownToggle.setAttribute('disabled', 'disabled');
} }
disableCreateAction() { disableCreateAction() {
...@@ -189,9 +186,6 @@ export default class CreateMergeRequestDropdown { ...@@ -189,9 +186,6 @@ export default class CreateMergeRequestDropdown {
this.createTargetButton.classList.remove('disabled'); this.createTargetButton.classList.remove('disabled');
this.createTargetButton.removeAttribute('disabled'); this.createTargetButton.removeAttribute('disabled');
this.dropdownToggle.classList.remove('disabled');
this.dropdownToggle.removeAttribute('disabled');
} }
static findByValue(objects, ref, returnFirstMatch = false) { static findByValue(objects, ref, returnFirstMatch = false) {
......
#import "../fragments/design.fragment.graphql" #import "../fragments/design.fragment.graphql"
#import "~/graphql_shared/fragments/author.fragment.graphql" #import "~/graphql_shared/fragments/author.fragment.graphql"
query getDesign($fullPath: ID!, $iid: String!, $atVersion: ID, $filenames: [String!]) { query getDesign(
$fullPath: ID!
$iid: String!
$atVersion: DesignManagementVersionID
$filenames: [String!]
) {
project(fullPath: $fullPath) { project(fullPath: $fullPath) {
id id
issue(iid: $iid) { issue(iid: $iid) {
......
...@@ -626,7 +626,7 @@ export function switchToFullDiffFromRenamedFile({ commit, dispatch, state }, { d ...@@ -626,7 +626,7 @@ export function switchToFullDiffFromRenamedFile({ commit, dispatch, state }, { d
.then(({ data }) => { .then(({ data }) => {
const lines = data.map((line, index) => const lines = data.map((line, index) =>
prepareLineForRenamedFile({ prepareLineForRenamedFile({
diffViewType: state.diffViewType, diffViewType: window.gon?.features?.unifiedDiffLines ? 'inline' : state.diffViewType,
line, line,
diffFile, diffFile,
index, index,
...@@ -638,6 +638,7 @@ export function switchToFullDiffFromRenamedFile({ commit, dispatch, state }, { d ...@@ -638,6 +638,7 @@ export function switchToFullDiffFromRenamedFile({ commit, dispatch, state }, { d
viewer: { viewer: {
...diffFile.alternate_viewer, ...diffFile.alternate_viewer,
automaticallyCollapsed: false, automaticallyCollapsed: false,
manuallyCollapsed: false,
}, },
}); });
commit(types.SET_CURRENT_VIEW_DIFF_FILE_LINES, { filePath: diffFile.file_path, lines }); commit(types.SET_CURRENT_VIEW_DIFF_FILE_LINES, { filePath: diffFile.file_path, lines });
......
...@@ -378,8 +378,13 @@ export default { ...@@ -378,8 +378,13 @@ export default {
}, },
[types.SET_CURRENT_VIEW_DIFF_FILE_LINES](state, { filePath, lines }) { [types.SET_CURRENT_VIEW_DIFF_FILE_LINES](state, { filePath, lines }) {
const file = state.diffFiles.find(f => f.file_path === filePath); const file = state.diffFiles.find(f => f.file_path === filePath);
const currentDiffLinesKey = let currentDiffLinesKey;
state.diffViewType === 'inline' ? 'highlighted_diff_lines' : 'parallel_diff_lines';
if (window.gon?.features?.unifiedDiffLines || state.diffViewType === 'inline') {
currentDiffLinesKey = 'highlighted_diff_lines';
} else {
currentDiffLinesKey = 'parallel_diff_lines';
}
file[currentDiffLinesKey] = lines; file[currentDiffLinesKey] = lines;
}, },
......
...@@ -27,7 +27,7 @@ export default { ...@@ -27,7 +27,7 @@ export default {
rolloutUserListLabel: s__('FeatureFlag|User List'), rolloutUserListLabel: s__('FeatureFlag|User List'),
rolloutUserListDescription: s__('FeatureFlag|Select a user list'), rolloutUserListDescription: s__('FeatureFlag|Select a user list'),
rolloutUserListNoListError: s__('FeatureFlag|There are no configured user lists'), rolloutUserListNoListError: s__('FeatureFlag|There are no configured user lists'),
defaultDropdownText: s__('FeatureFlags|Select a user list'), defaultDropdownText: s__('FeatureFlags|No user list selected'),
}, },
computed: { computed: {
...mapGetters(['hasUserLists', 'isLoading', 'hasError', 'userListOptions']), ...mapGetters(['hasUserLists', 'isLoading', 'hasError', 'userListOptions']),
...@@ -36,7 +36,7 @@ export default { ...@@ -36,7 +36,7 @@ export default {
return this.strategy?.userList?.id ?? ''; return this.strategy?.userList?.id ?? '';
}, },
dropdownText() { dropdownText() {
return this.strategy?.userList?.name ?? this.$options.defaultDropdownText; return this.strategy?.userList?.name ?? this.$options.translations.defaultDropdownText;
}, },
}, },
mounted() { mounted() {
......
...@@ -116,7 +116,7 @@ export default { ...@@ -116,7 +116,7 @@ export default {
<gl-dropdown <gl-dropdown
v-if="displayFilters" v-if="displayFilters"
id="discussion-filter-dropdown" id="discussion-filter-dropdown"
class="gl-mr-3 full-width-mobile discussion-filter-container js-discussion-filter-container qa-discussion-filter" class="gl-mr-3 full-width-mobile discussion-filter-container js-discussion-filter-container"
data-qa-selector="discussion_filter_dropdown" data-qa-selector="discussion_filter_dropdown"
:text="currentFilter.title" :text="currentFilter.title"
> >
......
...@@ -65,8 +65,8 @@ export default { ...@@ -65,8 +65,8 @@ export default {
}; };
}, },
computed: { computed: {
toggleChevronClass() { toggleChevronIconName() {
return this.expanded ? 'fa-chevron-up' : 'fa-chevron-down'; return this.expanded ? 'chevron-up' : 'chevron-down';
}, },
noteTimestampLink() { noteTimestampLink() {
return this.noteId ? `#note_${this.noteId}` : undefined; return this.noteId ? `#note_${this.noteId}` : undefined;
...@@ -133,7 +133,7 @@ export default { ...@@ -133,7 +133,7 @@ export default {
type="button" type="button"
@click="handleToggle" @click="handleToggle"
> >
<i ref="chevronIcon" :class="toggleChevronClass" class="fa" aria-hidden="true"></i> <gl-icon ref="chevronIcon" :name="toggleChevronIconName" aria-hidden="true" />
{{ __('Toggle thread') }} {{ __('Toggle thread') }}
</button> </button>
</div> </div>
......
import initDevopAdoption from 'ee_else_ce/admin/dev_ops_report/devops_adoption';
import initDevOpsScoreEmptyState from '~/admin/dev_ops_report/devops_score_empty_state'; import initDevOpsScoreEmptyState from '~/admin/dev_ops_report/devops_score_empty_state';
import initDevopAdoption from '~/admin/dev_ops_report/devops_adoption';
initDevOpsScoreEmptyState(); initDevOpsScoreEmptyState();
initDevopAdoption(); initDevopAdoption();
...@@ -15,6 +15,10 @@ export const DELETE_TAGS_SUCCESS_MESSAGE = s__( ...@@ -15,6 +15,10 @@ export const DELETE_TAGS_SUCCESS_MESSAGE = s__(
'ContainerRegistry|Tags successfully marked for deletion.', 'ContainerRegistry|Tags successfully marked for deletion.',
); );
export const FETCH_IMAGE_DETAILS_ERROR_MESSAGE = s__(
'ContainerRegistry|Something went wrong while fetching the image details.',
);
export const TAGS_LIST_TITLE = s__('ContainerRegistry|Image tags'); export const TAGS_LIST_TITLE = s__('ContainerRegistry|Image tags');
export const DIGEST_LABEL = s__('ContainerRegistry|Digest: %{imageId}'); export const DIGEST_LABEL = s__('ContainerRegistry|Digest: %{imageId}');
export const CREATED_AT_LABEL = s__('ContainerRegistry|Published %{timeInfo}'); export const CREATED_AT_LABEL = s__('ContainerRegistry|Published %{timeInfo}');
......
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { deprecatedCreateFlash as createFlash } from '~/flash'; import { deprecatedCreateFlash as createFlash } from '~/flash';
import Api from '~/api';
import * as types from './mutation_types'; import * as types from './mutation_types';
import { import {
FETCH_IMAGES_LIST_ERROR_MESSAGE, FETCH_IMAGES_LIST_ERROR_MESSAGE,
DEFAULT_PAGE, DEFAULT_PAGE,
DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZE,
FETCH_TAGS_LIST_ERROR_MESSAGE, FETCH_TAGS_LIST_ERROR_MESSAGE,
FETCH_IMAGE_DETAILS_ERROR_MESSAGE,
} from '../constants/index'; } from '../constants/index';
import { decodeAndParse } from '../utils'; import { decodeAndParse } from '../utils';
...@@ -61,6 +63,19 @@ export const requestTagsList = ({ commit, dispatch }, { pagination = {}, params ...@@ -61,6 +63,19 @@ export const requestTagsList = ({ commit, dispatch }, { pagination = {}, params
}); });
}; };
export const requestImageDetailsAndTagsList = ({ dispatch, commit }, id) => {
commit(types.SET_MAIN_LOADING, true);
return Api.containerRegistryDetails(id)
.then(({ data }) => {
commit(types.SET_IMAGE_DETAILS, data);
dispatch('requestTagsList');
})
.catch(() => {
createFlash(FETCH_IMAGE_DETAILS_ERROR_MESSAGE);
commit(types.SET_MAIN_LOADING, false);
});
};
export const requestDeleteTag = ({ commit, dispatch, state }, { tag, params }) => { export const requestDeleteTag = ({ commit, dispatch, state }, { tag, params }) => {
commit(types.SET_MAIN_LOADING, true); commit(types.SET_MAIN_LOADING, true);
return axios return axios
......
...@@ -7,3 +7,4 @@ export const SET_MAIN_LOADING = 'SET_MAIN_LOADING'; ...@@ -7,3 +7,4 @@ export const SET_MAIN_LOADING = 'SET_MAIN_LOADING';
export const SET_TAGS_PAGINATION = 'SET_TAGS_PAGINATION'; export const SET_TAGS_PAGINATION = 'SET_TAGS_PAGINATION';
export const SET_TAGS_LIST_SUCCESS = 'SET_TAGS_LIST_SUCCESS'; export const SET_TAGS_LIST_SUCCESS = 'SET_TAGS_LIST_SUCCESS';
export const SET_SHOW_GARBAGE_COLLECTION_TIP = 'SET_SHOW_GARBAGE_COLLECTION_TIP'; export const SET_SHOW_GARBAGE_COLLECTION_TIP = 'SET_SHOW_GARBAGE_COLLECTION_TIP';
export const SET_IMAGE_DETAILS = 'SET_IMAGE_DETAILS';
...@@ -47,4 +47,8 @@ export default { ...@@ -47,4 +47,8 @@ export default {
const normalizedHeaders = normalizeHeaders(headers); const normalizedHeaders = normalizeHeaders(headers);
state.tagsPagination = parseIntPagination(normalizedHeaders); state.tagsPagination = parseIntPagination(normalizedHeaders);
}, },
[types.SET_IMAGE_DETAILS](state, details) {
state.imageDetails = details;
},
}; };
...@@ -3,6 +3,7 @@ export default () => ({ ...@@ -3,6 +3,7 @@ export default () => ({
showGarbageCollectionTip: false, showGarbageCollectionTip: false,
config: {}, config: {},
images: [], images: [],
imageDetails: {},
tags: [], tags: [],
pagination: {}, pagination: {},
tagsPagination: {}, tagsPagination: {},
......
export const decodeAndParse = param => JSON.parse(window.atob(param)); export const decodeAndParse = param => JSON.parse(window.atob(param));
// eslint-disable-next-line @gitlab/require-i18n-strings
export const pathGenerator = (imageDetails, ending = 'tags?format=json') => {
// this method is a temporary workaround, to be removed with graphql implementation
// https://gitlab.com/gitlab-org/gitlab/-/issues/276432
const basePath = imageDetails.path.replace(`/${imageDetails.name}`, '');
return `/${basePath}/registry/repository/${imageDetails.id}/${ending}`;
};
...@@ -137,8 +137,8 @@ export default { ...@@ -137,8 +137,8 @@ export default {
:href="commit.author.webPath" :href="commit.author.webPath"
class="commit-author-link js-user-link" class="commit-author-link js-user-link"
> >
{{ commit.author.name }} {{ commit.author.name }}</gl-link
</gl-link> >
<template v-else> <template v-else>
{{ commit.authorName }} {{ commit.authorName }}
</template> </template>
......
...@@ -51,6 +51,7 @@ export const FIELDS = [ ...@@ -51,6 +51,7 @@ export const FIELDS = [
key: 'actions', key: 'actions',
thClass: 'col-actions', thClass: 'col-actions',
tdClass: 'col-actions', tdClass: 'col-actions',
showFunction: 'showActionsField',
}, },
]; ];
......
...@@ -2,6 +2,12 @@ ...@@ -2,6 +2,12 @@
import { mapState } from 'vuex'; import { mapState } from 'vuex';
import { GlTable, GlBadge } from '@gitlab/ui'; import { GlTable, GlBadge } from '@gitlab/ui';
import MembersTableCell from 'ee_else_ce/vue_shared/components/members/table/members_table_cell.vue'; import MembersTableCell from 'ee_else_ce/vue_shared/components/members/table/members_table_cell.vue';
import {
canOverride,
canRemove,
canResend,
canUpdate,
} from 'ee_else_ce/vue_shared/components/members/utils';
import { FIELDS } from '../constants'; import { FIELDS } from '../constants';
import initUserPopovers from '~/user_popovers'; import initUserPopovers from '~/user_popovers';
import MemberAvatar from './member_avatar.vue'; import MemberAvatar from './member_avatar.vue';
...@@ -33,14 +39,40 @@ export default { ...@@ -33,14 +39,40 @@ export default {
), ),
}, },
computed: { computed: {
...mapState(['members', 'tableFields']), ...mapState(['members', 'tableFields', 'currentUserId', 'sourceId']),
filteredFields() { filteredFields() {
return FIELDS.filter(field => this.tableFields.includes(field.key)); return FIELDS.filter(field => this.tableFields.includes(field.key) && this.showField(field));
},
userIsLoggedIn() {
return this.currentUserId !== null;
}, },
}, },
mounted() { mounted() {
initUserPopovers(this.$el.querySelectorAll('.js-user-link')); initUserPopovers(this.$el.querySelectorAll('.js-user-link'));
}, },
methods: {
showField(field) {
if (!Object.prototype.hasOwnProperty.call(field, 'showFunction')) {
return true;
}
return this[field.showFunction]();
},
showActionsField() {
if (!this.userIsLoggedIn) {
return false;
}
return this.members.some(member => {
return (
canRemove(member, this.sourceId) ||
canResend(member) ||
canUpdate(member, this.currentUserId, this.sourceId) ||
canOverride(member)
);
});
},
},
}; };
</script> </script>
......
<script> <script>
import { mapState } from 'vuex'; import { mapState } from 'vuex';
import { MEMBER_TYPES } from '../constants'; import { MEMBER_TYPES } from '../constants';
import { isGroup, isDirectMember, isCurrentUser, canRemove, canResend, canUpdate } from '../utils';
export default { export default {
name: 'MembersTableCell', name: 'MembersTableCell',
...@@ -13,7 +14,7 @@ export default { ...@@ -13,7 +14,7 @@ export default {
computed: { computed: {
...mapState(['sourceId', 'currentUserId']), ...mapState(['sourceId', 'currentUserId']),
isGroup() { isGroup() {
return Boolean(this.member.sharedWithGroup); return isGroup(this.member);
}, },
isInvite() { isInvite() {
return Boolean(this.member.invite); return Boolean(this.member.invite);
...@@ -33,19 +34,19 @@ export default { ...@@ -33,19 +34,19 @@ export default {
return MEMBER_TYPES.user; return MEMBER_TYPES.user;
}, },
isDirectMember() { isDirectMember() {
return this.isGroup || this.member.source?.id === this.sourceId; return isDirectMember(this.member, this.sourceId);
}, },
isCurrentUser() { isCurrentUser() {
return this.member.user?.id === this.currentUserId; return isCurrentUser(this.member, this.currentUserId);
}, },
canRemove() { canRemove() {
return this.isDirectMember && this.member.canRemove; return canRemove(this.member, this.sourceId);
}, },
canResend() { canResend() {
return Boolean(this.member.invite?.canResend); return canResend(this.member);
}, },
canUpdate() { canUpdate() {
return !this.isCurrentUser && this.isDirectMember && this.member.canUpdate; return canUpdate(this.member, this.currentUserId, this.sourceId);
}, },
}, },
render() { render() {
......
...@@ -17,3 +17,32 @@ export const generateBadges = (member, isCurrentUser) => [ ...@@ -17,3 +17,32 @@ export const generateBadges = (member, isCurrentUser) => [
variant: 'info', variant: 'info',
}, },
]; ];
export const isGroup = member => {
return Boolean(member.sharedWithGroup);
};
export const isDirectMember = (member, sourceId) => {
return isGroup(member) || member.source?.id === sourceId;
};
export const isCurrentUser = (member, currentUserId) => {
return member.user?.id === currentUserId;
};
export const canRemove = (member, sourceId) => {
return isDirectMember(member, sourceId) && member.canRemove;
};
export const canResend = member => {
return Boolean(member.invite?.canResend);
};
export const canUpdate = (member, currentUserId, sourceId) => {
return (
!isCurrentUser(member, currentUserId) && isDirectMember(member, sourceId) && member.canUpdate
);
};
// Defined in `ee/app/assets/javascripts/vue_shared/components/members/utils.js`
export const canOverride = () => false;
...@@ -109,10 +109,6 @@ ...@@ -109,10 +109,6 @@
content: '\f0da'; content: '\f0da';
} }
.fa-chevron-up::before {
content: '\f077';
}
.fa-exclamation-circle::before { .fa-exclamation-circle::before {
content: '\f06a'; content: '\f06a';
} }
......
...@@ -188,6 +188,12 @@ ul.related-merge-requests > li { ...@@ -188,6 +188,12 @@ ul.related-merge-requests > li {
border-width: 1px; border-width: 1px;
line-height: $line-height-base; line-height: $line-height-base;
width: auto; width: auto;
&.disabled {
background-color: $gray-light;
border-color: $gray-100;
color: $gl-text-color-disabled;
}
} }
} }
......
...@@ -51,7 +51,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -51,7 +51,7 @@ class Projects::IssuesController < Projects::ApplicationController
real_time_feature_flag = :real_time_issue_sidebar real_time_feature_flag = :real_time_issue_sidebar
real_time_enabled = Gitlab::ActionCable::Config.in_app? || Feature.enabled?(real_time_feature_flag, @project) real_time_enabled = Gitlab::ActionCable::Config.in_app? || Feature.enabled?(real_time_feature_flag, @project)
gon.push({ features: { real_time_feature_flag.to_s.camelize(:lower) => real_time_enabled } }, true) push_to_gon_features(real_time_feature_flag, real_time_enabled)
record_experiment_user(:invite_members_version_a) record_experiment_user(:invite_members_version_a)
record_experiment_user(:invite_members_version_b) record_experiment_user(:invite_members_version_b)
......
...@@ -32,7 +32,7 @@ module FinderWithCrossProjectAccess ...@@ -32,7 +32,7 @@ module FinderWithCrossProjectAccess
end end
override :execute override :execute
def execute(*args) def execute(*args, **kwargs)
check = Gitlab::CrossProjectAccess.find_check(self) check = Gitlab::CrossProjectAccess.find_check(self)
original = -> { super } original = -> { super }
......
# frozen_string_literal: true
module Mutations
module AlertManagement
module HttpIntegration
class Destroy < HttpIntegrationBase
graphql_name 'HttpIntegrationDestroy'
argument :id, Types::GlobalIDType[::AlertManagement::HttpIntegration],
required: true,
description: "The id of the integration to remove"
def resolve(id:)
integration = authorized_find!(id: id)
response ::AlertManagement::HttpIntegrations::DestroyService.new(
integration,
current_user
).execute
end
end
end
end
end
...@@ -7,7 +7,7 @@ module Mutations ...@@ -7,7 +7,7 @@ module Mutations
field :integration, field :integration,
Types::AlertManagement::HttpIntegrationType, Types::AlertManagement::HttpIntegrationType,
null: true, null: true,
description: "The updated HTTP integration" description: "The HTTP integration"
authorize :admin_operations authorize :admin_operations
......
...@@ -9,7 +9,7 @@ module Resolvers ...@@ -9,7 +9,7 @@ module Resolvers
authorize :read_design authorize :read_design
argument :id, GraphQL::ID_TYPE, argument :id, ::Types::GlobalIDType[::DesignManagement::DesignAtVersion],
required: true, required: true,
description: 'The Global ID of the design at this version' description: 'The Global ID of the design at this version'
...@@ -18,7 +18,10 @@ module Resolvers ...@@ -18,7 +18,10 @@ module Resolvers
end end
def find_object(id:) def find_object(id:)
dav = GitlabSchema.object_from_id(id, expected_type: ::DesignManagement::DesignAtVersion) # TODO: remove this line when the compatibility layer is removed
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
id = ::Types::GlobalIDType[::DesignManagement::DesignAtVersion].coerce_isolated_input(id)
dav = GitlabSchema.find_by_gid(id)
return unless consistent?(dav) return unless consistent?(dav)
dav dav
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
module Resolvers module Resolvers
module DesignManagement module DesignManagement
class DesignResolver < BaseResolver class DesignResolver < BaseResolver
argument :id, GraphQL::ID_TYPE, argument :id, ::Types::GlobalIDType[::DesignManagement::Design],
required: false, required: false,
description: 'Find a design by its ID' description: 'Find a design by its ID'
...@@ -50,7 +50,11 @@ module Resolvers ...@@ -50,7 +50,11 @@ module Resolvers
end end
def parse_gid(gid) def parse_gid(gid)
GitlabSchema.parse_gid(gid, expected_type: ::DesignManagement::Design).model_id # TODO: remove this line when the compatibility layer is removed
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
gid = ::Types::GlobalIDType[::DesignManagement::Design].coerce_isolated_input(gid)
gid.model_id
end end
end end
end end
......
...@@ -3,16 +3,16 @@ ...@@ -3,16 +3,16 @@
module Resolvers module Resolvers
module DesignManagement module DesignManagement
class DesignsResolver < BaseResolver class DesignsResolver < BaseResolver
argument :ids, DesignID = ::Types::GlobalIDType[::DesignManagement::Design]
[GraphQL::ID_TYPE], VersionID = ::Types::GlobalIDType[::DesignManagement::Version]
argument :ids, [DesignID],
required: false, required: false,
description: 'Filters designs by their ID' description: 'Filters designs by their ID'
argument :filenames, argument :filenames, [GraphQL::STRING_TYPE],
[GraphQL::STRING_TYPE],
required: false, required: false,
description: 'Filters designs by their filename' description: 'Filters designs by their filename'
argument :at_version, argument :at_version, VersionID,
GraphQL::ID_TYPE,
required: false, required: false,
description: 'Filters designs to only those that existed at the version. ' \ description: 'Filters designs to only those that existed at the version. ' \
'If argument is omitted or nil then all designs will reflect the latest version' 'If argument is omitted or nil then all designs will reflect the latest version'
...@@ -36,11 +36,20 @@ module Resolvers ...@@ -36,11 +36,20 @@ module Resolvers
def version(at_version) def version(at_version)
return unless at_version return unless at_version
GitlabSchema.object_from_id(at_version, expected_type: ::DesignManagement::Version)&.sync # TODO: remove this line when the compatibility layer is removed
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
at_version = VersionID.coerce_isolated_input(at_version)
# TODO: when we get promises use this to make resolve lazy
Gitlab::Graphql::Lazy.force(GitlabSchema.find_by_gid(at_version))
end end
def design_ids(ids) def design_ids(gids)
ids&.map { |id| GlobalID.parse(id, expected_type: ::DesignManagement::Design).model_id } return if gids.nil?
# TODO: remove this line when the compatibility layer is removed
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
gids = gids.map { |id| DesignID.coerce_isolated_input(id) }
gids.map(&:model_id)
end end
def issue def issue
......
...@@ -5,17 +5,20 @@ module Resolvers ...@@ -5,17 +5,20 @@ module Resolvers
module Version module Version
# Resolver for a DesignAtVersion object given an implicit version context # Resolver for a DesignAtVersion object given an implicit version context
class DesignAtVersionResolver < BaseResolver class DesignAtVersionResolver < BaseResolver
DesignAtVersionID = ::Types::GlobalIDType[::DesignManagement::DesignAtVersion]
DesignID = ::Types::GlobalIDType[::DesignManagement::Design]
include Gitlab::Graphql::Authorize::AuthorizeResource include Gitlab::Graphql::Authorize::AuthorizeResource
type Types::DesignManagement::DesignAtVersionType, null: true type Types::DesignManagement::DesignAtVersionType, null: true
authorize :read_design authorize :read_design
argument :id, GraphQL::ID_TYPE, argument :id, DesignAtVersionID,
required: false, required: false,
as: :design_at_version_id, as: :design_at_version_id,
description: 'The ID of the DesignAtVersion' description: 'The ID of the DesignAtVersion'
argument :design_id, GraphQL::ID_TYPE, argument :design_id, DesignID,
required: false, required: false,
description: 'The ID of a specific design' description: 'The ID of a specific design'
argument :filename, GraphQL::STRING_TYPE, argument :filename, GraphQL::STRING_TYPE,
...@@ -29,6 +32,11 @@ module Resolvers ...@@ -29,6 +32,11 @@ module Resolvers
def resolve(design_id: nil, filename: nil, design_at_version_id: nil) def resolve(design_id: nil, filename: nil, design_at_version_id: nil)
validate_arguments(design_id, filename, design_at_version_id) validate_arguments(design_id, filename, design_at_version_id)
# TODO: remove this when the compatibility layer is removed
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
design_id &&= DesignID.coerce_isolated_input(design_id)
design_at_version_id &&= DesignAtVersionID.coerce_isolated_input(design_at_version_id)
return unless Ability.allowed?(current_user, :read_design, issue) return unless Ability.allowed?(current_user, :read_design, issue)
return specific_design_at_version(design_at_version_id) if design_at_version_id return specific_design_at_version(design_at_version_id) if design_at_version_id
...@@ -49,7 +57,7 @@ module Resolvers ...@@ -49,7 +57,7 @@ module Resolvers
end end
def specific_design_at_version(id) def specific_design_at_version(id)
dav = GitlabSchema.object_from_id(id, expected_type: ::DesignManagement::DesignAtVersion) dav = GitlabSchema.find_by_gid(id)
return unless consistent?(dav) return unless consistent?(dav)
dav dav
...@@ -65,8 +73,8 @@ module Resolvers ...@@ -65,8 +73,8 @@ module Resolvers
dav.design.visible_in?(version) dav.design.visible_in?(version)
end end
def find(id, filename) def find(gid, filename)
ids = [parse_design_id(id).model_id] if id ids = [gid.model_id] if gid
filenames = [filename] if filename filenames = [filename] if filename
::DesignManagement::DesignsFinder ::DesignManagement::DesignsFinder
...@@ -74,10 +82,6 @@ module Resolvers ...@@ -74,10 +82,6 @@ module Resolvers
.execute .execute
end end
def parse_design_id(id)
GitlabSchema.parse_gid(id, expected_type: ::DesignManagement::Design)
end
def issue def issue
version.issue version.issue
end end
......
...@@ -11,8 +11,9 @@ module Resolvers ...@@ -11,8 +11,9 @@ module Resolvers
authorize :read_design authorize :read_design
argument :ids, DesignID = ::Types::GlobalIDType[::DesignManagement::Design]
[GraphQL::ID_TYPE],
argument :ids, [DesignID],
required: false, required: false,
description: 'Filters designs by their ID' description: 'Filters designs by their ID'
argument :filenames, argument :filenames,
...@@ -31,16 +32,19 @@ module Resolvers ...@@ -31,16 +32,19 @@ module Resolvers
private private
def find(ids, filenames) def find(ids, filenames)
ids = ids&.map { |id| parse_design_id(id).model_id }
::DesignManagement::DesignsFinder.new(issue, current_user, ::DesignManagement::DesignsFinder.new(issue, current_user,
ids: ids, ids: design_ids(ids),
filenames: filenames, filenames: filenames,
visible_at_version: version) visible_at_version: version)
end end
def parse_design_id(id) def design_ids(gids)
GitlabSchema.parse_gid(id, expected_type: ::DesignManagement::Design) return if gids.nil?
# TODO: remove this line when the compatibility layer is removed
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
gids = gids.map { |id| DesignID.coerce_isolated_input(id) }
gids.map(&:model_id)
end end
def issue def issue
......
...@@ -11,20 +11,25 @@ module Resolvers ...@@ -11,20 +11,25 @@ module Resolvers
alias_method :collection, :object alias_method :collection, :object
VersionID = ::Types::GlobalIDType[::DesignManagement::Version]
argument :sha, GraphQL::STRING_TYPE, argument :sha, GraphQL::STRING_TYPE,
required: false, required: false,
description: "The SHA256 of a specific version" description: "The SHA256 of a specific version"
argument :id, GraphQL::ID_TYPE, argument :id, VersionID,
as: :version_id,
required: false, required: false,
description: 'The Global ID of the version' description: 'The Global ID of the version'
def resolve(id: nil, sha: nil) def resolve(version_id: nil, sha: nil)
check_args(id, sha) # TODO: remove this line when the compatibility layer is removed
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
version_id &&= VersionID.coerce_isolated_input(version_id)
gid = GitlabSchema.parse_gid(id, expected_type: ::DesignManagement::Version) if id check_args(version_id, sha)
::DesignManagement::VersionsFinder ::DesignManagement::VersionsFinder
.new(collection, current_user, sha: sha, version_id: gid&.model_id) .new(collection, current_user, sha: sha, version_id: version_id&.model_id)
.execute .execute
.first .first
end end
......
...@@ -9,7 +9,7 @@ module Resolvers ...@@ -9,7 +9,7 @@ module Resolvers
authorize :read_design authorize :read_design
argument :id, GraphQL::ID_TYPE, argument :id, ::Types::GlobalIDType[::DesignManagement::Version],
required: true, required: true,
description: 'The Global ID of the version' description: 'The Global ID of the version'
...@@ -18,7 +18,11 @@ module Resolvers ...@@ -18,7 +18,11 @@ module Resolvers
end end
def find_object(id:) def find_object(id:)
GitlabSchema.object_from_id(id, expected_type: ::DesignManagement::Version) # TODO: remove this line when the compatibility layer is removed
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
id = ::Types::GlobalIDType[::DesignManagement::Version].coerce_isolated_input(id)
GitlabSchema.find_by_gid(id)
end end
end end
end end
......
...@@ -7,12 +7,14 @@ module Resolvers ...@@ -7,12 +7,14 @@ module Resolvers
alias_method :design_or_collection, :object alias_method :design_or_collection, :object
VersionID = ::Types::GlobalIDType[::DesignManagement::Version]
argument :earlier_or_equal_to_sha, GraphQL::STRING_TYPE, argument :earlier_or_equal_to_sha, GraphQL::STRING_TYPE,
as: :sha, as: :sha,
required: false, required: false,
description: 'The SHA256 of the most recent acceptable version' description: 'The SHA256 of the most recent acceptable version'
argument :earlier_or_equal_to_id, GraphQL::ID_TYPE, argument :earlier_or_equal_to_id, VersionID,
as: :id, as: :id,
required: false, required: false,
description: 'The Global ID of the most recent acceptable version' description: 'The Global ID of the most recent acceptable version'
...@@ -23,6 +25,9 @@ module Resolvers ...@@ -23,6 +25,9 @@ module Resolvers
end end
def resolve(parent: nil, id: nil, sha: nil) def resolve(parent: nil, id: nil, sha: nil)
# TODO: remove this line when the compatibility layer is removed
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
id &&= VersionID.coerce_isolated_input(id)
version = cutoff(parent, id, sha) version = cutoff(parent, id, sha)
raise ::Gitlab::Graphql::Errors::ResourceNotAvailable, 'cutoff not found' unless version.present? raise ::Gitlab::Graphql::Errors::ResourceNotAvailable, 'cutoff not found' unless version.present?
...@@ -47,8 +52,7 @@ module Resolvers ...@@ -47,8 +52,7 @@ module Resolvers
end end
end end
def specific_version(id, sha) def specific_version(gid, sha)
gid = GitlabSchema.parse_gid(id, expected_type: ::DesignManagement::Version) if id
find(sha: sha, version_id: gid&.model_id).first find(sha: sha, version_id: gid&.model_id).first
end end
...@@ -58,8 +62,8 @@ module Resolvers ...@@ -58,8 +62,8 @@ module Resolvers
.execute .execute
end end
def by_id(id) def by_id(gid)
GitlabSchema.object_from_id(id, expected_type: ::DesignManagement::Version).sync ::Gitlab::Graphql::Lazy.force(GitlabSchema.find_by_gid(gid))
end end
# Find an `at_version` argument passed to a parent node. # Find an `at_version` argument passed to a parent node.
...@@ -69,7 +73,11 @@ module Resolvers ...@@ -69,7 +73,11 @@ module Resolvers
# for consistency we should only present versions up to the given # for consistency we should only present versions up to the given
# version here. # version here.
def at_version_arg(parent) def at_version_arg(parent)
::Gitlab::Graphql::FindArgumentInParent.find(parent, :at_version, limit_depth: 4) # TODO: remove coercion when the compatibility layer is removed
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
version_id = ::Gitlab::Graphql::FindArgumentInParent.find(parent, :at_version, limit_depth: 4)
version_id &&= VersionID.coerce_isolated_input(version_id)
version_id
end end
end end
end end
......
...@@ -18,10 +18,14 @@ module Resolvers ...@@ -18,10 +18,14 @@ module Resolvers
required: false, required: false,
default_value: 'created_desc' default_value: 'created_desc'
def resolve(ids: nil, usernames: nil, sort: nil) argument :search, GraphQL::STRING_TYPE,
required: false,
description: "Query to search users by name, username, or primary email."
def resolve(ids: nil, usernames: nil, sort: nil, search: nil)
authorize! authorize!
::UsersFinder.new(context[:current_user], finder_params(ids, usernames, sort)).execute ::UsersFinder.new(context[:current_user], finder_params(ids, usernames, sort, search)).execute
end end
def ready?(**args) def ready?(**args)
...@@ -42,11 +46,12 @@ module Resolvers ...@@ -42,11 +46,12 @@ module Resolvers
private private
def finder_params(ids, usernames, sort) def finder_params(ids, usernames, sort, search)
params = {} params = {}
params[:sort] = sort if sort params[:sort] = sort if sort
params[:username] = usernames if usernames params[:username] = usernames if usernames
params[:id] = parse_gids(ids) if ids params[:id] = parse_gids(ids) if ids
params[:search] = search if search
params params
end end
......
...@@ -118,8 +118,7 @@ module Types ...@@ -118,8 +118,7 @@ module Types
resolver: Resolvers::MergeRequestPipelinesResolver resolver: Resolvers::MergeRequestPipelinesResolver
field :milestone, Types::MilestoneType, null: true, field :milestone, Types::MilestoneType, null: true,
description: 'The milestone of the merge request', description: 'The milestone of the merge request'
resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Milestone, obj.milestone_id).find }
field :assignees, Types::UserType.connection_type, null: true, complexity: 5, field :assignees, Types::UserType.connection_type, null: true, complexity: 5,
description: 'Assignees of the merge request' description: 'Assignees of the merge request'
field :author, Types::UserType, null: true, field :author, Types::UserType, null: true,
......
...@@ -14,6 +14,7 @@ module Types ...@@ -14,6 +14,7 @@ module Types
mount_mutation Mutations::AlertManagement::HttpIntegration::Create mount_mutation Mutations::AlertManagement::HttpIntegration::Create
mount_mutation Mutations::AlertManagement::HttpIntegration::Update mount_mutation Mutations::AlertManagement::HttpIntegration::Update
mount_mutation Mutations::AlertManagement::HttpIntegration::ResetToken mount_mutation Mutations::AlertManagement::HttpIntegration::ResetToken
mount_mutation Mutations::AlertManagement::HttpIntegration::Destroy
mount_mutation Mutations::AlertManagement::PrometheusIntegration::Create mount_mutation Mutations::AlertManagement::PrometheusIntegration::Create
mount_mutation Mutations::AlertManagement::PrometheusIntegration::Update mount_mutation Mutations::AlertManagement::PrometheusIntegration::Update
mount_mutation Mutations::AlertManagement::PrometheusIntegration::ResetToken mount_mutation Mutations::AlertManagement::PrometheusIntegration::ResetToken
......
...@@ -30,7 +30,7 @@ module OperationsHelper ...@@ -30,7 +30,7 @@ module OperationsHelper
'alerts_setup_url' => help_page_path('operations/incident_management/alert_integrations.md', anchor: 'generic-http-endpoint'), 'alerts_setup_url' => help_page_path('operations/incident_management/alert_integrations.md', anchor: 'generic-http-endpoint'),
'alerts_usage_url' => project_alert_management_index_path(@project), 'alerts_usage_url' => project_alert_management_index_path(@project),
'disabled' => disabled.to_s, 'disabled' => disabled.to_s,
'project_path' => project_path(@project) 'project_path' => @project.full_path
} }
end end
......
...@@ -92,11 +92,27 @@ module SearchHelper ...@@ -92,11 +92,27 @@ module SearchHelper
end end
end end
def search_entries_empty_message(scope, term) def search_entries_empty_message(scope, term, group, project)
(s_("SearchResults|We couldn't find any %{scope} matching %{term}") % { options = {
scope: search_entries_scope_label(scope, 0), scope: search_entries_scope_label(scope, 0),
term: "<code>#{h(term)}</code>" term: "<code>#{h(term)}</code>".html_safe
}).html_safe }
# We check project first because we have 3 possible combinations here:
# - group && project
# - group
# - group: nil, project: nil
if project
html_escape(_("We couldn't find any %{scope} matching %{term} in project %{project}")) % options.merge(
project: link_to(project.full_name, project_path(project), target: '_blank', rel: 'noopener noreferrer').html_safe
)
elsif group
html_escape(_("We couldn't find any %{scope} matching %{term} in group %{group}")) % options.merge(
group: link_to(group.full_name, group_path(group), target: '_blank', rel: 'noopener noreferrer').html_safe
)
else
html_escape(_("We couldn't find any %{scope} matching %{term}")) % options
end
end end
def repository_ref(project) def repository_ref(project)
......
...@@ -48,6 +48,8 @@ class ApplicationRecord < ActiveRecord::Base ...@@ -48,6 +48,8 @@ class ApplicationRecord < ActiveRecord::Base
def self.safe_find_or_create_by!(*args, &block) def self.safe_find_or_create_by!(*args, &block)
safe_find_or_create_by(*args, &block).tap do |record| safe_find_or_create_by(*args, &block).tap do |record|
raise ActiveRecord::RecordNotFound unless record.present?
record.validate! unless record.persisted? record.validate! unless record.persisted?
end end
end end
......
...@@ -589,7 +589,7 @@ class Group < Namespace ...@@ -589,7 +589,7 @@ class Group < Namespace
def update_two_factor_requirement def update_two_factor_requirement
return unless saved_change_to_require_two_factor_authentication? || saved_change_to_two_factor_grace_period? return unless saved_change_to_require_two_factor_authentication? || saved_change_to_two_factor_grace_period?
members_with_descendants.find_each(&:update_two_factor_requirement) direct_and_indirect_members.find_each(&:update_two_factor_requirement)
end end
def path_changed_hook def path_changed_hook
......
# frozen_string_literal: true
module AlertManagement
module HttpIntegrations
class DestroyService
# @param integration [AlertManagement::HttpIntegration]
# @param current_user [User]
def initialize(integration, current_user)
@integration = integration
@current_user = current_user
end
def execute
return error_no_permissions unless allowed?
return error_multiple_integrations unless Feature.enabled?(:multiple_http_integrations, integration.project)
if integration.destroy
success
else
error(integration.errors.full_messages.to_sentence)
end
end
private
attr_reader :integration, :current_user
def allowed?
current_user&.can?(:admin_operations, integration)
end
def error(message)
ServiceResponse.error(message: message)
end
def success
ServiceResponse.success(payload: { integration: integration })
end
def error_no_permissions
error(_('You have insufficient permissions to remove this HTTP integration'))
end
def error_multiple_integrations
error(_('Removing integrations is not supported for this project'))
end
end
end
end
...@@ -163,16 +163,18 @@ module Ci ...@@ -163,16 +163,18 @@ module Ci
end end
def ensure_pending_state def ensure_pending_state
Ci::BuildPendingState.create_or_find_by!( build_state = Ci::BuildPendingState.safe_find_or_create_by(
build_id: build.id, build_id: build.id,
state: params.fetch(:state), state: params.fetch(:state),
trace_checksum: params.fetch(:checksum), trace_checksum: params.fetch(:checksum),
failure_reason: params.dig(:failure_reason) failure_reason: params.dig(:failure_reason)
) )
rescue ActiveRecord::RecordNotFound
metrics.increment_trace_operation(operation: :conflict)
build.pending_state unless build_state.present?
metrics.increment_trace_operation(operation: :conflict)
end
build_state || build.pending_state
end end
## ##
......
- breadcrumb_title _("Dashboard") - breadcrumb_title _("Dashboard")
- page_title _("Dashboard") - page_title _("Dashboard")
- billable_users_url = help_page_path('subscriptions/self_managed/index', anchor: 'billable-users')
- billable_users_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer nofollow">'.html_safe % { url: billable_users_url }
- if @notices - if @notices
- @notices.each do |notice| - @notices.each do |notice|
...@@ -22,10 +24,20 @@ ...@@ -22,10 +24,20 @@
= link_to(s_('AdminArea|New project'), new_project_path, class: "btn gl-button btn-success gl-w-full") = link_to(s_('AdminArea|New project'), new_project_path, class: "btn gl-button btn-success gl-w-full")
.col-sm-4 .col-sm-4
.info-well.dark-well .info-well.dark-well
.well-segment.well-centered .well-segment.well-centered.gl-text-center
= link_to admin_users_path do = link_to admin_users_path do
%h3.text-center %h3.gl-display-inline-block.gl-mb-0
= s_('AdminArea|Users: %{number_of_users}') % { number_of_users: approximate_count_with_delimiters(@counts, User) } = s_('AdminArea|Users: %{number_of_users}') % { number_of_users: approximate_count_with_delimiters(@counts, User) }
%span.gl-outline-0.gl-ml-2{ href: "#", tabindex: "0", data: { container: "body",
toggle: "popover",
placement: "top",
html: "true",
trigger: "focus",
content: s_("AdminArea|All users created in the instance, including users who are not %{billable_users_link_start}billable users%{billable_users_link_end}.").html_safe % { billable_users_link_start: billable_users_link_start, billable_users_link_end: '</a>'.html_safe },
} }
= sprite_icon('question', size: 16, css_class: 'gl-text-gray-700 gl-mb-1')
%hr %hr
.btn-group.d-flex{ role: 'group' } .btn-group.d-flex{ role: 'group' }
= link_to s_('AdminArea|New user'), new_admin_user_path, class: "btn gl-button btn-success gl-w-full" = link_to s_('AdminArea|New user'), new_admin_user_path, class: "btn gl-button btn-success gl-w-full"
......
...@@ -4,17 +4,7 @@ ...@@ -4,17 +4,7 @@
.container .container
.gl-mt-3 .gl-mt-3
- if Feature.enabled?(:devops_adoption) - if Feature.enabled?(:devops_adoption)
%h2 = render_if_exists 'admin/dev_ops_report/devops_tabs'
= _('DevOps Report')
%ul.nav-links.nav-tabs.nav.js-devops-tabs{ role: 'tablist' }
= render 'tab', active: true, title: _('DevOps Score'), target: '#devops_score_pane'
= render 'tab', active: false, title: _('Adoption'), target: '#devops_adoption_pane'
.tab-content
.tab-pane.active#devops_score_pane
= render 'report'
.tab-pane#devops_adoption_pane
.js-devops-adoption{ data: { empty_state_svg_path: image_path('illustrations/monitoring/getting_started.svg') } }
- else - else
= render 'report' = render 'report'
...@@ -2,17 +2,15 @@ ...@@ -2,17 +2,15 @@
%li.note.note-discussion.timeline-entry.unstyled-comments %li.note.note-discussion.timeline-entry.unstyled-comments
.timeline-entry-inner .timeline-entry-inner
.timeline-content .timeline-content
.discussion.js-toggle-container{ data: { discussion_id: discussion.id } } .discussion.js-toggle-container{ data: { discussion_id: discussion.id, is_expanded: expanded.to_s } }
.discussion-header .discussion-header
.timeline-icon .timeline-icon
= link_to user_path(discussion.author) do = link_to user_path(discussion.author) do
= image_tag avatar_icon_for_user(discussion.author), class: "avatar s40" = image_tag avatar_icon_for_user(discussion.author), class: "avatar s40"
.discussion-actions .discussion-actions
%button.note-action-button.discussion-toggle-button.js-toggle-button{ type: "button", class: ("js-toggle-lazy-diff" unless expanded) } %button.note-action-button.discussion-toggle-button.js-toggle-button{ type: "button", class: ("js-toggle-lazy-diff" unless expanded) }
- if expanded = sprite_icon('chevron-up', css_class: "js-sidebar-collapse #{'hidden' unless expanded}")
= icon("chevron-up") = sprite_icon('chevron-down', css_class: "js-sidebar-expand #{'hidden' if expanded}")
- else
= icon("chevron-down")
= _('Toggle thread') = _('Toggle thread')
= link_to_member(@project, discussion.author, avatar: false) = link_to_member(@project, discussion.author, avatar: false)
......
.search_box .search_box.gl-my-8
.search_glyph .search_glyph
%h4 %h4
= sprite_icon('search', size: 24, css_class: 'gl-vertical-align-text-bottom') = sprite_icon('search', size: 24, css_class: 'gl-vertical-align-text-bottom')
= search_entries_empty_message(@scope, @search_term) = search_entries_empty_message(@scope, @search_term, @group, @project)
---
title: Add EC2 to AutoDevOps template
merge_request: 45651
author:
type: changed
---
title: Resolve User stuck in 2FA setup page even if group disable 2FA enforce
merge_request: 46432
author:
type: fixed
---
title: Replace fa-chevron-up with GitLab SVG icon
merge_request: 46118
author:
type: changed
---
title: Track usage of CI Secrets Management (Vault secrets)
merge_request: 46515
author:
type: added
---
title: Remove all records from `security_findings` table
merge_request: 44312
author:
type: fixed
---
title: Remove feedback alert from on-demand scans form
merge_request: 45217
author:
type: changed
---
title: Improve empty search results message for group and project scopes
merge_request: 46237
author:
type: changed
---
title: Fix IDE issues with special characters
merge_request: 46398
author:
type: fixed
---
title: Add search param to Users GraphQL type
merge_request: 46609
author:
type: added
---
title: 'Auto Deploy: fixes issues for fetching other charts from stable repo'
merge_request: 46531
author:
type: fixed
---
title: Fix logging handling for API integer params
merge_request: 46551
author:
type: fixed
---
title: Assign new incoming diff lines for renamed files to the correct view type
merge_request: 46823
author:
type: fixed
---
title: Ensure that copy to clipboard button is visible
merge_request: 46466
author:
type: fixed
---
title: Fix linebreak issue in last commit anchor
merge_request: 46643
author:
type: fixed
---
title: Fix example responses for Group Issue Board creation API in the docs
merge_request: 46760
author: Takuya Noguchi
type: fixed
---
title: Fix compliance framework database migration on CE instances
merge_request: 46761
author:
type: fixed
---
title: Show "No user list selected" in feature flags
merge_request: 46790
author:
type: fixed
---
title: Add total projects imported usage ping
merge_request: 46541
author:
type: added
---
title: Fix 'File name too long' error happening during Project Export when exporting
project uploads
merge_request: 46674
author:
type: fixed
---
title: Add `has_vulnerabilities` column into project_settings table
merge_request: 45944
author:
type: added
---
title: Fixed create merge request dropdown not re-opening after typing invalid source
branch
merge_request: 46802
author:
type: fixed
---
title: Add environment variables to override backup/restore DB settings
merge_request: 45855
author:
type: added
--- ---
name: ci_secrets_management name: usage_data_i_ci_secrets_management_vault_build_created
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/42055 introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46515
rollout_issue_url: rollout_issue_url:
milestone: '13.6'
type: development
group: group::release management group: group::release management
type: licensed
default_enabled: true default_enabled: true
---
name: incident_sla
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43648
rollout_issue_url:
group: group::health
type: licensed
default_enabled: true
...@@ -14,10 +14,8 @@ if Gitlab.ee? && Gitlab.dev_or_test_env? ...@@ -14,10 +14,8 @@ if Gitlab.ee? && Gitlab.dev_or_test_env?
# being unique to licensed names. These feature flags should be reworked to # being unique to licensed names. These feature flags should be reworked to
# be "development" with explicit check # be "development" with explicit check
IGNORED_FEATURE_FLAGS = %i[ IGNORED_FEATURE_FLAGS = %i[
ci_secrets_management
feature_flags_related_issues feature_flags_related_issues
group_wikis group_wikis
incident_sla
swimlanes swimlanes
minimal_access_role minimal_access_role
].to_set ].to_set
......
...@@ -11,7 +11,8 @@ ActiveSupport::Notifications.subscribe(/rack_attack/) do |name, start, finish, r ...@@ -11,7 +11,8 @@ ActiveSupport::Notifications.subscribe(/rack_attack/) do |name, start, finish, r
env: req.env['rack.attack.match_type'], env: req.env['rack.attack.match_type'],
remote_ip: req.ip, remote_ip: req.ip,
request_method: req.request_method, request_method: req.request_method,
path: req.fullpath path: req.fullpath,
matched: req.env['rack.attack.matched']
} }
throttles_with_user_information = [ throttles_with_user_information = [
...@@ -25,9 +26,8 @@ ActiveSupport::Notifications.subscribe(/rack_attack/) do |name, start, finish, r ...@@ -25,9 +26,8 @@ ActiveSupport::Notifications.subscribe(/rack_attack/) do |name, start, finish, r
user_id = req.env['rack.attack.match_discriminator'] user_id = req.env['rack.attack.match_discriminator']
user = User.find_by(id: user_id) user = User.find_by(id: user_id)
rack_attack_info[:throttle_type] = req.env['rack.attack.matched']
rack_attack_info[:user_id] = user_id rack_attack_info[:user_id] = user_id
rack_attack_info[:username] = user.username unless user.nil? rack_attack_info['meta.user'] = user.username unless user.nil?
end end
Gitlab::AuthLogger.error(rack_attack_info) Gitlab::AuthLogger.error(rack_attack_info)
......
# frozen_string_literal: true
class AddHasVulnerabilitiesIntoProjectSettings < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
with_lock_retries do
add_column :project_settings, :has_vulnerabilities, :boolean, default: false, null: false
end
end
def down
with_lock_retries do
remove_column :project_settings, :has_vulnerabilities
end
end
end
# frozen_string_literal: true
class IndexProjectSettingsOnProjectIdPartially < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
INDEX_NAME = 'index_project_settings_on_project_id_partially'
disable_ddl_transaction!
def up
add_concurrent_index :project_settings, :project_id, name: INDEX_NAME, where: 'has_vulnerabilities IS TRUE'
end
def down
remove_concurrent_index_by_name :project_settings, INDEX_NAME
end
end
...@@ -52,8 +52,6 @@ class MigrateComplianceFrameworkEnumToDatabaseFrameworkRecord < ActiveRecord::Mi ...@@ -52,8 +52,6 @@ class MigrateComplianceFrameworkEnumToDatabaseFrameworkRecord < ActiveRecord::Mi
end end
def up def up
return unless Gitlab.ee?
TmpComplianceFramework.reset_column_information TmpComplianceFramework.reset_column_information
TmpProjectSettings.reset_column_information TmpProjectSettings.reset_column_information
......
# frozen_string_literal: true
class TruncateSecurityFindingsTable < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
return unless Gitlab.dev_env_or_com?
with_lock_retries do
connection.execute('TRUNCATE security_findings RESTART IDENTITY')
end
end
def down
# no-op
end
end
205cb628e9637bcd1acb90c5211b71b51015fa5f50aadcacd5fbafc4f09c00d0
\ No newline at end of file
9f942de6f83629a144e9d460b4bed7a246afe95275b5913745109fc0ab9dacc1
\ No newline at end of file
55ffd18d5f55ee0fd51a31d50cf2d51595740c72ca23d5134d93e2da3fc186ff
\ No newline at end of file
...@@ -15191,6 +15191,7 @@ CREATE TABLE project_settings ( ...@@ -15191,6 +15191,7 @@ CREATE TABLE project_settings (
allow_merge_on_skipped_pipeline boolean, allow_merge_on_skipped_pipeline boolean,
squash_option smallint DEFAULT 3, squash_option smallint DEFAULT 3,
has_confluence boolean DEFAULT false NOT NULL, has_confluence boolean DEFAULT false NOT NULL,
has_vulnerabilities boolean DEFAULT false NOT NULL,
CONSTRAINT check_bde223416c CHECK ((show_default_award_emojis IS NOT NULL)) CONSTRAINT check_bde223416c CHECK ((show_default_award_emojis IS NOT NULL))
); );
...@@ -21461,6 +21462,8 @@ CREATE UNIQUE INDEX index_project_repository_states_on_project_id ON project_rep ...@@ -21461,6 +21462,8 @@ CREATE UNIQUE INDEX index_project_repository_states_on_project_id ON project_rep
CREATE INDEX index_project_repository_storage_moves_on_project_id ON project_repository_storage_moves USING btree (project_id); CREATE INDEX index_project_repository_storage_moves_on_project_id ON project_repository_storage_moves USING btree (project_id);
CREATE INDEX index_project_settings_on_project_id_partially ON project_settings USING btree (project_id) WHERE (has_vulnerabilities IS TRUE);
CREATE UNIQUE INDEX index_project_settings_on_push_rule_id ON project_settings USING btree (push_rule_id); CREATE UNIQUE INDEX index_project_settings_on_push_rule_id ON project_settings USING btree (push_rule_id);
CREATE INDEX index_project_statistics_on_namespace_id ON project_statistics USING btree (namespace_id); CREATE INDEX index_project_statistics_on_namespace_id ON project_statistics USING btree (namespace_id);
......
...@@ -17,7 +17,7 @@ This integration works with most LDAP-compliant directory servers, including: ...@@ -17,7 +17,7 @@ This integration works with most LDAP-compliant directory servers, including:
- Open LDAP - Open LDAP
- 389 Server - 389 Server
Users added through LDAP take a [licensed seat](../../../subscriptions/self_managed/index.md#choose-the-number-of-users). Users added through LDAP take a [licensed seat](../../../subscriptions/self_managed/index.md#billable-users).
GitLab Enterprise Editions (EE) include enhanced integration, GitLab Enterprise Editions (EE) include enhanced integration,
including group membership syncing as well as multiple LDAP servers support. including group membership syncing as well as multiple LDAP servers support.
......
...@@ -17,12 +17,9 @@ a service networking solution that you can manage by using `/etc/gitlab/gitlab.r ...@@ -17,12 +17,9 @@ a service networking solution that you can manage by using `/etc/gitlab/gitlab.r
## Configure the Consul nodes ## Configure the Consul nodes
NOTE: **Important:** After you review the [reference architecture](reference_architectures/index.md#available-reference-architectures)
Before proceeding, refer to the documentation to determine the number of Consul server nodes you should have,
[available reference architectures](reference_architectures/index.md#available-reference-architectures) on _each_ Consul server node:
to find out how many Consul server nodes you should have.
On **each** Consul server node perform the following:
1. Follow the instructions to [install](https://about.gitlab.com/install/) 1. Follow the instructions to [install](https://about.gitlab.com/install/)
GitLab by choosing your preferred platform, but do not supply the GitLab by choosing your preferred platform, but do not supply the
...@@ -93,10 +90,9 @@ Consult the [troubleshooting section](#troubleshooting-consul) if the cluster is ...@@ -93,10 +90,9 @@ Consult the [troubleshooting section](#troubleshooting-consul) if the cluster is
able to recover after the upgrade. The [outage recovery](#outage-recovery) may able to recover after the upgrade. The [outage recovery](#outage-recovery) may
be of particular interest. be of particular interest.
NOTE: **Note:** GitLab uses Consul to store only easily regenerated, transient data. If the
GitLab uses Consul to store only transient data that is easily regenerated. If bundled Consul wasn't used by any process other than GitLab itself, you can
the bundled Consul was not used by any process other than GitLab itself, then [rebuild the cluster from scratch](#recreate-from-scratch).
[rebuilding the cluster from scratch](#recreate-from-scratch) is fine.
## Troubleshooting Consul ## Troubleshooting Consul
......
...@@ -36,8 +36,8 @@ error, it's very important that you [**provide feedback**](https://gitlab.com/gi ...@@ -36,8 +36,8 @@ error, it's very important that you [**provide feedback**](https://gitlab.com/gi
as possible so we can improve or fix it while behind a flag. When you upgrade as possible so we can improve or fix it while behind a flag. When you upgrade
GitLab to an earlier version, the feature flag status may change. GitLab to an earlier version, the feature flag status may change.
NOTE: **Note:** CAUTION: **Caution:**
Mind that features deployed behind feature flags may not be ready for Features deployed behind feature flags may not be ready for
production use. However, disabling features behind flags that were deployed production use. However, disabling features behind flags that were deployed
enabled by default may also present a risk. If they're enabled, we recommend enabled by default may also present a risk. If they're enabled, we recommend
you leave them as-is. you leave them as-is.
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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