Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-10-09 00:08:41 +00:00
parent b0980f5557
commit c02e2a5ef6
113 changed files with 978 additions and 1046 deletions

View File

@ -151,3 +151,20 @@ dependency_scanning:
reports:
dependency_scanning: gl-dependency-scanning-report.json
expire_in: 1 week # GitLab-specific
license_scanning:
extends:
- .default-retry
- .reports:rules:license_scanning
stage: test
image:
name: "registry.gitlab.com/gitlab-org/security-products/analyzers/license-finder:3"
entrypoint: [""]
needs: []
script:
- /run.sh analyze .
artifacts:
reports:
license_scanning: gl-license-scanning-report.json
expire_in: 1 week # GitLab-specific
dependencies: []

View File

@ -719,6 +719,14 @@
- <<: *if-master-schedule-nightly
allow_failure: true
.reports:rules:license_scanning:
rules:
- if: '$LICENSE_SCANNING_DISABLED || $GITLAB_FEATURES !~ /\blicense_scanning\b/'
when: never
- <<: *if-default-refs
changes: *code-backstage-qa-patterns
allow_failure: true
################
# Review rules #
################

View File

@ -401,7 +401,7 @@ end
# Gems required in omnibus-gitlab pipeline
group :development, :test, :omnibus do
gem 'license_finder', '~> 5.4', require: false
gem 'license_finder', '~> 6.0', require: false
end
group :test do

View File

@ -643,9 +643,9 @@ GEM
actionmailer (>= 3.2)
letter_opener (~> 1.0)
railties (>= 3.2)
license_finder (5.4.0)
license_finder (6.0.0)
bundler
rubyzip
rubyzip (>= 1, < 3)
thor
toml (= 0.2.0)
with_env (= 1.1.0)
@ -1374,7 +1374,7 @@ DEPENDENCIES
kramdown (~> 2.3.0)
kubeclient (~> 4.6.0)
letter_opener_web (~> 1.3.4)
license_finder (~> 5.4)
license_finder (~> 6.0)
licensee (~> 8.9)
lockbox (~> 0.3.3)
lograge (~> 0.5)

View File

@ -5,3 +5,4 @@ export const EDITOR_LITE_INSTANCE_ERROR_NO_EL = __(
);
export const URI_PREFIX = 'gitlab';
export const CONTENT_UPDATE_DEBOUNCE = 250;

View File

@ -1,17 +1,13 @@
<script>
import { GlAlert, GlLoadingIcon, GlToggle } from '@gitlab/ui';
import { createNamespacedHelpers } from 'vuex';
import { mapState, mapActions } from 'vuex';
import axios from '~/lib/utils/axios_utils';
import { sprintf, s__ } from '~/locale';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { LEGACY_FLAG, NEW_FLAG_ALERT } from '../constants';
import store from '../store/index';
import FeatureFlagForm from './form.vue';
const { mapState, mapActions } = createNamespacedHelpers('edit');
export default {
store,
components: {
GlAlert,
GlLoadingIcon,
@ -20,14 +16,6 @@ export default {
},
mixins: [glFeatureFlagMixin()],
props: {
endpoint: {
type: String,
required: true,
},
path: {
type: String,
required: true,
},
environmentsEndpoint: {
type: String,
required: true,
@ -71,6 +59,7 @@ export default {
},
computed: {
...mapState([
'path',
'error',
'name',
'description',
@ -110,17 +99,10 @@ export default {
},
},
created() {
this.setPath(this.path);
return this.setEndpoint(this.endpoint).then(() => this.fetchFeatureFlag());
return this.fetchFeatureFlag();
},
methods: {
...mapActions([
'updateFeatureFlag',
'setEndpoint',
'setPath',
'fetchFeatureFlag',
'toggleActive',
]),
...mapActions(['updateFeatureFlag', 'fetchFeatureFlag', 'toggleActive']),
dismissNewVersionFlagAlert() {
this.userShouldSeeNewFlagAlert = false;
axios.post(this.userCalloutsPath, {

View File

@ -1,12 +1,11 @@
<script>
import { createNamespacedHelpers } from 'vuex';
import { mapState, mapActions } from 'vuex';
import { isEmpty } from 'lodash';
import { GlButton, GlModalDirective, GlTabs } from '@gitlab/ui';
import { FEATURE_FLAG_SCOPE, USER_LIST_SCOPE } from '../constants';
import FeatureFlagsTab from './feature_flags_tab.vue';
import FeatureFlagsTable from './feature_flags_table.vue';
import UserListsTable from './user_lists_table.vue';
import store from '../store';
import { s__ } from '~/locale';
import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
import {
@ -17,12 +16,9 @@ import {
import ConfigureFeatureFlagsModal from './configure_feature_flags_modal.vue';
const { mapState, mapActions } = createNamespacedHelpers('index');
const SCOPES = { FEATURE_FLAG_SCOPE, USER_LIST_SCOPE };
export default {
store,
components: {
FeatureFlagsTable,
UserListsTable,
@ -36,14 +32,6 @@ export default {
GlModal: GlModalDirective,
},
props: {
endpoint: {
type: String,
required: true,
},
projectId: {
type: String,
required: true,
},
csrfToken: {
type: String,
required: true,
@ -56,19 +44,10 @@ export default {
type: String,
required: true,
},
rotateInstanceIdPath: {
type: String,
required: false,
default: '',
},
unleashApiUrl: {
type: String,
required: true,
},
unleashApiInstanceId: {
type: String,
required: true,
},
canUserConfigure: {
type: Boolean,
required: true,
@ -144,23 +123,15 @@ export default {
},
},
created() {
this.setFeatureFlagsEndpoint(this.endpoint);
this.setFeatureFlagsOptions({ scope: this.scope, page: this.page });
this.setProjectId(this.projectId);
this.fetchFeatureFlags();
this.fetchUserLists();
this.setInstanceId(this.unleashApiInstanceId);
this.setInstanceIdEndpoint(this.rotateInstanceIdPath);
},
methods: {
...mapActions([
'setFeatureFlagsEndpoint',
'setFeatureFlagsOptions',
'fetchFeatureFlags',
'fetchUserLists',
'setInstanceIdEndpoint',
'setInstanceId',
'setProjectId',
'rotateInstanceId',
'toggleFeatureFlag',
'deleteUserList',

View File

@ -28,7 +28,7 @@ import {
NEW_VERSION_FLAG,
LEGACY_FLAG,
} from '../constants';
import { createNewEnvironmentScope } from '../store/modules/helpers';
import { createNewEnvironmentScope } from '../store/helpers';
export default {
components: {

View File

@ -1,8 +1,7 @@
<script>
import { createNamespacedHelpers } from 'vuex';
import { mapState, mapActions } from 'vuex';
import { GlAlert } from '@gitlab/ui';
import axios from '~/lib/utils/axios_utils';
import store from '../store/index';
import FeatureFlagForm from './form.vue';
import {
LEGACY_FLAG,
@ -10,28 +9,17 @@ import {
NEW_FLAG_ALERT,
ROLLOUT_STRATEGY_ALL_USERS,
} from '../constants';
import { createNewEnvironmentScope } from '../store/modules/helpers';
import { createNewEnvironmentScope } from '../store/helpers';
import featureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
const { mapState, mapActions } = createNamespacedHelpers('new');
export default {
store,
components: {
GlAlert,
FeatureFlagForm,
},
mixins: [featureFlagsMixin()],
props: {
endpoint: {
type: String,
required: true,
},
path: {
type: String,
required: true,
},
environmentsEndpoint: {
type: String,
required: true,
@ -64,7 +52,7 @@ export default {
newFlagAlert: NEW_FLAG_ALERT,
},
computed: {
...mapState(['error']),
...mapState(['error', 'path']),
scopes() {
return [
createNewEnvironmentScope(
@ -89,12 +77,8 @@ export default {
return [{ name: ROLLOUT_STRATEGY_ALL_USERS, parameters: {}, scopes: [] }];
},
},
created() {
this.setEndpoint(this.endpoint);
this.setPath(this.path);
},
methods: {
...mapActions(['createFeatureFlag', 'setEndpoint', 'setPath']),
...mapActions(['createFeatureFlag']),
dismissNewVersionFlagAlert() {
this.userShouldSeeNewFlagAlert = false;
axios.post(this.userCalloutsPath, {

View File

@ -1,25 +1,30 @@
import Vue from 'vue';
import EditFeatureFlag from '~/feature_flags/components/edit_feature_flag.vue';
import Vuex from 'vuex';
import { parseBoolean } from '~/lib/utils/common_utils';
import createStore from './store/edit';
import EditFeatureFlag from './components/edit_feature_flag.vue';
Vue.use(Vuex);
export default () => {
const el = document.querySelector('#js-edit-feature-flag');
const { environmentsScopeDocsPath, strategyTypeDocsPagePath } = el.dataset;
const {
environmentsScopeDocsPath,
strategyTypeDocsPagePath,
endpoint,
featureFlagsPath,
} = el.dataset;
return new Vue({
store: createStore({ endpoint, path: featureFlagsPath }),
el,
components: {
EditFeatureFlag,
},
provide: {
environmentsScopeDocsPath,
strategyTypeDocsPagePath,
},
render(createElement) {
return createElement('edit-feature-flag', {
return createElement(EditFeatureFlag, {
props: {
endpoint: el.dataset.endpoint,
path: el.dataset.featureFlagsPath,
environmentsEndpoint: el.dataset.environmentsEndpoint,
projectId: el.dataset.projectId,
featureFlagIssuesEndpoint: el.dataset.featureFlagIssuesEndpoint,

View File

@ -1,41 +1,47 @@
import Vue from 'vue';
import FeatureFlagsComponent from '~/feature_flags/components/feature_flags.vue';
import Vuex from 'vuex';
import csrf from '~/lib/utils/csrf';
import FeatureFlagsComponent from './components/feature_flags.vue';
import createStore from './store/index';
export default () =>
new Vue({
el: '#feature-flags-vue',
components: {
FeatureFlagsComponent,
},
data() {
return {
dataset: document.querySelector(this.$options.el).dataset,
};
},
Vue.use(Vuex);
export default () => {
const el = document.querySelector('#feature-flags-vue');
const {
projectName,
featureFlagsHelpPagePath,
errorStateSvgPath,
endpoint,
projectId,
unleashApiInstanceId,
rotateInstanceIdPath,
} = el.dataset;
return new Vue({
el,
store: createStore({ endpoint, projectId, unleashApiInstanceId, rotateInstanceIdPath }),
provide() {
return {
projectName: this.dataset.projectName,
featureFlagsHelpPagePath: this.dataset.featureFlagsHelpPagePath,
errorStateSvgPath: this.dataset.errorStateSvgPath,
projectName,
featureFlagsHelpPagePath,
errorStateSvgPath,
};
},
render(createElement) {
return createElement('feature-flags-component', {
return createElement(FeatureFlagsComponent, {
props: {
endpoint: this.dataset.endpoint,
projectId: this.dataset.projectId,
featureFlagsClientLibrariesHelpPagePath: this.dataset
.featureFlagsClientLibrariesHelpPagePath,
featureFlagsClientExampleHelpPagePath: this.dataset.featureFlagsClientExampleHelpPagePath,
unleashApiUrl: this.dataset.unleashApiUrl,
unleashApiInstanceId: this.dataset.unleashApiInstanceId || '',
featureFlagsClientLibrariesHelpPagePath:
el.dataset.featureFlagsClientLibrariesHelpPagePath,
featureFlagsClientExampleHelpPagePath: el.dataset.featureFlagsClientExampleHelpPagePath,
unleashApiUrl: el.dataset.unleashApiUrl,
csrfToken: csrf.token,
canUserConfigure: this.dataset.canUserAdminFeatureFlag,
newFeatureFlagPath: this.dataset.newFeatureFlagPath,
rotateInstanceIdPath: this.dataset.rotateInstanceIdPath,
newUserListPath: this.dataset.newUserListPath,
canUserConfigure: el.dataset.canUserAdminFeatureFlag,
newFeatureFlagPath: el.dataset.newFeatureFlagPath,
newUserListPath: el.dataset.newUserListPath,
},
});
},
});
};

View File

@ -1,25 +1,30 @@
import Vue from 'vue';
import NewFeatureFlag from '~/feature_flags/components/new_feature_flag.vue';
import Vuex from 'vuex';
import { parseBoolean } from '~/lib/utils/common_utils';
import createStore from './store/new';
import NewFeatureFlag from './components/new_feature_flag.vue';
Vue.use(Vuex);
export default () => {
const el = document.querySelector('#js-new-feature-flag');
const { environmentsScopeDocsPath, strategyTypeDocsPagePath } = el.dataset;
const {
environmentsScopeDocsPath,
strategyTypeDocsPagePath,
endpoint,
featureFlagsPath,
} = el.dataset;
return new Vue({
el,
components: {
NewFeatureFlag,
},
store: createStore({ endpoint, path: featureFlagsPath }),
provide: {
environmentsScopeDocsPath,
strategyTypeDocsPagePath,
},
render(createElement) {
return createElement('new-feature-flag', {
return createElement(NewFeatureFlag, {
props: {
endpoint: el.dataset.endpoint,
path: el.dataset.featureFlagsPath,
environmentsEndpoint: el.dataset.environmentsEndpoint,
projectId: el.dataset.projectId,
userCalloutsPath: el.dataset.userCalloutsPath,

View File

@ -3,23 +3,9 @@ import axios from '~/lib/utils/axios_utils';
import { visitUrl } from '~/lib/utils/url_utility';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import { __ } from '~/locale';
import { NEW_VERSION_FLAG } from '../../../constants';
import { NEW_VERSION_FLAG } from '../../constants';
import { mapFromScopesViewModel, mapStrategiesToRails } from '../helpers';
/**
* Commits mutation to set the main endpoint
* @param {String} endpoint
*/
export const setEndpoint = ({ commit }, endpoint) => commit(types.SET_ENDPOINT, endpoint);
/**
* Commits mutation to set the feature flag path.
* Used to redirect the user after form submission
*
* @param {String} path
*/
export const setPath = ({ commit }, path) => commit(types.SET_PATH, path);
/**
* Handles the edition of a feature flag.
*

View File

@ -0,0 +1,11 @@
import Vuex from 'vuex';
import state from './state';
import * as actions from './actions';
import mutations from './mutations';
export default data =>
new Vuex.Store({
actions,
mutations,
state: state(data),
});

View File

@ -1,6 +1,3 @@
export const SET_ENDPOINT = 'SET_ENDPOINT';
export const SET_PATH = 'SET_PATH';
export const REQUEST_UPDATE_FEATURE_FLAG = 'REQUEST_UPDATE_FEATURE_FLAG';
export const RECEIVE_UPDATE_FEATURE_FLAG_SUCCESS = 'RECEIVE_UPDATE_FEATURE_FLAG_SUCCESS';
export const RECEIVE_UPDATE_FEATURE_FLAG_ERROR = 'RECEIVE_UPDATE_FEATURE_FLAG_ERROR';

View File

@ -1,14 +1,8 @@
import * as types from './mutation_types';
import { mapToScopesViewModel, mapStrategiesToViewModel } from '../helpers';
import { LEGACY_FLAG } from '../../../constants';
import { LEGACY_FLAG } from '../../constants';
export default {
[types.SET_ENDPOINT](state, endpoint) {
state.endpoint = endpoint;
},
[types.SET_PATH](state, path) {
state.path = path;
},
[types.REQUEST_FEATURE_FLAG](state) {
state.isLoading = true;
},

View File

@ -1,8 +1,8 @@
import { LEGACY_FLAG } from '../../../constants';
import { LEGACY_FLAG } from '../../constants';
export default () => ({
endpoint: null,
path: null,
export default ({ path, endpoint }) => ({
endpoint,
path,
isSendingRequest: false,
error: [],

View File

@ -10,7 +10,7 @@ import {
fetchPercentageParams,
fetchUserIdParams,
LEGACY_FLAG,
} from '../../constants';
} from '../constants';
/**
* Converts raw scope objects fetched from the API into an array of scope

View File

@ -1,18 +0,0 @@
import Vue from 'vue';
import Vuex from 'vuex';
import indexModule from './modules/index';
import newModule from './modules/new';
import editModule from './modules/edit';
Vue.use(Vuex);
export const createStore = () =>
new Vuex.Store({
modules: {
index: indexModule,
new: newModule,
edit: editModule,
},
});
export default createStore();

View File

@ -2,19 +2,9 @@ import Api from '~/api';
import * as types from './mutation_types';
import axios from '~/lib/utils/axios_utils';
export const setFeatureFlagsEndpoint = ({ commit }, endpoint) =>
commit(types.SET_FEATURE_FLAGS_ENDPOINT, endpoint);
export const setFeatureFlagsOptions = ({ commit }, options) =>
commit(types.SET_FEATURE_FLAGS_OPTIONS, options);
export const setInstanceIdEndpoint = ({ commit }, endpoint) =>
commit(types.SET_INSTANCE_ID_ENDPOINT, endpoint);
export const setProjectId = ({ commit }, endpoint) => commit(types.SET_PROJECT_ID, endpoint);
export const setInstanceId = ({ commit }, instanceId) => commit(types.SET_INSTANCE_ID, instanceId);
export const fetchFeatureFlags = ({ state, dispatch }) => {
dispatch('requestFeatureFlags');

View File

@ -0,0 +1,11 @@
import Vuex from 'vuex';
import state from './state';
import * as actions from './actions';
import mutations from './mutations';
export default data =>
new Vuex.Store({
actions,
mutations,
state: state(data),
});

View File

@ -1,8 +1,4 @@
export const SET_FEATURE_FLAGS_ENDPOINT = 'SET_FEATURE_FLAGS_ENDPOINT';
export const SET_FEATURE_FLAGS_OPTIONS = 'SET_FEATURE_FLAGS_OPTIONS';
export const SET_INSTANCE_ID_ENDPOINT = 'SET_INSTANCE_ID_ENDPOINT';
export const SET_INSTANCE_ID = 'SET_INSTANCE_ID';
export const SET_PROJECT_ID = 'SET_PROJECT_ID';
export const REQUEST_FEATURE_FLAGS = 'REQUEST_FEATURE_FLAGS';
export const RECEIVE_FEATURE_FLAGS_SUCCESS = 'RECEIVE_FEATURE_FLAGS_SUCCESS';

View File

@ -1,7 +1,7 @@
import Vue from 'vue';
import * as types from './mutation_types';
import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils';
import { FEATURE_FLAG_SCOPE, USER_LIST_SCOPE } from '../../../constants';
import { FEATURE_FLAG_SCOPE, USER_LIST_SCOPE } from '../../constants';
import { mapToScopesViewModel } from '../helpers';
const mapFlag = flag => ({ ...flag, scopes: mapToScopesViewModel(flag.scopes || []) });
@ -23,21 +23,9 @@ const createPaginationInfo = (state, headers) => {
};
export default {
[types.SET_FEATURE_FLAGS_ENDPOINT](state, endpoint) {
state.endpoint = endpoint;
},
[types.SET_FEATURE_FLAGS_OPTIONS](state, options = {}) {
state.options = options;
},
[types.SET_INSTANCE_ID_ENDPOINT](state, endpoint) {
state.rotateEndpoint = endpoint;
},
[types.SET_INSTANCE_ID](state, instance) {
state.instanceId = instance;
},
[types.SET_PROJECT_ID](state, project) {
state.projectId = project;
},
[types.REQUEST_FEATURE_FLAGS](state) {
state.isLoading = true;
},

View File

@ -0,0 +1,18 @@
import { FEATURE_FLAG_SCOPE, USER_LIST_SCOPE } from '../../constants';
export default ({ endpoint, projectId, unleashApiInstanceId, rotateInstanceIdPath }) => ({
[FEATURE_FLAG_SCOPE]: [],
[USER_LIST_SCOPE]: [],
alerts: [],
count: {},
pageInfo: { [FEATURE_FLAG_SCOPE]: {}, [USER_LIST_SCOPE]: {} },
isLoading: true,
hasError: false,
endpoint,
rotateEndpoint: rotateInstanceIdPath,
instanceId: unleashApiInstanceId,
isRotating: false,
hasRotateError: false,
options: {},
projectId,
});

View File

@ -1,10 +0,0 @@
import state from './state';
import * as actions from './actions';
import mutations from './mutations';
export default {
namespaced: true,
actions,
mutations,
state: state(),
};

View File

@ -1,10 +0,0 @@
import state from './state';
import * as actions from './actions';
import mutations from './mutations';
export default {
namespaced: true,
actions,
mutations,
state: state(),
};

View File

@ -1,18 +0,0 @@
import { FEATURE_FLAG_SCOPE, USER_LIST_SCOPE } from '../../../constants';
export default () => ({
[FEATURE_FLAG_SCOPE]: [],
[USER_LIST_SCOPE]: [],
alerts: [],
count: {},
pageInfo: { [FEATURE_FLAG_SCOPE]: {}, [USER_LIST_SCOPE]: {} },
isLoading: true,
hasError: false,
endpoint: null,
rotateEndpoint: null,
instanceId: '',
isRotating: false,
hasRotateError: false,
options: {},
projectId: '',
});

View File

@ -1,10 +0,0 @@
import state from './state';
import * as actions from './actions';
import mutations from './mutations';
export default {
namespaced: true,
actions,
mutations,
state: state(),
};

View File

@ -1,6 +0,0 @@
export default () => ({
endpoint: null,
path: null,
isSendingRequest: false,
error: [],
});

View File

@ -1,23 +1,9 @@
import * as types from './mutation_types';
import axios from '~/lib/utils/axios_utils';
import { visitUrl } from '~/lib/utils/url_utility';
import { NEW_VERSION_FLAG } from '../../../constants';
import { NEW_VERSION_FLAG } from '../../constants';
import { mapFromScopesViewModel, mapStrategiesToRails } from '../helpers';
/**
* Commits mutation to set the main endpoint
* @param {String} endpoint
*/
export const setEndpoint = ({ commit }, endpoint) => commit(types.SET_ENDPOINT, endpoint);
/**
* Commits mutation to set the feature flag path.
* Used to redirect the user after form submission
*
* @param {String} path
*/
export const setPath = ({ commit }, path) => commit(types.SET_PATH, path);
/**
* Handles the creation of a new feature flag.
*

View File

@ -0,0 +1,11 @@
import Vuex from 'vuex';
import state from './state';
import * as actions from './actions';
import mutations from './mutations';
export default data =>
new Vuex.Store({
actions,
mutations,
state: state(data),
});

View File

@ -1,6 +1,3 @@
export const SET_ENDPOINT = 'SET_ENDPOINT';
export const SET_PATH = 'SET_PATH';
export const REQUEST_CREATE_FEATURE_FLAG = 'REQUEST_CREATE_FEATURE_FLAG';
export const RECEIVE_CREATE_FEATURE_FLAG_SUCCESS = 'RECEIVE_CREATE_FEATURE_FLAG_SUCCESS';
export const RECEIVE_CREATE_FEATURE_FLAG_ERROR = 'RECEIVE_CREATE_FEATURE_FLAG_ERROR';

View File

@ -1,12 +1,6 @@
import * as types from './mutation_types';
export default {
[types.SET_ENDPOINT](state, endpoint) {
state.endpoint = endpoint;
},
[types.SET_PATH](state, path) {
state.path = path;
},
[types.REQUEST_CREATE_FEATURE_FLAG](state) {
state.isSendingRequest = true;
state.error = [];

View File

@ -0,0 +1,6 @@
export default ({ endpoint, path }) => ({
endpoint,
path,
isSendingRequest: false,
error: [],
});

View File

@ -1,3 +1,3 @@
import initEditFeatureFlags from '~/feature_flags/edit';
document.addEventListener('DOMContentLoaded', initEditFeatureFlags);
initEditFeatureFlags();

View File

@ -1,3 +1,3 @@
import initFeatureFlags from '~/feature_flags';
document.addEventListener('DOMContentLoaded', initFeatureFlags);
initFeatureFlags();

View File

@ -1,3 +1,3 @@
import initNewFeatureFlags from '~/feature_flags/new';
document.addEventListener('DOMContentLoaded', initNewFeatureFlags);
initNewFeatureFlags();

View File

@ -0,0 +1,91 @@
<script>
import { debounce } from 'lodash';
import Editor from '~/editor/editor_lite';
import { CONTENT_UPDATE_DEBOUNCE } from '~/editor/constants';
function initEditorLite({ el, ...args }) {
const editor = new Editor({
scrollbar: {
alwaysConsumeMouseWheel: false,
},
});
return editor.createInstance({
el,
...args,
});
}
export default {
inheritAttrs: false,
props: {
value: {
type: String,
required: false,
default: '',
},
fileName: {
type: String,
required: false,
default: '',
},
// This is used to help uniquely create a monaco model
// even if two blob's share a file path.
fileGlobalId: {
type: String,
required: false,
default: '',
},
extensions: {
type: [String, Array],
required: false,
default: () => null,
},
editorOptions: {
type: Object,
required: false,
default: () => ({}),
},
},
data() {
return {
loading: true,
editor: null,
};
},
watch: {
fileName(newVal) {
this.editor.updateModelLanguage(newVal);
},
},
mounted() {
this.editor = initEditorLite({
el: this.$refs.editor,
blobPath: this.fileName,
blobContent: this.value,
blobGlobalId: this.fileGlobalId,
extensions: this.extensions,
...this.editorOptions,
});
this.editor.onDidChangeModelContent(
debounce(this.onFileChange.bind(this), CONTENT_UPDATE_DEBOUNCE),
);
},
beforeDestroy() {
this.editor.dispose();
},
methods: {
onFileChange() {
this.$emit('input', this.editor.getValue());
},
},
};
</script>
<template>
<div class="file-content code">
<div id="editor" ref="editor" data-editor-loading @editor-ready="$emit('editor-ready')">
<pre class="editor-loading-content">{{ value }}</pre>
</div>
</div>
</template>

View File

@ -6,6 +6,7 @@
@import '@gitlab/at.js/dist/css/jquery.atwho';
@import 'dropzone/dist/basic';
@import 'select2';
@import 'cropper/dist/cropper';
// GitLab UI framework
@import 'framework';

View File

@ -241,7 +241,8 @@ class GroupsController < Groups::ApplicationController
:two_factor_grace_period,
:project_creation_level,
:subgroup_creation_level,
:default_branch_protection
:default_branch_protection,
:default_branch_name
]
end

View File

@ -301,7 +301,6 @@ module ProjectsHelper
!disabled && !compact_mode
end
# overridden in EE
def settings_operations_available?
can?(current_user, :read_environment, @project)
end
@ -755,6 +754,7 @@ module ProjectsHelper
logs
product_analytics
metrics_dashboard
tracings
]
end

View File

@ -3,7 +3,19 @@
class NamespaceSetting < ApplicationRecord
belongs_to :namespace, inverse_of: :namespace_settings
validate :default_branch_name_content
NAMESPACE_SETTINGS_PARAMS = [:default_branch_name].freeze
self.primary_key = :namespace_id
def default_branch_name_content
return if default_branch_name.nil?
if default_branch_name.blank?
errors.add(:default_branch_name, "can not be an empty string")
end
end
end
NamespaceSetting.prepend_if_ee('EE::NamespaceSetting')

View File

@ -23,6 +23,8 @@ module Groups
before_assignment_hook(group, params)
handle_namespace_settings
group.assign_attributes(params)
begin
@ -40,6 +42,18 @@ module Groups
private
def handle_namespace_settings
settings_params = params.slice(*::NamespaceSetting::NAMESPACE_SETTINGS_PARAMS)
return if settings_params.empty?
::NamespaceSetting::NAMESPACE_SETTINGS_PARAMS.each do |nsp|
params.delete(nsp)
end
::NamespaceSettings::UpdateService.new(current_user, group, settings_params).execute
end
def valid_path_change_with_npm_packages?
return true unless group.packages_feature_enabled?
return true if params[:path].blank?

View File

@ -236,7 +236,7 @@
= _('Logs')
- if project_nav_tab? :environments
= render_if_exists "layouts/nav/sidebar/tracing_link"
= render "layouts/nav/sidebar/tracing_link"
- if project_nav_tab?(:error_tracking)
= nav_link(controller: :error_tracking) do

View File

@ -0,0 +1,7 @@
- return unless can?(current_user, :read_environment, @project)
- if project_nav_tab? :settings
= nav_link(controller: :tracings, action: [:show]) do
= link_to project_tracing_path(@project), title: _('Tracing') do
%span
= _('Tracing')

View File

@ -0,0 +1,33 @@
- setting = tracing_setting
- has_jaeger_url = setting.external_url.present?
%section.settings.border-0.no-animate
.settings-header{ :class => "border-top" }
%h3{ :class => "h4" }
= _("Jaeger tracing")
%button.btn.gl-button.js-settings-toggle{ type: 'button' }
= _('Expand')
%p
- if has_jaeger_url
- tracing_link = link_to sanitize(setting.external_url, scrubber: Rails::Html::TextOnlyScrubber.new), target: "_blank", rel: 'noopener noreferrer' do
%span
= _('Tracing')
= sprite_icon('external-link', css_class: 'ml-1 vertical-align-middle')
- else
- tracing_link = link_to project_tracing_path(@project) do
%span
= _('Tracing')
= _("To open Jaeger and easily view tracing from GitLab, link the %{link} page to your server").html_safe % { link: tracing_link }
.settings-content
= form_for @project, url: project_settings_operations_path(@project), method: :patch do |f|
= form_errors(@project)
.form-group
= f.fields_for :tracing_setting_attributes, setting do |form|
= form.label :external_url, _('Jaeger URL'), class: 'label-bold'
= form.url_field :external_url, class: 'form-control', placeholder: 'e.g. https://jaeger.mycompany.com'
%p.form-text.text-muted
- jaeger_help_url = "https://www.jaegertracing.io/docs/1.7/getting-started/"
- link_start_tag = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: jaeger_help_url }
- link_end_tag = "#{sprite_icon('external-link', css_class: 'ml-1 vertical-align-middle')}</a>".html_safe
= _("For more information, please review %{link_start_tag}Jaeger's configuration doc%{link_end_tag}").html_safe % { link_start_tag: link_start_tag, link_end_tag: link_end_tag }
= f.submit _('Save changes'), class: 'btn btn-success'

View File

@ -8,5 +8,5 @@
= render 'projects/settings/operations/prometheus', service: prometheus_service if Feature.enabled?(:settings_operations_prometheus_service)
= render 'projects/settings/operations/metrics_dashboard'
= render 'projects/settings/operations/grafana_integration'
= render_if_exists 'projects/settings/operations/tracing'
= render 'projects/settings/operations/tracing'
= render_if_exists 'projects/settings/operations/status_page'

View File

@ -25,6 +25,6 @@
= _("Incident")
- if issuable.incident?
%p.form-text.text-muted
- incident_docs_url = help_page_path('operations/incident_management/incidents.md', anchor: 'create-and-manage-incidents-in-gitlab')
- incident_docs_url = help_page_path('operations/incident_management/incidents.md')
- incident_docs_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: incident_docs_url }
= _('A %{incident_docs_start}modified issue%{incident_docs_end} to guide the resolution of incidents.').html_safe % { incident_docs_start: incident_docs_start, incident_docs_end: '</a>'.html_safe }

View File

@ -0,0 +1,5 @@
---
title: Handle the blacklisted ip error in the Go middleware
merge_request: 44614
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Added new editor-lite Vue component
merge_request: 44577
author:
type: added

View File

@ -0,0 +1,5 @@
---
title: Move Tracing feature to Core
merge_request: 44574
author:
type: added

View File

@ -18,85 +18,85 @@
:why: Bundler is MIT licensed but will sometimes fail in CI.
:versions: []
:when: 2016-05-02 06:42:08.045090000 Z
- - :whitelist
- - :permit
- MIT
- :who: Connor Shea
:why: http://choosealicense.com/licenses/mit/
:versions: []
:when: 2016-04-17 21:12:24.558441000 Z
- - :whitelist
- - :permit
- Apache 2.0
- :who: Connor Shea
:why: http://choosealicense.com/licenses/apache-2.0/
:versions: []
:when: 2016-05-02 05:27:43.762702000 Z
- - :whitelist
- - :permit
- ruby
- :who: Connor Shea
:why: https://github.com/ruby/ruby/blob/ruby_2_1/COPYING
:versions: []
:when: 2016-05-02 05:31:54.498490000 Z
- - :whitelist
- - :permit
- LGPL
- :who: Connor Shea
:why: http://www.gnu.org/licenses/license-list.html#LGPLv2.1
:versions: []
:when: 2016-05-02 05:32:48.645841000 Z
- - :whitelist
- - :permit
- ISC
- :who: Connor Shea
:why: http://www.gnu.org/licenses/license-list.html#ISC
:versions: []
:when: 2016-05-02 05:42:01.894452000 Z
- - :whitelist
- - :permit
- New BSD
- :who: Connor Shea
:why: https://opensource.org/licenses/BSD-3-Clause
:versions: []
:when: 2016-05-02 05:44:38.246021000 Z
- - :whitelist
- - :permit
- LGPL-2.1+
- :who: Connor Shea
:why: Equivalent to LGPL.
:versions: []
:when: 2016-05-02 05:52:56.303239000 Z
- - :whitelist
- - :permit
- BSD
- :who: Connor Shea
:why: https://opensource.org/licenses/BSD-2-Clause
:versions: []
:when: 2016-05-02 05:55:09.796363000 Z
- - :whitelist
- - :permit
- LGPLv2+
- :who: Stan Hu
:why: Equivalent to LGPLv2
:versions: []
:when: 2016-06-07 17:14:10.907682000 Z
- - :whitelist
- - :permit
- Artistic 2.0
- :who: Josh Frye
:why: Disk/mount information display on Admin pages
:versions: []
:when: 2016-06-29 16:32:45.432113000 Z
- - :whitelist
- - :permit
- Simplified BSD
- :who: Douwe Maan
:why: https://opensource.org/licenses/BSD-2-Clause
:versions: []
:when: 2016-07-26 21:24:07.248480000 Z
- - :blacklist
- - :restrict
- GPLv2
- :who: Connor Shea
:why: GPL-licensed libraries cannot be linked to from non-GPL projects.
:versions: []
:when: 2016-05-02 05:29:27.637336000 Z
- - :blacklist
- - :restrict
- GPLv3
- :who: Connor Shea
:why: GPL-licensed libraries cannot be linked to from non-GPL projects.
:versions: []
:when: 2016-05-02 05:29:43.904715000 Z
- - :blacklist
- - :restrict
- OSL-3.0
- :who: Sean McGivern
:why: The OSL license is a copyleft license
@ -188,13 +188,13 @@
:why: https://github.com/nodeca/pako/blob/master/LICENSE
:versions: []
:when: 2017-04-05 10:43:45.897720000 Z
- - :whitelist
- - :permit
- Unlicense
- :who: Nick Thomas <nick@gitlab.com>
:why: https://gitlab.com/gitlab-com/organization/issues/116
:versions: []
:when: 2017-09-01 17:17:51.996511844 Z
- - :blacklist
- - :restrict
- Facebook BSD+PATENTS
- :who: Nick Thomas <nick@gitlab.com>
:why: https://gitlab.com/gitlab-com/organization/issues/117
@ -281,19 +281,19 @@
:why: https://github.com/hexorx/countries/blob/master/LICENSE
:versions: []
:when: 2019-09-11 13:08:28.431132000 Z
- - :whitelist
- - :permit
- "(MIT OR CC0-1.0)"
- :who:
:why:
:versions: []
:when: 2019-11-08 10:03:31.787226000 Z
- - :whitelist
- - :permit
- CC0-1.0
- :who: Thomas Randolph
:why: This license is public domain
:versions: []
:when: 2020-06-03 05:04:44.632875345 Z
- - :whitelist
- - :permit
- 0BSD
- :who: Natalia Tepluhina
:why: This license is public domain
@ -313,9 +313,15 @@
:why: "https://github.com/cure53/DOMPurify/blob/main/LICENSE and https://gitlab.com/gitlab-org/gitlab/-/merge_requests/31928#note_346604841"
:versions: []
:when: 2020-08-13 13:42:46.508082000 Z
- - :whitelist
- - :permit
- Apache-2.0 WITH LLVM-exception
- :who: Nathan Friend
:why: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40670#note_403946372
:versions: []
:when: 2020-08-28 15:01:59.329048917 Z
- - :approve
- docutils
- :who: Mo Khan
:why: Used to generate documentation. https://pypi.org/project/docutils/0.13.1/
:versions: []
:when: 2020-10-05 20:22:55.955189491 Z

View File

@ -599,7 +599,6 @@ installations from source.
## Unicorn Logs
NOTE: **Note:**
Starting with GitLab 13.0, Puma is the default web server used in GitLab
all-in-one package based installations as well as GitLab Helm chart deployments.
@ -674,10 +673,8 @@ This log records:
- Information whenever [Rack Attack](../security/rack_attack.md) registers an abusive request.
- Requests over the [Rate Limit](../user/admin_area/settings/rate_limits_on_raw_endpoints.md) on raw endpoints.
- [Protected paths](../user/admin_area/settings/protected_paths.md) abusive requests.
NOTE: **Note:**
In GitLab versions [12.3](https://gitlab.com/gitlab-org/gitlab/-/issues/29239) and greater, user ID and username are also
recorded on this log, if available.
- In GitLab versions [12.3](https://gitlab.com/gitlab-org/gitlab/-/issues/29239) and greater,
user ID and username, if available.
## `graphql_json.log`

View File

@ -17,7 +17,6 @@ or Grafana supplies package repositories (Yum/Apt) for easy installation.
See [Grafana installation documentation](https://grafana.com/docs/grafana/latest/installation/)
for detailed steps.
NOTE: **Note:**
Before starting Grafana for the first time, set the admin user
and password in `/etc/grafana/grafana.ini`. If you don't, the default password
is `admin`.

View File

@ -25,7 +25,6 @@ To profile a request:
curl --header 'X-Profile-Token: <token>' --header 'X-Profile-Mode: <mode>' "https://gitlab.example.com/group/project"
```
NOTE: **Note:**
Profiled requests can take longer than usual.
After the request completes, you can view the profiling output from the

View File

@ -32,7 +32,7 @@ dashboard tool like [Grafana](https://grafana.com).
## Configuring Prometheus
NOTE: **Note:**
For installations from source, you'll have to install and configure it yourself.
For installations from source, you must install and configure it yourself.
Prometheus and its exporters are on by default, starting with GitLab 9.0.
Prometheus will run as the `gitlab-prometheus` user and listen on
@ -179,7 +179,7 @@ The next step is to tell all the other nodes where the monitoring node is:
take effect.
NOTE: **Note:**
Once monitoring using Service Discovery is enabled with `consul['monitoring_service_discovery'] = true`,
After monitoring using Service Discovery is enabled with `consul['monitoring_service_discovery'] = true`,
ensure that `prometheus['scrape_configs']` is not set in `/etc/gitlab/gitlab.rb`. Setting both
`consul['monitoring_service_discovery'] = true` and `prometheus['scrape_configs']` in `/etc/gitlab/gitlab.rb`
will result in errors.
@ -312,7 +312,6 @@ To use an external Prometheus server:
You can visit `http://localhost:9090` for the dashboard that Prometheus offers by default.
NOTE: **Note:**
If SSL has been enabled on your GitLab instance, you may not be able to access
Prometheus on the same browser as GitLab if using the same FQDN due to [HSTS](https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security). We plan to
[provide access via GitLab](https://gitlab.com/gitlab-org/multi-user-prometheus), but in the interim there are

View File

@ -10,7 +10,7 @@ The [node exporter](https://github.com/prometheus/node_exporter) enables you to
various machine resources such as memory, disk and CPU utilization.
NOTE: **Note:**
For installations from source you'll have to install and configure it yourself.
For installations from source you must install and configure it yourself.
To enable the node exporter:

View File

@ -12,7 +12,7 @@ The [PgBouncer exporter](https://github.com/prometheus-community/pgbouncer_expor
you to measure various [PgBouncer](https://www.pgbouncer.org/) metrics.
NOTE: **Note:**
For installations from source you'll have to install and configure it yourself.
For installations from source you must install and configure it yourself.
To enable the PgBouncer exporter:

View File

@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
The [PostgreSQL Server Exporter](https://github.com/wrouesnel/postgres_exporter) allows you to export various PostgreSQL metrics.
NOTE: **Note:**
For installations from source you will have to install and configure it yourself.
For installations from source you must install and configure it yourself.
To enable the PostgreSQL Server Exporter:
@ -20,7 +20,6 @@ To enable the PostgreSQL Server Exporter:
postgres_exporter['enable'] = true
```
NOTE: **Note:**
If PostgreSQL Server Exporter is configured on a separate node, make sure that the local
address is [listed in `trust_auth_cidr_addresses`](../../postgresql/replication_and_failover.md#network-information) or the
exporter will not be able to connect to the database.

View File

@ -11,7 +11,7 @@ various [Redis](https://redis.io) metrics. For more information on what is expor
[read the upstream documentation](https://github.com/oliver006/redis_exporter/blob/master/README.md#whats-exported).
NOTE: **Note:**
For installations from source you'll have to install and configure it yourself.
For installations from source you must install and configure it yourself.
To enable the Redis exporter:

View File

@ -18,14 +18,11 @@ POST /environments/:id/metrics_dashboard/annotations/
POST /clusters/:id/metrics_dashboard/annotations/
```
NOTE: **Note:**
The value of `dashboard_path` will be treated as a CGI-escaped path, and automatically un-escaped.
Parameters:
| Attribute | Type | Required | Description |
|:---------------|:---------------|:---------|:-----------------------------------------------------------------------------|
| `dashboard_path` | string | yes | ID of the dashboard which needs to be annotated. |
| `dashboard_path` | string | yes | ID of the dashboard which needs to be annotated. Treated as a CGI-escaped path, and automatically un-escaped. |
| `starting_at` | string | yes | Date time string, ISO 8601 formatted, such as `2016-03-11T03:45:40Z`. Timestamp marking start point of annotation. |
| `ending_at` | string | no | Date time string, ISO 8601 formatted, such as `2016-03-11T03:45:40Z`. Timestamp marking end point of annotation. When not supplied annotation will be displayed as single event at start point. |
| `description` | string | yes | Description of the annotation. |

View File

@ -1,3 +1,9 @@
---
stage: none
group: Development
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
# Community members & roles
GitLab community members and their privileges/responsibilities.

View File

@ -1,3 +1,10 @@
---
type: reference, dev
stage: none
group: Development
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
# Implement design & UI elements
For guidance on UX implementation at GitLab, please refer to our [Design System](https://design.gitlab.com/).

View File

@ -1,3 +1,10 @@
---
type: reference, dev
stage: none
group: Development
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
# Contribute to GitLab
Thank you for your interest in contributing to GitLab. This guide details how

View File

@ -1,3 +1,10 @@
---
type: reference, dev
stage: none
group: Development
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
# Issues workflow
## Issue tracker guidelines

View File

@ -1,3 +1,10 @@
---
type: reference, dev
stage: none
group: Development
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
# Merge requests workflow
We welcome merge requests from everyone, with fixes and improvements

View File

@ -1,3 +1,10 @@
---
type: reference, dev
stage: none
group: Development
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
# Style guides
## Editor/IDE styling standardization

View File

@ -6,10 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Distributed Tracing - development guidelines
NOTE: **Note:**
Distributed Tracing in GitLab is currently considered **experimental**, as it has not yet been tested at scale on GitLab.com.
GitLab is instrumented for distributed tracing.
GitLab is instrumented for distributed tracing. Distributed Tracing in GitLab is currently considered **experimental**, as it has not yet been tested at scale on GitLab.com.
According to [Open Tracing](https://opentracing.io/docs/overview/what-is-tracing/):

View File

@ -20,10 +20,8 @@ You can sign up to the cloud hosted <https://sentry.io>, deploy your own [on-pre
### Enabling Sentry
NOTE: **Note:**
You will need at least Maintainer [permissions](../user/permissions.md) to enable the Sentry integration.
GitLab provides an easy way to connect Sentry to your project:
GitLab provides an easy way to connect Sentry to your project. You will need at
least Maintainer [permissions](../user/permissions.md) to enable the Sentry integration.
1. Sign up to Sentry.io or [deploy your own](#deploying-sentry) Sentry instance.
1. [Create](https://docs.sentry.io/product/sentry-basics/guides/integrate-frontend/create-new-project/) a new Sentry project. For each GitLab project that you want to integrate, we recommend that you create a new Sentry project.
@ -47,9 +45,8 @@ You may also want to enable Sentry's GitLab integration by following the steps i
## Error Tracking List
NOTE: **Note:**
You will need at least Reporter [permissions](../user/permissions.md) to view the Error Tracking list.
You can find the Error Tracking list at **Operations > Error Tracking** in your project's sidebar.
Users with at least Reporter [permissions](../user/permissions.md)
can find the Error Tracking list at **Operations > Error Tracking** in your project's sidebar.
Here, you can filter errors by title or by status (one of Ignored , Resolved, or Unresolved) and sort in descending order by Frequency, First Seen, or Last Seen. By default, the error list is ordered by Last Seen and filtered to Unresolved errors.
![Error Tracking list](img/error_tracking_list_v12_6.png)

View File

@ -40,13 +40,11 @@ in GitLab to examine alerts in action.
## Enable Alerts
NOTE: **Note:**
You need at least Maintainer [permissions](../../user/permissions.md) to enable
the Alerts feature.
There are several ways to accept alerts into your GitLab project. Enabling any
of these methods enables the Alert list. After configuring alerts, visit
**Operations > Alerts** in your project's sidebar to view the list of alerts.
of these methods enables the Alert list. You need at least Maintainer
[permissions](../../user/permissions.md) to enable the Alerts feature. After
configuring alerts, visit **Operations > Alerts** in your project's sidebar to view
the list of alerts.
### Enable GitLab-managed Prometheus alerts
@ -83,7 +81,6 @@ for requests to the alerts endpoint.
You can monitor alerts using a GitLab integration with [Opsgenie](https://www.atlassian.com/software/opsgenie).
NOTE: **Note:**
If you enable the Opsgenie integration, you can't have other GitLab alert
services, such as [Generic Alerts](generic_alerts.md) or Prometheus alerts,
active at the same time.
@ -168,14 +165,12 @@ about alert statuses.
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3066) in GitLab 13.1.
The Alert detail view allows users to update the Alert assignee.
GitLab supports only a single assignee per alert.
In large teams, where there is shared ownership of an alert, it can be
difficult to track who is investigating and working on it. The Alert detail
view enables you to update the Alert assignee:
NOTE: **Note:**
GitLab supports only a single assignee per alert.
1. To display the list of current alerts, navigate to **Operations > Alerts**:
![Alert List View Assignee(s)](./img/alert_list_assignees_v13_1.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -4,7 +4,7 @@ group: Health
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
# Create and manage incidents in GitLab
# Incidents
While no configuration is required to use the [manual features](#create-an-incident-manually)
of incident management, some simple [configuration](#configure-incidents) is needed to automate incident creation.
@ -27,8 +27,7 @@ in your project's sidebar. The list contains the following metrics:
- **{severity-low}** **Low - S4**
- **{severity-unknown}** **Unknown**
NOTE: **Note:**
Editing incident severity on the incident details page was
[Editing incident severity](#incident-details) on the incident details page was
[introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/229402) in GitLab 13.4.
- **Incident** - The description of the incident, which attempts to capture the
@ -45,13 +44,12 @@ The Incident list displays incidents sorted by incident created date.
To see if a column is sortable, point your mouse at the header. Sortable columns
display an arrow next to the column name.
Incidents share the [Issues API](../../user/project/issues/index.md).
TIP: **Tip:**
For a live example of the incident list in action, visit this
[demo project](https://gitlab.com/gitlab-examples/ops/incident-setup/everyone/tanuki-inc/-/incidents).
NOTE: **Note:**
Incidents share the [Issues API](../../user/project/issues/index.md).
## Configure incidents
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/4925) in GitLab Ultimate 11.11.
@ -134,24 +132,55 @@ confirm that a GitLab issue is created from the incident.
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/230847) in GitLab 13.4.
Users with at least Reporter [permissions](../../user/permissions.md) can view
the Incident Details page. Navigate to **Operations > Incidents** in your project's
sidebar, and select an incident from the list.
When you take any of these actions on an incident, GitLab logs a system note and
displays it in the Incident Details view:
- Updating the severity of an incident
([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/42358) in GitLab 13.5.)
For live examples of GitLab incidents, visit the `tanuki-inc` project's
[incident list page](https://gitlab.com/gitlab-examples/ops/incident-setup/everyone/tanuki-inc/-/incidents).
Click any incident in the list to display its incident details page.
### Summary
The summary section for incidents provides both critical details about and the
contents of the issue template (if one was used). The highlighted bar at the top
of the incident displays from left to right: the link to the original alert, the
alert start time, and the event count. Beneath the highlight bar, GitLab
displays a summary that includes the following fields:
of the incident displays from left to right:
- The link to the original alert.
- The alert start time.
- The event count.
Beneath the highlight bar, GitLab displays a summary that includes the following fields:
- Start time
- Severity
- `full_query`
- Monitoring tool
Comments are displayed in threads, but can be displayed chronologically
[in a timeline view](#timeline-view).
### Alert details
Incidents show the details of linked alerts in a separate tab. To populate this
tab, the incident must have been created with a linked alert. Incidents
[created automatically](#configure-incidents) from alerts will have this
[created automatically](#configure-incidents) from alerts have this
field populated.
![Incident alert details](./img/incident_alert_details_v13_4.png)
### Timeline view
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/227836) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.5.
To quickly see the latest updates on an incident, click
**{comments}** **Turn timeline view on** in the comment bar to display comments
un-threaded and ordered chronologically, newest to oldest:
![Timeline view toggle](./img/timeline_view_toggle_v13_5.png)

View File

@ -37,11 +37,10 @@ To configure a GitLab Status Page you must:
### Configure GitLab with cloud provider information
To provide GitLab with the AWS account information needed to push content to your Status Page:
NOTE: **Note:**
Only AWS S3 is supported as a deploy target.
To provide GitLab with the AWS account information needed to push content to your Status Page:
1. Sign into GitLab as a user with Maintainer or greater [permissions](../../user/permissions.md).
1. Navigate to **{settings}** **Settings > Operations**. Next to **Status Page**,
click **Expand**.
@ -74,8 +73,6 @@ the necessary CI/CD variables to deploy the Status Page to AWS S3:
1. Scroll to **Variables**, and click **Expand**.
1. Add the following variables from your Amazon Console:
- `S3_BUCKET_NAME` - The name of the Amazon S3 bucket.
NOTE: **Note:**
If no bucket with the provided name exists, the first pipeline run creates
one and configures it for
[static website hosting](https://docs.aws.amazon.com/AmazonS3/latest/dev/HostingWebsiteOnS3Setup.html).
@ -128,10 +125,7 @@ To publish an incident:
1. Create an issue in the project you enabled the GitLab Status Page settings in.
1. A [project or group owner](../../user/permissions.md) must use the
`/publish` [quick action](../../user/project/quick_actions.md) to publish the
issue to the GitLab Status Page.
NOTE: **Note:**
Confidential issues can't be published.
issue to the GitLab Status Page. Confidential issues can't be published.
A background worker publishes the issue onto the Status Page using the credentials
you provided during setup. As part of publication, GitLab will:

View File

@ -78,7 +78,6 @@ For GitLab to associate your alerts with an [environment](../../ci/environments/
you must configure a `gitlab_environment_name` label on the alerts you set up in
Prometheus. The value of this should match the name of your environment in GitLab.
NOTE: **Note:**
In GitLab versions 13.1 and greater, you can configure your manually configured
Prometheus server to use the
[Generic alerts integration](../incident_management/generic_alerts.md).

View File

@ -25,7 +25,6 @@ metrics about the [deployed application](../index.md#configure-prometheus-to-gat
## Kubernetes pod health dashboard
NOTE: **Note:**
This dashboard requires Kubernetes v1.14 or higher, due to the
[change in metric labels](https://github.com/kubernetes/kubernetes/pull/69099)
in Kubernetes 1.14.

View File

@ -14,7 +14,6 @@ includes a few key metrics, but you can also define your own custom dashboards.
You may create a [new dashboard from scratch](#add-a-new-dashboard-to-your-project)
or [duplicate a GitLab-defined Prometheus dashboard](#duplicate-a-gitlab-defined-dashboard).
NOTE: **Note:**
The metrics as defined below do not support alerts, unlike
[custom metrics](../index.md#adding-custom-metrics).
@ -86,7 +85,7 @@ with the **Add Panel** page:
1. Click **Add panel** in the **{ellipsis_v}** **More actions** menu.
NOTE: **Note:**
You can add panel only to custom dashboards.
You can only add panels to custom dashboards.
![Monitoring Dashboard actions menu with add panel item](img/actions_menu_create_add_panel_v13_3.png)
1. In the **Define and preview panel** section, paste in the YAML you want to
@ -100,16 +99,12 @@ with the **Add Panel** page:
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/37238) in GitLab 12.7.
> - From [GitLab 12.8 onwards](https://gitlab.com/gitlab-org/gitlab/-/issues/39505), custom metrics are also duplicated when you duplicate a dashboard.
You can save a complete copy of a GitLab defined dashboard along with all custom metrics added to it.
You can save a complete copy of a GitLab-defined dashboard along with all custom metrics added to it.
The resulting `.yml` file can be customized and adapted to your project.
You can decide to save the dashboard `.yml` file in the project's **default** branch or in a
new branch.
new branch. To duplicate a GitLab-defined dashboard:
1. Click **Duplicate current dashboard** in the **{ellipsis_v}** **More actions** menu.
NOTE: **Note:**
You can duplicate only GitLab-defined dashboards.
1. Enter the filename and other information, such as the new commit's message, and click **Duplicate**.
1. Select a branch to add your dashboard to:
- *If you select your **default** branch,* the new dashboard becomes immediately available.

View File

@ -16,7 +16,10 @@ Queries that continue to use the old format will show no data.
## Predefined variables
GitLab supports a limited set of [CI variables](../../../ci/variables/README.md) in the Prometheus query. This is particularly useful for identifying a specific environment, for example with `ci_environment_slug`. The supported variables are:
GitLab supports a limited set of [CI variables](../../../ci/variables/README.md)
in the Prometheus query. This is particularly useful for identifying a specific
environment, for example with `ci_environment_slug`. Variables for Prometheus queries
must be lowercase. The supported variables are:
- `environment_filter`
- `ci_environment_slug`
@ -27,9 +30,6 @@ GitLab supports a limited set of [CI variables](../../../ci/variables/README.md)
- `ci_environment_name`
- `__range`
NOTE: **Note:**
Variables for Prometheus queries must be lowercase.
### environment_filter
`environment_filter` is automatically expanded to `container_name!="POD",environment="ENVIRONMENT_NAME"`

View File

@ -17,8 +17,7 @@ metrics to others, and you want to have relevant information directly available.
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/29691) in GitLab 12.2.
NOTE: **Note:**
Requires [Kubernetes](../../user/project/integrations/prometheus_library/kubernetes.md) metrics.
This feature requires [Kubernetes](../../user/project/integrations/prometheus_library/kubernetes.md) metrics.
Note: **Note:**
In GitLab versions 13.3 and earlier, metrics dashboard links were in the form

View File

@ -12,14 +12,13 @@ Grafana metrics can be embedded in [GitLab Flavored Markdown](../../user/markdow
You can embed live [Grafana](https://docs.gitlab.com/omnibus/settings/grafana.html)
charts in issues as a
[direct linked rendered image](https://grafana.com/docs/grafana/latest/reference/share_panel/#direct-link-rendered-image).
The **Direct link rendered image** sharing dialog within Grafana provides the link:
[direct linked rendered image](https://grafana.com/docs/grafana/latest/reference/share_panel/#direct-link-rendered-image). Your Grafana instance must be available to the
target user, either as a public dashboard or on the same network. The
**Direct link rendered image** sharing dialog within Grafana provides the link:
![Grafana Direct Linked Rendered Image](img/grafana_live_embed.png)
NOTE: **Note:**
For this embed to display correctly, the Grafana instance must be available to the
target user, either as a public dashboard or on the same network.
For this embed to display correctly, the
Copy the link and add an image tag as [inline HTML](../../user/markdown.md#inline-html)
in your Markdown. You can tweak the query parameters to meet your needs, such as

View File

@ -24,7 +24,7 @@ Cron scheduling uses a series of five numbers, separated by spaces:
# * * * * * <command to execute>
```
[Source: [Wikipedia](https://en.wikipedia.org/wiki/Cron)]
(Source: [Wikipedia](https://en.wikipedia.org/wiki/Cron))
In cron syntax, the asterisk (`*`) means 'every,' so the following cron strings
are valid:

View File

@ -55,7 +55,6 @@ Currently, GitLab supports the following Kubernetes versions:
- 1.14
- 1.13 (deprecated, support ends on November 22, 2020)
NOTE: **Note:**
Some GitLab features may support versions outside the range provided here.
### Adding and removing clusters
@ -195,7 +194,6 @@ To clear the cache:
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/24580) in GitLab 11.8.
NOTE: **Note:**
You do not need to specify a base domain on cluster settings when using GitLab Serverless. The domain in that case
will be specified as part of the Knative installation. See [Installing Applications](#installing-applications).
@ -223,13 +221,11 @@ Auto DevOps automatically detects, builds, tests, deploys, and monitors your
applications.
To make full use of Auto DevOps (Auto Deploy, Auto Review Apps, and
Auto Monitoring) you will need the Kubernetes project integration enabled.
Auto Monitoring) you will need the Kubernetes project integration enabled, but
Kubernetes clusters can be used without Auto DevOps.
[Read more about Auto DevOps](../../../topics/autodevops/index.md)
NOTE: **Note:**
Kubernetes clusters can be used without Auto DevOps.
## Deploying to a Kubernetes cluster
A Kubernetes cluster can be the destination for a deployment job. If
@ -252,20 +248,13 @@ GitLab CI/CD build environment.
| Variable | Description |
| -------- | ----------- |
| `KUBE_URL` | Equal to the API URL. |
| `KUBE_TOKEN` | The Kubernetes token of the [environment service account](add_remove_clusters.md#access-controls). |
| `KUBE_NAMESPACE` | The namespace associated with the project's deployment service account. In the format `<project_name>-<project_id>-<environment>`. For GitLab-managed clusters, a matching namespace is automatically created by GitLab in the cluster. |
| `KUBE_TOKEN` | The Kubernetes token of the [environment service account](add_remove_clusters.md#access-controls). Prior to GitLab 11.5, `KUBE_TOKEN` was the Kubernetes token of the main service account of the cluster integration. |
| `KUBE_NAMESPACE` | The namespace associated with the project's deployment service account. In the format `<project_name>-<project_id>-<environment>`. For GitLab-managed clusters, a matching namespace is automatically created by GitLab in the cluster. If your cluster was created before GitLab 12.2, the default `KUBE_NAMESPACE` is set to `<project_name>-<project_id>`. |
| `KUBE_CA_PEM_FILE` | Path to a file containing PEM data. Only present if a custom CA bundle was specified. |
| `KUBE_CA_PEM` | (**deprecated**) Raw PEM data. Only if a custom CA bundle was specified. |
| `KUBECONFIG` | Path to a file containing `kubeconfig` for this deployment. CA bundle would be embedded if specified. This config also embeds the same token defined in `KUBE_TOKEN` so you likely will only need this variable. This variable name is also automatically picked up by `kubectl` so you won't actually need to reference it explicitly if using `kubectl`. |
| `KUBE_INGRESS_BASE_DOMAIN` | From GitLab 11.8, this variable can be used to set a domain per cluster. See [cluster domains](#base-domain) for more information. |
NOTE: **Note:**
Prior to GitLab 11.5, `KUBE_TOKEN` was the Kubernetes token of the main
service account of the cluster integration.
NOTE: **Note:**
If your cluster was created before GitLab 12.2, default `KUBE_NAMESPACE` will be set to `<project_name>-<project_id>`.
### Custom namespace
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/27630) in GitLab 12.6.
@ -290,7 +279,6 @@ You can customize the deployment namespace in a few ways:
[`environment:kubernetes:namespace`](../../../ci/environments/index.md#configuring-kubernetes-deployments)
in `.gitlab-ci.yml`.
NOTE: **Note:**
When you customize the namespace, existing environments remain linked to their current
namespaces until you [clear the cluster cache](#clearing-the-cluster-cache).

View File

@ -28,7 +28,6 @@ above the log file data, depending on your configuration:
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
To learn more about the Log Explorer, see [APM - Log Explorer](https://www.youtube.com/watch?v=hWclZHA7Dgw).
NOTE: **Note:**
[Learn more about Kubernetes + GitLab](https://about.gitlab.com/solutions/kubernetes/).
Everything you need to build, test, deploy, and run your application at scale.

View File

@ -89,7 +89,7 @@ Alternatively, if there is also no email address, the project creator is set as
User assignment by username is under development and not ready for production use. It is
deployed behind a feature flag that is **disabled by default**.
[GitLab administrators with access to the GitLab Rails console](<replace with path to>/administration/feature_flags.md)
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
can enable it.
To enable it:

View File

@ -8,11 +8,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/22133) in GitLab 11.7.
GitLab has support for automatically detecting and monitoring the Kubernetes NGINX Ingress controller. This is provided by leveraging the built-in Prometheus metrics included with Kubernetes NGINX Ingress controller [version 0.16.0](https://github.com/kubernetes/ingress-nginx/blob/master/Changelog.md#0160) onward.
NOTE: **Note:**
NGINX Ingress versions prior to 0.16.0 offer an included [VTS Prometheus metrics exporter](nginx_ingress_vts.md), which exports metrics different than the built-in metrics.
GitLab has support for automatically detecting and monitoring the Kubernetes NGINX Ingress controller. This is provided by leveraging the built-in Prometheus metrics included with Kubernetes NGINX Ingress controller [version 0.16.0](https://github.com/kubernetes/ingress-nginx/blob/master/Changelog.md#0160) onward.
## Requirements
[Prometheus integration](../prometheus.md) must be active.

View File

@ -18,6 +18,15 @@ module Gitlab
request = ActionDispatch::Request.new(env)
render_go_doc(request) || @app.call(env)
rescue Gitlab::Auth::IpBlacklisted
Gitlab::AuthLogger.error(
message: 'Rack_Attack',
env: :blocklist,
remote_ip: request.ip,
request_method: request.request_method,
path: request.fullpath
)
Rack::Response.new('', 403).finish
end
private

View File

@ -201,7 +201,7 @@ module Gitlab
personal_snippets: count(PersonalSnippet.where(last_28_days_time_period)),
project_snippets: count(ProjectSnippet.where(last_28_days_time_period))
}.merge(
snowplow_event_counts(time_period: last_28_days_time_period(column: :collector_tstamp))
snowplow_event_counts(last_28_days_time_period(column: :collector_tstamp))
).tap do |data|
data[:snippets] = data[:personal_snippets] + data[:project_snippets]
end

View File

@ -2222,13 +2222,13 @@ msgstr ""
msgid "After a successful password update, you will be redirected to the login page where you can log in with your new password."
msgstr ""
msgid "After that, you will not to be able to use merge approvals or code quality as well as many other features."
msgid "After that, you will not be able to use merge approvals or code quality as well as many other features."
msgstr ""
msgid "After that, you will not to be able to use merge approvals or epics as well as many other features."
msgid "After that, you will not be able to use merge approvals or epics as well as many other features."
msgstr ""
msgid "After that, you will not to be able to use merge approvals or epics as well as many security features."
msgid "After that, you will not be able to use merge approvals or epics as well as many security features."
msgstr ""
msgid "Alert"
@ -29944,7 +29944,7 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription will expire on %{strong}%{expires_on}%{strong_close}. After that, you will not to be able to create issues or merge requests as well as many other features."
msgid "Your %{strong}%{plan_name}%{strong_close} subscription will expire on %{strong}%{expires_on}%{strong_close}. After that, you will not be able to create issues or merge requests as well as many other features."
msgstr ""
msgid "Your CSV export has started. It will be emailed to %{email} when complete."

View File

@ -58,6 +58,10 @@ module QA
click_element :retry_button
end
def has_job_log?
has_element? :job_log_content
end
private
def loaded?(wait: 60)
@ -70,3 +74,5 @@ module QA
end
end
end
QA::Page::Project::Job::Show.prepend_if_ee('QA::EE::Page::Project::Job::Show')

View File

@ -57,3 +57,5 @@ module QA
end
end
end
QA::Page::Project::Pipeline::Index.prepend_if_ee('QA::EE::Page::Project::Pipeline::Index')

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
RSpec.describe 'Plan' do
RSpec.describe 'Plan', :reliable do
describe 'Related issues' do
let(:project) do
Resource::Project.fabricate_via_api! do |project|

View File

@ -551,6 +551,37 @@ RSpec.describe GroupsController, factory_default: :keep do
end
end
context "updating default_branch_name" do
let(:example_branch_name) { "example_branch_name" }
subject(:update_action) do
put :update,
params: {
id: group.to_param,
group: { default_branch_name: example_branch_name }
}
end
it "updates the attribute" do
expect { subject }
.to change { group.namespace_settings.reload.default_branch_name }
.from(nil)
.to(example_branch_name)
expect(response).to have_gitlab_http_status(:found)
end
context "to empty string" do
let(:example_branch_name) { '' }
it "does not update the attribute" do
subject
expect(group.namespace_settings.reload.default_branch_name).not_to eq('')
end
end
end
context 'when there is a conflicting group path' do
let!(:conflict_group) { create(:group, path: SecureRandom.hex(12) ) }
let!(:old_name) { group.name }

View File

@ -43,31 +43,17 @@ RSpec.describe Projects::TracingsController do
end
end
describe 'with valid license' do
before do
stub_licensed_features(tracing: true)
sign_in(user)
end
context 'with maintainer role' do
it_behaves_like 'user with read access', :public
it_behaves_like 'user with read access', :internal
it_behaves_like 'user with read access', :private
end
context 'without maintainer role' do
it_behaves_like 'user without read access', :public
it_behaves_like 'user without read access', :internal
it_behaves_like 'user without read access', :private
end
before do
sign_in(user)
end
context 'with invalid license' do
before do
stub_licensed_features(tracing: false)
sign_in(user)
end
context 'with maintainer role' do
it_behaves_like 'user with read access', :public
it_behaves_like 'user with read access', :internal
it_behaves_like 'user with read access', :private
end
context 'without maintainer role' do
it_behaves_like 'user without read access', :public
it_behaves_like 'user without read access', :internal
it_behaves_like 'user without read access', :private

View File

@ -0,0 +1,63 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Tracings Content Security Policy' do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
subject { response_headers['Content-Security-Policy'] }
before_all do
project.add_maintainer(user)
end
before do
sign_in(user)
end
context 'when there is no global config' do
before do
expect_next_instance_of(Projects::TracingsController) do |controller|
expect(controller).to receive(:current_content_security_policy)
.and_return(ActionDispatch::ContentSecurityPolicy.new)
end
end
it 'does not add CSP directives' do
visit project_tracing_path(project)
is_expected.to be_blank
end
end
context 'when a global CSP config exists' do
before do
csp = ActionDispatch::ContentSecurityPolicy.new do |p|
p.frame_src 'https://global-policy.com'
end
expect_next_instance_of(Projects::TracingsController) do |controller|
expect(controller).to receive(:current_content_security_policy).and_return(csp)
end
end
context 'when external_url is set' do
let!(:project_tracing_setting) { create(:project_tracing_setting, project: project) }
it 'overwrites frame-src' do
visit project_tracing_path(project)
is_expected.to eq("frame-src https://example.com")
end
end
context 'when external_url is not set' do
it 'uses global policy' do
visit project_tracing_path(project)
is_expected.to eq("frame-src https://global-policy.com")
end
end
end
end

View File

@ -6,7 +6,7 @@ import { TEST_HOST } from 'spec/test_constants';
import { mockTracking } from 'helpers/tracking_helper';
import { LEGACY_FLAG, NEW_VERSION_FLAG, NEW_FLAG_ALERT } from '~/feature_flags/constants';
import Form from '~/feature_flags/components/form.vue';
import editModule from '~/feature_flags/store/modules/edit';
import createStore from '~/feature_flags/store/edit';
import EditFeatureFlag from '~/feature_flags/components/edit_feature_flag.vue';
import axios from '~/lib/utils/axios_utils';
@ -20,10 +20,9 @@ describe('Edit feature flag form', () => {
let wrapper;
let mock;
const store = new Vuex.Store({
modules: {
edit: editModule,
},
const store = createStore({
path: '/feature_flags',
endpoint: `${TEST_HOST}/feature_flags.json`,
});
const factory = (opts = {}) => {
@ -34,8 +33,6 @@ describe('Edit feature flag form', () => {
wrapper = shallowMount(EditFeatureFlag, {
localVue,
propsData: {
endpoint: `${TEST_HOST}/feature_flags.json`,
path: '/feature_flags',
environmentsEndpoint: 'environments.json',
projectId: '8',
featureFlagIssuesEndpoint: `${TEST_HOST}/feature_flags/5/issues`,
@ -105,7 +102,7 @@ describe('Edit feature flag form', () => {
describe('with error', () => {
it('should render the error', () => {
store.dispatch('edit/receiveUpdateFeatureFlagError', { message: ['The name is required'] });
store.dispatch('receiveUpdateFeatureFlagError', { message: ['The name is required'] });
return wrapper.vm.$nextTick(() => {
expect(wrapper.find('.alert-danger').exists()).toEqual(true);
expect(wrapper.find('.alert-danger').text()).toContain('The name is required');

View File

@ -1,9 +1,10 @@
import { shallowMount } from '@vue/test-utils';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import MockAdapter from 'axios-mock-adapter';
import { GlEmptyState, GlLoadingIcon } from '@gitlab/ui';
import { TEST_HOST } from 'spec/test_constants';
import Api from '~/api';
import { createStore } from '~/feature_flags/store';
import createStore from '~/feature_flags/store/index';
import FeatureFlagsTab from '~/feature_flags/components/feature_flags_tab.vue';
import FeatureFlagsComponent from '~/feature_flags/components/feature_flags.vue';
import FeatureFlagsTable from '~/feature_flags/components/feature_flags_table.vue';
@ -14,19 +15,25 @@ import TablePagination from '~/vue_shared/components/pagination/table_pagination
import axios from '~/lib/utils/axios_utils';
import { getRequestData, userList } from '../mock_data';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('Feature flags', () => {
const mockData = {
endpoint: `${TEST_HOST}/endpoint.json`,
csrfToken: 'testToken',
featureFlagsClientLibrariesHelpPagePath: '/help/feature-flags#unleash-clients',
featureFlagsClientExampleHelpPagePath: '/help/feature-flags#client-example',
unleashApiUrl: `${TEST_HOST}/api/unleash`,
unleashApiInstanceId: 'oP6sCNRqtRHmpy1gw2-F',
canUserConfigure: true,
canUserRotateToken: true,
newFeatureFlagPath: 'feature-flags/new',
newUserListPath: '/user-list/new',
};
const mockState = {
endpoint: `${TEST_HOST}/endpoint.json`,
projectId: '8',
unleashApiInstanceId: 'oP6sCNRqtRHmpy1gw2-F',
};
let wrapper;
@ -34,8 +41,9 @@ describe('Feature flags', () => {
let store;
const factory = (propsData = mockData, fn = shallowMount) => {
store = createStore();
store = createStore(mockState);
wrapper = fn(FeatureFlagsComponent, {
localVue,
store,
propsData,
provide: {
@ -76,7 +84,6 @@ describe('Feature flags', () => {
describe('without permissions', () => {
const propsData = {
endpoint: `${TEST_HOST}/endpoint.json`,
csrfToken: 'testToken',
errorStateSvgPath: '/assets/illustrations/feature_flag.svg',
featureFlagsHelpPagePath: '/help/feature-flags',
@ -85,8 +92,6 @@ describe('Feature flags', () => {
featureFlagsClientLibrariesHelpPagePath: '/help/feature-flags#unleash-clients',
featureFlagsClientExampleHelpPagePath: '/help/feature-flags#client-example',
unleashApiUrl: `${TEST_HOST}/api/unleash`,
unleashApiInstanceId: 'oP6sCNRqtRHmpy1gw2-F',
projectId: '8',
};
beforeEach(done => {
@ -134,7 +139,7 @@ describe('Feature flags', () => {
let emptyState;
beforeEach(async () => {
mock.onGet(mockData.endpoint, { params: { scope: FEATURE_FLAG_SCOPE, page: '1' } }).reply(
mock.onGet(mockState.endpoint, { params: { scope: FEATURE_FLAG_SCOPE, page: '1' } }).reply(
200,
{
feature_flags: [],
@ -154,8 +159,6 @@ describe('Feature flags', () => {
});
it('should render the empty state', async () => {
await axios.waitForAll();
emptyState = wrapper.find(GlEmptyState);
expect(emptyState.exists()).toBe(true);
});
@ -182,7 +185,7 @@ describe('Feature flags', () => {
describe('with paginated feature flags', () => {
beforeEach(done => {
mock
.onGet(mockData.endpoint, { params: { scope: FEATURE_FLAG_SCOPE, page: '1' } })
.onGet(mockState.endpoint, { params: { scope: FEATURE_FLAG_SCOPE, page: '1' } })
.replyOnce(200, getRequestData, {
'x-next-page': '2',
'x-page': '1',
@ -218,7 +221,7 @@ describe('Feature flags', () => {
const [flag] = table.props(FEATURE_FLAG_SCOPE);
table.vm.$emit('toggle-flag', flag);
expect(store.dispatch).toHaveBeenCalledWith('index/toggleFeatureFlag', flag);
expect(store.dispatch).toHaveBeenCalledWith('toggleFeatureFlag', flag);
});
it('renders configure button', () => {
@ -287,7 +290,7 @@ describe('Feature flags', () => {
describe('unsuccessful request', () => {
beforeEach(done => {
mock
.onGet(mockData.endpoint, { params: { scope: FEATURE_FLAG_SCOPE, page: '1' } })
.onGet(mockState.endpoint, { params: { scope: FEATURE_FLAG_SCOPE, page: '1' } })
.replyOnce(500, {});
Api.fetchFeatureFlagUserLists.mockRejectedValueOnce();

View File

@ -1,10 +1,10 @@
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import { shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import { GlAlert } from '@gitlab/ui';
import { TEST_HOST } from 'spec/test_constants';
import Form from '~/feature_flags/components/form.vue';
import newModule from '~/feature_flags/store/modules/new';
import createStore from '~/feature_flags/store/new';
import NewFeatureFlag from '~/feature_flags/components/new_feature_flag.vue';
import {
ROLLOUT_STRATEGY_ALL_USERS,
@ -17,13 +17,15 @@ import { allUsersStrategy } from '../mock_data';
const userCalloutId = 'feature_flags_new_version';
const userCalloutsPath = `${TEST_HOST}/user_callouts`;
const localVue = createLocalVue();
localVue.use(Vuex);
describe('New feature flag form', () => {
let wrapper;
const store = new Vuex.Store({
modules: {
new: newModule,
},
const store = createStore({
endpoint: `${TEST_HOST}/feature_flags.json`,
path: '/feature_flags',
});
const factory = (opts = {}) => {
@ -32,9 +34,8 @@ describe('New feature flag form', () => {
wrapper = null;
}
wrapper = shallowMount(NewFeatureFlag, {
localVue,
propsData: {
endpoint: `${TEST_HOST}/feature_flags.json`,
path: '/feature_flags',
environmentsEndpoint: 'environments.json',
projectId: '8',
showUserCallout: true,
@ -63,7 +64,7 @@ describe('New feature flag form', () => {
describe('with error', () => {
it('should render the error', () => {
store.dispatch('new/receiveCreateFeatureFlagError', { message: ['The name is required'] });
store.dispatch('receiveCreateFeatureFlagError', { message: ['The name is required'] });
return wrapper.vm.$nextTick(() => {
expect(wrapper.find('.alert').exists()).toEqual(true);
expect(wrapper.find('.alert').text()).toContain('The name is required');

View File

@ -2,8 +2,6 @@ import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
import { TEST_HOST } from 'spec/test_constants';
import {
setEndpoint,
setPath,
updateFeatureFlag,
requestUpdateFeatureFlag,
receiveUpdateFeatureFlagSuccess,
@ -13,18 +11,15 @@ import {
receiveFeatureFlagSuccess,
receiveFeatureFlagError,
toggleActive,
} from '~/feature_flags/store/modules/edit/actions';
import state from '~/feature_flags/store/modules/edit/state';
import {
mapStrategiesToRails,
mapFromScopesViewModel,
} from '~/feature_flags/store/modules/helpers';
} from '~/feature_flags/store/edit/actions';
import state from '~/feature_flags/store/edit/state';
import { mapStrategiesToRails, mapFromScopesViewModel } from '~/feature_flags/store/helpers';
import {
NEW_VERSION_FLAG,
LEGACY_FLAG,
ROLLOUT_STRATEGY_ALL_USERS,
} from '~/feature_flags/constants';
import * as types from '~/feature_flags/store/modules/edit/mutation_types';
import * as types from '~/feature_flags/store/edit/mutation_types';
import axios from '~/lib/utils/axios_utils';
jest.mock('~/lib/utils/url_utility');
@ -33,33 +28,7 @@ describe('Feature flags Edit Module actions', () => {
let mockedState;
beforeEach(() => {
mockedState = state();
});
describe('setEndpoint', () => {
it('should commit SET_ENDPOINT mutation', done => {
testAction(
setEndpoint,
'feature_flags.json',
mockedState,
[{ type: types.SET_ENDPOINT, payload: 'feature_flags.json' }],
[],
done,
);
});
});
describe('setPath', () => {
it('should commit SET_PATH mutation', done => {
testAction(
setPath,
'/feature_flags',
mockedState,
[{ type: types.SET_PATH, payload: '/feature_flags' }],
[],
done,
);
});
mockedState = state({ endpoint: 'feature_flags.json', path: '/feature_flags' });
});
describe('updateFeatureFlag', () => {

View File

@ -1,28 +1,12 @@
import state from '~/feature_flags/store/modules/edit/state';
import mutations from '~/feature_flags/store/modules/edit/mutations';
import * as types from '~/feature_flags/store/modules/edit/mutation_types';
import state from '~/feature_flags/store/edit/state';
import mutations from '~/feature_flags/store/edit/mutations';
import * as types from '~/feature_flags/store/edit/mutation_types';
describe('Feature flags Edit Module Mutations', () => {
let stateCopy;
beforeEach(() => {
stateCopy = state();
});
describe('SET_ENDPOINT', () => {
it('should set endpoint', () => {
mutations[types.SET_ENDPOINT](stateCopy, 'feature_flags.json');
expect(stateCopy.endpoint).toEqual('feature_flags.json');
});
});
describe('SET_PATH', () => {
it('should set provided options', () => {
mutations[types.SET_PATH](stateCopy, 'feature_flags');
expect(stateCopy.path).toEqual('feature_flags');
});
stateCopy = state({ endpoint: 'feature_flags.json', path: '/feature_flags' });
});
describe('REQUEST_FEATURE_FLAG', () => {

View File

@ -5,7 +5,7 @@ import {
createNewEnvironmentScope,
mapStrategiesToViewModel,
mapStrategiesToRails,
} from '~/feature_flags/store/modules/helpers';
} from '~/feature_flags/store/helpers';
import {
ROLLOUT_STRATEGY_ALL_USERS,
ROLLOUT_STRATEGY_PERCENT_ROLLOUT,

View File

@ -7,10 +7,7 @@ import {
receiveFeatureFlagsSuccess,
receiveFeatureFlagsError,
fetchFeatureFlags,
setFeatureFlagsEndpoint,
setFeatureFlagsOptions,
setInstanceIdEndpoint,
setInstanceId,
rotateInstanceId,
requestRotateInstanceId,
receiveRotateInstanceIdSuccess,
@ -26,10 +23,10 @@ import {
deleteUserList,
receiveDeleteUserListError,
clearAlert,
} from '~/feature_flags/store/modules/index/actions';
import { mapToScopesViewModel } from '~/feature_flags/store/modules/helpers';
import state from '~/feature_flags/store/modules/index/state';
import * as types from '~/feature_flags/store/modules/index/mutation_types';
} from '~/feature_flags/store/index/actions';
import { mapToScopesViewModel } from '~/feature_flags/store/helpers';
import state from '~/feature_flags/store/index/state';
import * as types from '~/feature_flags/store/index/mutation_types';
import axios from '~/lib/utils/axios_utils';
import { getRequestData, rotateData, featureFlag, userList } from '../../mock_data';
@ -39,20 +36,7 @@ describe('Feature flags actions', () => {
let mockedState;
beforeEach(() => {
mockedState = state();
});
describe('setFeatureFlagsEndpoint', () => {
it('should commit SET_FEATURE_FLAGS_ENDPOINT mutation', done => {
testAction(
setFeatureFlagsEndpoint,
'feature_flags.json',
mockedState,
[{ type: types.SET_FEATURE_FLAGS_ENDPOINT, payload: 'feature_flags.json' }],
[],
done,
);
});
mockedState = state({});
});
describe('setFeatureFlagsOptions', () => {
@ -68,32 +52,6 @@ describe('Feature flags actions', () => {
});
});
describe('setInstanceIdEndpoint', () => {
it('should commit SET_INSTANCE_ID_ENDPOINT mutation', done => {
testAction(
setInstanceIdEndpoint,
'instance_id.json',
mockedState,
[{ type: types.SET_INSTANCE_ID_ENDPOINT, payload: 'instance_id.json' }],
[],
done,
);
});
});
describe('setInstanceId', () => {
it('should commit SET_INSTANCE_ID mutation', done => {
testAction(
setInstanceId,
'test_instance_id',
mockedState,
[{ type: types.SET_INSTANCE_ID, payload: 'test_instance_id' }],
[],
done,
);
});
});
describe('fetchFeatureFlags', () => {
let mock;

Some files were not shown because too many files have changed in this diff Show More