提交 0d83264a 编写于 作者: G GitLab Bot

Add latest changes from gitlab-org/gitlab@master

上级 b7c735c8
......@@ -37,7 +37,7 @@ If applicable, any groups/projects that are happy to have this feature turned on
- [ ] Coordinate a time to enable the flag with `#production` and `#g_delivery` on slack.
- [ ] Announce on the issue an estimated time this will be enabled on GitLab.com
- [ ] Enable on GitLab.com by running chatops command in `#production`
- [ ] Cross post chatops slack command to `#support_gitlab-com` and in your team channel
- [ ] Cross post chatops slack command to `#support_gitlab-com` ([more guidance when this is necessary in the dev docs](https://docs.gitlab.com/ee/development/feature_flags/controls.html#where-to-run-commands)) and in your team channel
- [ ] Announce on the issue that the flag has been enabled
- [ ] Remove feature flag and add changelog entry
- [ ] After the flag removal is deployed, [clean up the feature flag](https://docs.gitlab.com/ee/development/feature_flags/controls.html#cleaning-up) by running chatops command in `#production` channel
......
<script>
import { mapActions, mapState } from 'vuex';
import { mapActions, mapGetters, mapState } from 'vuex';
import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
import { s__ } from '~/locale';
......@@ -15,8 +15,15 @@ export default {
GlLink,
},
i18n: {
unavailableFeatureText: s__(
'ContainerRegistry|Currently, the Container Registry tag expiration feature is not available for projects created before GitLab version 12.8. For updates and more information, visit Issue %{linkStart}#196124%{linkEnd}',
unavailableFeatureTitle: s__(
`ContainerRegistry|Container Registry tag expiration and retention policy is disabled`,
),
unavailableFeatureIntroText: s__(
`ContainerRegistry|The Container Registry tag expiration and retention policies for this project have not been enabled.`,
),
unavailableUserFeatureText: s__(`ContainerRegistry|Please contact your administrator.`),
unavailableAdminFeatureText: s__(
`ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature.`,
),
fetchSettingsErrorText: FETCH_SETTINGS_ERROR_MESSAGE,
},
......@@ -26,10 +33,19 @@ export default {
};
},
computed: {
...mapState(['isDisabled']),
...mapState(['isAdmin', 'adminSettingsPath']),
...mapGetters({ isDisabled: 'getIsDisabled' }),
showSettingForm() {
return !this.isDisabled && !this.fetchSettingsError;
},
showDisabledFormMessage() {
return this.isDisabled && !this.fetchSettingsError;
},
unavailableFeatureMessage() {
return this.isAdmin
? this.$options.i18n.unavailableAdminFeatureText
: this.$options.i18n.unavailableUserFeatureText;
},
},
mounted() {
this.fetchSettings().catch(() => {
......@@ -59,16 +75,21 @@ export default {
</ul>
<settings-form v-if="showSettingForm" />
<template v-else>
<gl-alert v-if="isDisabled" :dismissible="false">
<p>
<gl-sprintf :message="$options.i18n.unavailableFeatureText">
<template #link="{content}">
<gl-link href="https://gitlab.com/gitlab-org/gitlab/issues/196124" target="_blank">
{{ content }}
</gl-link>
</template>
</gl-sprintf>
</p>
<gl-alert
v-if="showDisabledFormMessage"
:dismissible="false"
:title="$options.i18n.unavailableFeatureTitle"
variant="tip"
>
{{ $options.i18n.unavailableFeatureIntroText }}
<gl-sprintf :message="unavailableFeatureMessage">
<template #link="{ content }">
<gl-link :href="adminSettingsPath" target="_blank">
{{ content }}
</gl-link>
</template>
</gl-sprintf>
</gl-alert>
<gl-alert v-else-if="fetchSettingsError" variant="warning" :dismissible="false">
<gl-sprintf :message="$options.i18n.fetchSettingsErrorText" />
......
......@@ -5,11 +5,7 @@ export const setInitialState = ({ commit }, data) => commit(types.SET_INITIAL_ST
export const updateSettings = ({ commit }, data) => commit(types.UPDATE_SETTINGS, data);
export const toggleLoading = ({ commit }) => commit(types.TOGGLE_LOADING);
export const receiveSettingsSuccess = ({ commit }, data) => {
if (data) {
commit(types.SET_SETTINGS, data);
} else {
commit(types.SET_IS_DISABLED, true);
}
commit(types.SET_SETTINGS, data);
};
export const resetSettings = ({ commit }) => commit(types.RESET_SETTINGS);
......
......@@ -19,3 +19,7 @@ export const getSettings = (state, getters) => ({
});
export const getIsEdited = state => !isEqual(state.original, state.settings);
export const getIsDisabled = state => {
return !(state.original || state.enableHistoricEntries);
};
......@@ -3,4 +3,3 @@ export const UPDATE_SETTINGS = 'UPDATE_SETTINGS';
export const TOGGLE_LOADING = 'TOGGLE_LOADING';
export const SET_SETTINGS = 'SET_SETTINGS';
export const RESET_SETTINGS = 'RESET_SETTINGS';
export const SET_IS_DISABLED = 'SET_IS_DISABLED';
import { parseBoolean } from '~/lib/utils/common_utils';
import * as types from './mutation_types';
export default {
......@@ -8,19 +9,19 @@ export default {
keepN: JSON.parse(initialState.keepNOptions),
olderThan: JSON.parse(initialState.olderThanOptions),
};
state.enableHistoricEntries = parseBoolean(initialState.enableHistoricEntries);
state.isAdmin = parseBoolean(initialState.isAdmin);
state.adminSettingsPath = initialState.adminSettingsPath;
},
[types.UPDATE_SETTINGS](state, data) {
state.settings = { ...state.settings, ...data.settings };
},
[types.SET_SETTINGS](state, settings) {
state.settings = settings;
state.settings = settings ?? state.settings;
state.original = Object.freeze(settings);
},
[types.SET_IS_DISABLED](state, isDisabled) {
state.isDisabled = isDisabled;
},
[types.RESET_SETTINGS](state) {
state.settings = { ...state.original };
state.settings = Object.assign({}, state.original);
},
[types.TOGGLE_LOADING](state) {
state.isLoading = !state.isLoading;
......
......@@ -8,9 +8,17 @@ export default () => ({
*/
isLoading: false,
/*
* Boolean to determine if the user is allowed to interact with the form
* Boolean to determine if the user is an admin
*/
isDisabled: false,
isAdmin: false,
/*
* String containing the full path to the admin config page for CI/CD
*/
adminSettingsPath: '',
/*
* Boolean to determine if project created before 12.8 can use this feature
*/
enableHistoricEntries: false,
/*
* This contains the data shown and manipulated in the UI
* Has the following structure:
......@@ -24,9 +32,9 @@ export default () => ({
*/
settings: {},
/*
* Same structure as settings, above but Frozen object and used only in case the user clicks 'cancel'
* Same structure as settings, above but Frozen object and used only in case the user clicks 'cancel', initialized to null
*/
original: {},
original: null,
/*
* Contains the options used to populate the form selects
*/
......
......@@ -202,7 +202,7 @@ const fileExtensionIcons = {
flv: 'movie',
vob: 'movie',
ogv: 'movie',
ogg: 'movie',
ogg: 'music',
gifv: 'movie',
avi: 'movie',
mov: 'movie',
......
......@@ -496,6 +496,10 @@ class ApplicationController < ActionController::Base
html_request? && !devise_controller?
end
def public_visibility_restricted?
Gitlab::CurrentSettings.restricted_visibility_levels.include? Gitlab::VisibilityLevel::PUBLIC
end
def set_usage_stats_consent_flag
return unless current_user
return if sessionless_user?
......
# frozen_string_literal: true
class Explore::ApplicationController < ApplicationController
skip_before_action :authenticate_user!
skip_before_action :authenticate_user!, unless: :public_visibility_restricted?
layout 'explore'
end
# frozen_string_literal: true
class HelpController < ApplicationController
skip_before_action :authenticate_user!
skip_before_action :authenticate_user!, unless: :public_visibility_restricted?
layout 'help'
......
......@@ -51,6 +51,10 @@ module ExploreHelper
links.any? { |link| explore_nav_link?(link) }
end
def public_visibility_restricted?
Gitlab::CurrentSettings.restricted_visibility_levels.include? Gitlab::VisibilityLevel::PUBLIC
end
private
def get_explore_nav_links
......
......@@ -74,7 +74,7 @@ module ServicesHelper
def scoped_integration_path(integration)
if @project.present?
project_settings_integration_path(@project, integration)
project_service_path(@project, integration)
elsif @group.present?
group_settings_integration_path(@group, integration)
else
......
......@@ -15,6 +15,8 @@ class AlertsService < Service
before_validation :ensure_token, if: :activated?
def url
return if instance? || template?
url_helpers.project_alerts_notify_url(project, format: :json)
end
......
......@@ -56,7 +56,12 @@ module Groups
end
def tree_exporter
Gitlab::ImportExport::Group::TreeSaver.new(group: @group, current_user: @current_user, shared: @shared, params: @params)
Gitlab::ImportExport::Group::LegacyTreeSaver.new(
group: @group,
current_user: @current_user,
shared: @shared,
params: @params
)
end
def file_saver
......
......@@ -38,7 +38,9 @@
%hr.footer-fixed
.container.footer-container
.footer-links
= link_to _("Explore"), explore_root_path
= link_to _("Help"), help_path
- if !public_visibility_restricted?
= link_to _("Explore"), explore_root_path
= link_to _("Help"), help_path
= link_to _("About GitLab"), "https://about.gitlab.com/"
= footer_message
......@@ -14,7 +14,8 @@
%hr
.container
.footer-links
= link_to _("Explore"), explore_root_path
= link_to _("Help"), help_path
- if !public_visibility_restricted?
= link_to _("Explore"), explore_root_path
= link_to _("Help"), help_path
= link_to _("About GitLab"), "https://about.gitlab.com/"
= footer_message
#js-registry-settings{ data: { project_id: @project.id,
cadence_options: cadence_options.to_json,
keep_n_options: keep_n_options.to_json,
older_than_options: older_than_options.to_json} }
older_than_options: older_than_options.to_json,
is_admin: current_user&.admin.to_s,
admin_settings_path: ci_cd_admin_application_settings_path(anchor: 'js-registry-settings'),
enable_historic_entries: Gitlab::CurrentSettings.try(:container_expiration_policies_enable_historic_entries).to_s} }
......@@ -10,7 +10,7 @@
- if @service.respond_to?(:detailed_description)
%p= @service.detailed_description
.col-lg-9
= form_for(@service, as: :service, url: project_service_path(@project, @service.to_param), method: :put, html: { class: 'gl-show-field-errors integration-settings-form js-integration-settings-form', data: { 'can-test' => @service.can_test?, 'test-url' => test_project_service_path(@project, @service) } }) do |form|
= form_for(@service, as: :service, url: scoped_integration_path(@service), method: :put, html: { class: 'gl-show-field-errors integration-settings-form js-integration-settings-form', data: { 'can-test' => @service.can_test?, 'test-url' => test_project_service_path(@project, @service) } }) do |form|
= render 'shared/service_settings', form: form, service: @service
- if @service.editable?
.footer-block.row-content-block
......
.js-alerts-service-settings{ data: { activated: @service.activated?.to_s,
form_path: project_service_path(@project, @service.to_param),
authorization_key: @service.token, url: @service.url, learn_more_url: 'https://docs.gitlab.com/ee/user/project/integrations/generic_alerts.html' } }
form_path: scoped_integration_path(@service),
authorization_key: @service.token, url: @service.url || _('<namespace / project>'), learn_more_url: 'https://docs.gitlab.com/ee/user/project/integrations/generic_alerts.html' } }
- run_actions_text = s_("ProjectService|Perform common operations on GitLab project: %{project_name}") % { project_name: @project.full_name }
- pretty_name = @project&.full_name || _('<project name>')
- run_actions_text = s_("ProjectService|Perform common operations on GitLab project: %{project_name}") % { project_name: pretty_name }
%p= s_("ProjectService|To set up this service:")
%ul.list-unstyled.indent-list
......@@ -20,7 +21,7 @@
.form-group
= label_tag :display_name, _('Display name'), class: 'col-12 col-form-label label-bold'
.col-12.input-group
= text_field_tag :display_name, "GitLab / #{@project.full_name}", class: 'form-control form-control-sm', readonly: 'readonly'
= text_field_tag :display_name, "GitLab / #{pretty_name}", class: 'form-control form-control-sm', readonly: 'readonly'
.input-group-append
= clipboard_button(target: '#display_name', class: 'input-group-text')
......@@ -38,8 +39,9 @@
%p
= s_('MattermostService|Suggestions:')
%code= 'gitlab'
%code= @project.path # Path contains no spaces, but dashes
%code= @project.full_path
- if @project
%code= @project.path # Path contains no spaces, but dashes
%code= @project.full_path
.form-group
= label_tag :request_url, s_('MattermostService|Request URL'), class: 'col-12 col-form-label label-bold'
......
- pretty_name = defined?(@project) ? @project.full_name : 'namespace / path'
- run_actions_text = "Perform common operations on GitLab project: #{pretty_name}"
- pretty_name = @project&.full_name || _('<project name>')
- run_actions_text = s_("ProjectService|Perform common operations on GitLab project: %{project_name}") % { project_name: pretty_name }
.info-well
.well-segment
......@@ -31,8 +31,10 @@
%p
= _("Suggestions:")
%code= 'gitlab'
%code= @project.path # Path contains no spaces, but dashes
%code= @project.full_path
%code= 'project'
- if @project
%code= @project.path # Path contains no spaces, but dashes
%code= @project.full_path
.form-group
= label_tag :url, 'URL', class: 'col-12 col-form-label label-bold'
......
---
title: Introduce rate limit for creating issues via API
merge_request: 28130
author:
type: performance
---
title: Protect sidekiq admin UI with admin mode
merge_request: 28164
author: Diego Louzán
type: fixed
---
title: Use music icon for files with .ogg extension
merge_request: 29514
author:
type: fixed
constraint = lambda { |request| request.env['warden'].authenticate? && request.env['warden'].user.admin? }
constraints constraint do
constraints ::Constraints::AdminConstrainer.new do
mount Sidekiq::Web, at: '/admin/sidekiq', as: :sidekiq
end
......@@ -390,7 +390,7 @@ CAUTION: **Caution:**
For performance reasons since
[GitLab 11.8](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/23931)
and **behind the `api_kaminari_count_with_limit`
[feature flag](../development/feature_flags.md)**, if the number of resources is
[feature flag](../development/feature_flags/index.md)**, if the number of resources is
more than 10,000, the `X-Total` and `X-Total-Pages` headers as well as the
`rel="last"` `Link` are not present in the response headers.
......
......@@ -740,6 +740,14 @@ the `weight` parameter:
**Note**: The `closed_by` attribute was [introduced in GitLab 10.6](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17042). This value will only be present for issues which were closed after GitLab 10.6 and when the user account that closed the issue still exists.
## Rate limits
To help avoid abuse, users are limited to:
| Request Type | Limit |
| ---------------- | --------------------------- |
| Create | 300 issues per minute |
## Edit issue
Updates an existing project issue. This call is also used to mark an issue as
......
......@@ -63,7 +63,7 @@ Complementary reads:
styleguide if you are contributing to the [GraphQL API](../api/graphql/index.md)
- [Sidekiq guidelines](sidekiq_style_guide.md) for working with Sidekiq workers
- [Working with Gitaly](gitaly.md)
- [Manage feature flags](feature_flags.md)
- [Manage feature flags](feature_flags/index.md)
- [Licensed feature availability](licensed_feature_availability.md)
- [View sent emails or preview mailers](emails.md)
- [Shell commands](shell_commands.md) in the GitLab codebase
......
......@@ -5,9 +5,9 @@ file, as well as information and history about our changelog process.
## Overview
Each bullet point, or **entry**, in our [`CHANGELOG.md`][changelog.md] file is
generated from a single data file in the [`changelogs/unreleased/`][unreleased]
(or corresponding EE) folder. The file is expected to be a [YAML] file in the
Each bullet point, or **entry**, in our [`CHANGELOG.md`](https://gitlab.com/gitlab-org/gitlab/blob/master/CHANGELOG.md) file is
generated from a single data file in the [`changelogs/unreleased/`](https://gitlab.com/gitlab-org/gitlab-foss/tree/master/changelogs/)
(or corresponding EE) folder. The file is expected to be a [YAML](https://en.wikipedia.org/wiki/YAML) file in the
following format:
```yaml
......@@ -27,15 +27,12 @@ valid options are: added, fixed, changed, deprecated, removed, security, perform
Community contributors and core team members are encouraged to add their name to
the `author` field. GitLab team members **should not**.
[changelog.md]: https://gitlab.com/gitlab-org/gitlab/blob/master/CHANGELOG.md
[unreleased]: https://gitlab.com/gitlab-org/gitlab-foss/tree/master/changelogs/
[YAML]: https://en.wikipedia.org/wiki/YAML
## What warrants a changelog entry?
- Any change that introduces a database migration, whether it's regular, post,
or data migration, **must** have a changelog entry.
- [Security fixes] **must** have a changelog entry, without `merge_request` value
- [Security fixes](https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md)
**must** have a changelog entry, without `merge_request` value
and with `type` set to `security`.
- Any user-facing change **should** have a changelog entry. Example: "GitLab now
uses system fonts for all text."
......@@ -269,13 +266,14 @@ as the other was merged. When we had dozens of merge requests fighting for the
same changelog entry location, this quickly became a major source of merge
conflicts and delays in development.
This led us to a [boring solution] of "add your entry in a random location in
This led us to a [boring solution](https://about.gitlab.com/handbook/values/#boring-solutions) of "add your entry in a random location in
the list." This actually worked pretty well as we got further along in each
monthly release cycle, but at the start of a new cycle, when a new version
section was added and there were fewer places to "randomly" add an entry, the
conflicts became a problem again until we had a sufficient number of entries.
On top of all this, it created an entirely different headache for [release managers]
On top of all this, it created an entirely different headache for
[release managers](https://gitlab.com/gitlab-org/release/docs/blob/master/quickstart/release-manager.md)
when they cherry-picked a commit into a stable branch for a patch release. If
the commit included an entry in the `CHANGELOG`, it would include the entire
changelog for the latest version in `master`, so the release manager would have
......@@ -283,16 +281,11 @@ to manually remove the later entries. They often would have had to do this
multiple times per patch release. This was compounded when we had to release
multiple patches at once due to a security issue.
We needed to automate all of this manual work. So we [started brainstorming].
We needed to automate all of this manual work. So we
[started brainstorming](https://gitlab.com/gitlab-org/gitlab-foss/issues/17826).
After much discussion we settled on the current solution of one file per entry,
and then compiling the entries into the overall `CHANGELOG.md` file during the
[release process].
[boring solution]: https://about.gitlab.com/handbook/values/#boring-solutions
[release managers]: https://gitlab.com/gitlab-org/release/docs/blob/master/quickstart/release-manager.md
[started brainstorming]: https://gitlab.com/gitlab-org/gitlab-foss/issues/17826
[release process]: https://gitlab.com/gitlab-org/release-tools
[Security fixes]: https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md
[release process](https://gitlab.com/gitlab-org/release-tools).
---
......
......@@ -490,15 +490,11 @@ A good example of collaboration on an MR touching multiple parts of the codebase
### Credits
Largely based on the [thoughtbot code review guide].
[thoughtbot code review guide]: https://github.com/thoughtbot/guides/tree/master/code-review
Largely based on the [thoughtbot code review guide](https://github.com/thoughtbot/guides/tree/master/code-review).
---
[Return to Development documentation](README.md)
[projects]: https://about.gitlab.com/handbook/engineering/projects/
[build handbook]: https://about.gitlab.com/handbook/build/handbook/build#how-to-work-with-build
[^1]: Please note that specs other than JavaScript specs are considered backend code.
[^2]: We encourage you to seek guidance from a database maintainer if your merge request is potentially introducing expensive queries. It is most efficient to comment on the line of code in question with the SQL queries so they can give their advice.
......@@ -402,7 +402,8 @@ below will make it easy to manage this, without unnecessary overhead.
Every monthly release has a corresponding issue on the CE issue tracker to keep
track of functionality broken by that release and any fixes that need to be
included in a patch release (see [8.3 Regressions] as an example).
included in a patch release (see
[8.3 Regressions](https://gitlab.com/gitlab-org/gitlab-foss/issues/4127) as an example).
As outlined in the issue description, the intended workflow is to post one note
with a reference to an issue describing the regression, and then to update that
......@@ -412,11 +413,9 @@ If you're a contributor who doesn't have the required permissions to update
other users' notes, please post a new note with a reference to both the issue
and the merge request.
The release manager will [update the notes] in the regression issue as fixes are
addressed.
[8.3 Regressions]: https://gitlab.com/gitlab-org/gitlab-foss/issues/4127
[update the notes]: https://gitlab.com/gitlab-org/release-tools/blob/master/doc/pro-tips.md#update-the-regression-issue
The release manager will
[update the notes](https://gitlab.com/gitlab-org/release-tools/blob/master/doc/pro-tips.md#update-the-regression-issue)
in the regression issue as fixes are addressed.
## Technical and UX debt
......
......@@ -1349,11 +1349,9 @@ Replace `reconfigure` with `restart` where appropriate.
In [step 2 of the installation guide](../../install/installation.md#2-ruby),
we install Ruby from source. Whenever there is a new version that needs to
be updated, remember to change it throughout the codeblock and also replace
the sha256sum (it can be found in the [downloads page][ruby-dl] of the Ruby
the sha256sum (it can be found in the [downloads page](https://www.ruby-lang.org/en/downloads/) of the Ruby
website).
[ruby-dl]: https://www.ruby-lang.org/en/downloads/ "Ruby download website"
### Configuration documentation for source and Omnibus installations
GitLab currently officially supports two installation methods: installations
......@@ -1380,7 +1378,7 @@ the style below as a guide:
external_url "https://gitlab.example.com"
```
1. Save the file and [reconfigure] GitLab for the changes to take effect.
1. Save the file and [reconfigure](path/to/administration/restart_gitlab.md#omnibus-gitlab-reconfigure) GitLab for the changes to take effect.
---
......@@ -1393,10 +1391,7 @@ the style below as a guide:
host: "gitlab.example.com"
```
1. Save the file and [restart] GitLab for the changes to take effect.
[reconfigure]: path/to/administration/restart_gitlab.md#omnibus-gitlab-reconfigure
[restart]: path/to/administration/restart_gitlab.md#installations-from-source
1. Save the file and [restart](path/to/administration/restart_gitlab.md#installations-from-source) GitLab for the changes to take effect.
````
In this case:
......
......@@ -9,7 +9,8 @@
## Act as CE when unlicensed
Since the implementation of [GitLab CE features to work with unlicensed EE instance][ee-as-ce]
Since the implementation of
[GitLab CE features to work with unlicensed EE instance](https://gitlab.com/gitlab-org/gitlab/issues/2500)
GitLab Enterprise Edition should work like GitLab Community Edition
when no license is active. So EE features always should be guarded by
`project.feature_available?` or `group.feature_available?` (or
......@@ -24,8 +25,6 @@ setting the [`FOSS_ONLY` environment variable](https://gitlab.com/gitlab-org/git
to something that evaluates as `true`. The same works for running tests
(for example `FOSS_ONLY=1 yarn jest`).
[ee-as-ce]: https://gitlab.com/gitlab-org/gitlab/issues/2500
## Separation of EE code
All EE code should be put inside the `ee/` top-level directory. The
......@@ -53,11 +52,9 @@ is applied not only to models. Here's a list of other examples:
- `ee/app/views/foo/_bar.html.haml`
This works because for every path that is present in CE's eager-load/auto-load
paths, we add the same `ee/`-prepended path in [`config/application.rb`].
paths, we add the same `ee/`-prepended path in [`config/application.rb`](https://gitlab.com/gitlab-org/gitlab/blob/925d3d4ebc7a2c72964ce97623ae41b8af12538d/config/application.rb#L42-52).
This also applies to views.
[`config/application.rb`]: https://gitlab.com/gitlab-org/gitlab/blob/925d3d4ebc7a2c72964ce97623ae41b8af12538d/config/application.rb#L42-52
### EE features based on CE features
For features that build on existing CE features, write a module in the `EE`
......
......@@ -29,6 +29,11 @@ Monitor stage, Health group.
For all production environment Chatops commands, use the `#production` channel.
As per the template, where a feature would have a (potentially) significant user
impact and the feature is being enabled instance wide prior to release, please copy
the Slack message and repost in the `#support_gitlab-com` channel for added visibility
and awareness, preferably with a link to the issue, MR, or docs.
Regardless of the channel in which the Chatops command is ran, any feature flag change that affects GitLab.com will automatically be logged in an issue.
The issue is created in the [gl-infra/feature-flag-log](https://gitlab.com/gitlab-com/gl-infra/feature-flag-log/issues?scope=all&utf8=%E2%9C%93&state=closed) project, and it will at minimum log the Slack handle of person enabling a feature flag, the time, and the name of the flag being changed.
......
# File Storage in GitLab
We use the [CarrierWave] gem to handle file upload, store and retrieval.
We use the [CarrierWave](https://github.com/carrierwaveuploader/carrierwave) gem to handle file upload, store and retrieval.
File uploads should be accelerated by workhorse, for details please refer to [uploads development documentation](uploads.md).
......@@ -46,14 +46,14 @@ they are still not 100% standardized. You can see them below:
CI Artifacts and LFS Objects behave differently in CE and EE. In CE they inherit the `GitlabUploader`
while in EE they inherit the `ObjectStorage` and store files in and S3 API compatible object store.
In the case of Issues/MR/Notes Markdown attachments, there is a different approach using the [Hashed Storage] layout,
In the case of Issues/MR/Notes Markdown attachments, there is a different approach using the [Hashed Storage](../administration/repository_storage_types.md) layout,
instead of basing the path into a mutable variable `:project_path_with_namespace`, it's possible to use the
hash of the project ID instead, if project migrates to the new approach (introduced in 10.2).
> Note: We provide an [all-in-one Rake task] to migrate all uploads to object
> Note: We provide an [all-in-one Rake task](../administration/raketasks/uploads/migrate.md) to migrate all uploads to object
> storage in one go. If a new Uploader class or model type is introduced, make
> sure you add a Rake task invocation corresponding to it to the [category
> list].
> sure you add a Rake task invocation corresponding to it to the
> [category list](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/tasks/gitlab/uploads/migrate.rake).
### Path segments
......@@ -144,8 +144,3 @@ class Thing < ActiveRecord::Base
...
end
```
[CarrierWave]: https://github.com/carrierwaveuploader/carrierwave
[Hashed Storage]: ../administration/repository_storage_types.md
[all-in-one rake task]: ../administration/raketasks/uploads/migrate.md
[category list]: https://gitlab.com/gitlab-org/gitlab/blob/master/lib/tasks/gitlab/uploads/migrate.rake
......@@ -9,7 +9,7 @@ when making _backend_ changes that might involve multiple features or [component
## Uploads
GitLab supports uploads to [object storage]. That means every feature and
GitLab supports uploads to [object storage](https://docs.gitlab.com/charts/advanced/external-object-storage/). That means every feature and
change that affects uploads should also be tested against [object storage],
which is _not_ enabled by default in [GDK](https://gitlab.com/gitlab-org/gitlab-development-kit).
......@@ -25,5 +25,3 @@ See also [File Storage in GitLab](file_storage.md).
GitLab supports a great amount of features for [merge requests](../user/project/merge_requests/index.md). One
of them is the ability to create merge requests from and to [forks](../gitlab-basics/fork-project.md),
which should also be highly considered and tested upon development phase.
[object storage]: https://docs.gitlab.com/charts/advanced/external-object-storage/
......@@ -8,7 +8,7 @@ consistent performance of GitLab.
The process of solving performance problems is roughly as follows:
1. Make sure there's an issue open somewhere (for example, on the GitLab CE issue
tracker), and create one if there is not. See [#15607][#15607] for an example.
tracker), and create one if there is not. See [#15607](https://gitlab.com/gitlab-org/gitlab-foss/issues/15607) for an example.
1. Measure the performance of the code in a production environment such as
GitLab.com (see the [Tooling](#tooling) section below). Performance should be
measured over a period of _at least_ 24 hours.
......@@ -495,7 +495,7 @@ just memory but also unnecessary time spent in CPU and I/O for processing lines
## Anti-Patterns
This is a collection of [anti-patterns][anti-pattern] that should be avoided
This is a collection of [anti-patterns](https://en.wikipedia.org/wiki/Anti-pattern) that should be avoided
unless these changes have a measurable, significant, and positive impact on
production environments.
......@@ -539,6 +539,3 @@ Assuming you are working with ActiveRecord models, you might also find these lin
You may find some useful examples in this snippet:
<https://gitlab.com/gitlab-org/gitlab-foss/snippets/33946>
[#15607]: https://gitlab.com/gitlab-org/gitlab-foss/issues/15607
[anti-pattern]: https://en.wikipedia.org/wiki/Anti-pattern
......@@ -205,7 +205,7 @@ On the EC2 dashboard, look for Load Balancer in the left navigation bar:
1. Click **Configure Health Check** and set up a health check for your EC2 instances.
1. For **Ping Protocol**, select HTTP.
1. For **Ping Port**, enter 80.
1. For **Ping Path**, enter `/explore`. (We use `/explore` as it's a public endpoint that does
1. For **Ping Path**, enter `/users/sign_in`. (We use `/users/sign_in` as it's a public endpoint that does
not require authorization.)
1. Keep the default **Advanced Details** or adjust them according to your needs.
1. Click **Add EC2 Instances** but, as we don't have any instances to add yet, come back
......
......@@ -69,6 +69,16 @@ you are privileged to.
If the public level is restricted, user profiles are only visible to logged in users.
## Visibility of pages
By default, the following directories are visible to unauthenticated users:
- Public access (`/public`).
- Explore (`/explore`).
- Help (`/help`).
However, if the access level of the `/public` directory is restricted, these directories are visible only to logged in users.
## Restricting the use of public or internal projects
You can restrict the use of visibility levels for users when they create a project or a
......
......@@ -91,7 +91,7 @@ For more details on group visibility, see [Public access](../../../public_access
## Restricted visibility levels
To set the available visibility levels for new projects and snippets:
To set the available visibility levels for projects, snippets, and selected pages:
1. Check the desired visibility levels.
1. Click **Save changes**.
......
......@@ -15,6 +15,7 @@
1. [By uploading a manifest file (AOSP)](manifest.md)
1. [From Gemnasium](gemnasium.md)
1. [From Phabricator](phabricator.md)
1. [From Jira (issues only)](jira.md)
In addition to the specific migration documentation above, you can import any
Git repository via HTTP from the New Project page. Be aware that if the
......
# Import your Jira project issues to GitLab
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2766) in GitLab 12.10.
Using GitLab Jira importer, you can import your Jira issues to GitLab.com or to
your self-managed GitLab instance.
Jira issues import is an MVC, project-level feature, meaning that issues from multiple
Jira projects can be imported into a GitLab project. MVC version imports issue title and description
as well as some other issue metadata as a section in the issue description.
## Prerequisites
### Permissions
In order to be able to import issues from a Jira project you need to have read access on Jira
issues and a [Maintainer or higher](../../permissions.md#project-members-permissions) role in the
GitLab project that you wish to import into.
### Jira integration
This feature uses the existing GitLab [Jira integration](../integrations/jira.md).
Make sure you have the integration set up before trying to import Jira issues.
## Import Jira issues to GitLab
To import Jira issues to a GitLab project, follow the steps below.
NOTE: **Note:**
Importing Jira issues is done as an asynchronous background job, which
may result in delays based on import queues load, system load, or other factors.
Importing large projects may take several minutes depending on the size of the import.
1. On the **{issues}** **Issues** page, click the **Import Issues** (**{import}**) button.
1. Select **Import from Jira**.
![Import issues from Jira button](img/jira/import_issues_from_jira_button_v12_10.png)
The following form appears.
![Import issues from Jira form](img/jira/import_issues_from_jira_form_v12_10.png)
If you've previously set up the [Jira integration](../integrations/jira.md), you now see the Jira
projects that you have access to in the dropdown.
1. Select the Jira project that you wish to import issues from.
![Import issues from Jira form](img/jira/import_issues_from_jira_projects_v12_10.png)
1. Click **Import Issues**. You're presented with a confirmation that import has started.
While the import is running in the background, you can navigate away from the import status page
to the issues page, and you'll see the new issues appearing in the issues list.
1. To check the status of your import, go back to the Jira import page.
![Import issues from Jira button](img/jira/import_issues_from_jira_button_v12_10.png)
# frozen_string_literal: true
module API
module Helpers
module RateLimiter
def check_rate_limit!(key, scope)
if rate_limiter.throttled?(key, scope: scope)
log_request(key)
render_exceeded_limit_error!
end
end
private
def rate_limiter
::Gitlab::ApplicationRateLimiter
end
def render_exceeded_limit_error!
render_api_error!({ error: _('This endpoint has been requested too many times. Try again later.') }, 429)
end
def log_request(key)
rate_limiter.log_request(request, "#{key}_request_limit".to_sym, current_user)
end
end
end
end
......@@ -4,6 +4,7 @@ module API
class Issues < Grape::API
include PaginationParams
helpers Helpers::IssuesHelpers
helpers Helpers::RateLimiter
helpers ::Gitlab::IssuableMetadata
before { authenticate_non_get! }
......@@ -211,6 +212,8 @@ module API
post ':id/issues' do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42320')
check_rate_limit! :issues_create, [current_user, :issues_create]
authorize! :create_issue, user_project
params.delete(:created_at) unless current_user.can?(:set_issue_created_at, user_project)
......
......@@ -2,15 +2,8 @@
module API
class ProjectExport < Grape::API
helpers do
def throttled?(action)
rate_limiter.throttled?(action, scope: [current_user, action, user_project])
end
helpers Helpers::RateLimiter
def rate_limiter
::Gitlab::ApplicationRateLimiter
end
end
before do
not_found! unless Gitlab::CurrentSettings.project_export_enabled?
authorize_admin_project
......@@ -32,9 +25,7 @@ module API
detail 'This feature was introduced in GitLab 10.6.'
end
get ':id/export/download' do
if throttled?(:project_download_export)
render_api_error!({ error: 'This endpoint has been requested too many times. Try again later.' }, 429)
end
check_rate_limit! :project_download_export, [current_user, :project_download_export, user_project]
if user_project.export_file_exists?
present_carrierwave_file!(user_project.export_file)
......@@ -54,9 +45,7 @@ module API
end
end
post ':id/export' do
if throttled?(:project_export)
render_api_error!({ error: 'This endpoint has been requested too many times. Try again later.' }, 429)
end
check_rate_limit! :project_export, [current_user, :project_export, user_project]
project_export_params = declared_params(include_missing: false)
after_export_params = project_export_params.delete(:upload) || {}
......
......@@ -8,19 +8,12 @@ module API
helpers Helpers::ProjectsHelpers
helpers Helpers::FileUploadHelpers
helpers Helpers::RateLimiter
helpers do
def import_params
declared_params(include_missing: false)
end
def throttled?(key, scope)
rate_limiter.throttled?(key, scope: scope)
end
def rate_limiter
::Gitlab::ApplicationRateLimiter
end
end
before do
......@@ -69,13 +62,7 @@ module API
post 'import' do
require_gitlab_workhorse!
key = "project_import".to_sym
if throttled?(key, [current_user, key])
rate_limiter.log_request(request, "#{key}_request_limit".to_sym, current_user)
render_api_error!({ error: _('This endpoint has been requested too many times. Try again later.') }, 429)
end
check_rate_limit! :project_import, [current_user, :project_import]
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42437')
......
# frozen_string_literal: true
module Constraints
class AdminConstrainer
def matches?(request)
if Feature.enabled?(:user_mode_in_session)
admin_mode_enabled?(request)
else
user_is_admin?(request)
end
end
private
def user_is_admin?(request)
request.env['warden'].authenticate? && request.env['warden'].user.admin?
end
def admin_mode_enabled?(request)
Gitlab::Session.with_session(request.session) do
request.env['warden'].authenticate? && Gitlab::Auth::CurrentUserMode.new(request.env['warden'].user).admin_mode?
end
end
end
end
......@@ -3,7 +3,7 @@
module Gitlab
module ImportExport
module Group
class TreeSaver
class LegacyTreeSaver
attr_reader :full_path, :shared
def initialize(group:, current_user:, shared:, params: {})
......
......@@ -788,12 +788,18 @@ msgstr ""
msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
msgstr ""
msgid "<namespace / project>"
msgstr ""
msgid "<no name set>"
msgstr ""
msgid "<no scopes selected>"
msgstr ""
msgid "<project name>"
msgstr ""
msgid "<strong>%{group_name}</strong> group members"
msgstr ""
......@@ -5456,6 +5462,9 @@ msgstr ""
msgid "Container repositories sync capacity"
msgstr ""
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
msgstr ""
msgid "ContainerRegistry|%{imageName} tags"
msgstr ""
......@@ -5471,6 +5480,9 @@ msgstr ""
msgid "ContainerRegistry|Container Registry"
msgstr ""
msgid "ContainerRegistry|Container Registry tag expiration and retention policy is disabled"
msgstr ""
msgid "ContainerRegistry|Copy build command"
msgstr ""
......@@ -5480,9 +5492,6 @@ msgstr ""
msgid "ContainerRegistry|Copy push command"
msgstr ""
msgid "ContainerRegistry|Currently, the Container Registry tag expiration feature is not available for projects created before GitLab version 12.8. For updates and more information, visit Issue %{linkStart}#196124%{linkEnd}"
msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
......@@ -5531,6 +5540,9 @@ msgstr ""
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
msgid "ContainerRegistry|Please contact your administrator."
msgstr ""
msgid "ContainerRegistry|Push an image"
msgstr ""
......@@ -5587,6 +5599,9 @@ msgstr ""
msgid "ContainerRegistry|Tags deleted successfully"
msgstr ""
msgid "ContainerRegistry|The Container Registry tag expiration and retention policies for this project have not been enabled."
msgstr ""
msgid "ContainerRegistry|The last tag related to this image was recently removed. This empty image and any associated data will be automatically removed as part of the regular Garbage Collection process. If you have any questions, contact your administrator."
msgstr ""
......
......@@ -22,4 +22,18 @@ describe Explore::GroupsController do
expect(assigns(:groups)).to contain_exactly(member_of_group, public_group)
end
context 'restricted visibility level is public' do
before do
sign_out(user)
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
end
it 'redirects to login page' do
get :index
expect(response).to redirect_to new_user_session_path
end
end
end
......@@ -171,5 +171,17 @@ describe Explore::ProjectsController do
get :index, params: { sort: sorting_param }
end
end
context 'restricted visibility level is public' do
before do
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
end
it 'redirects to login page' do
get :index
expect(response).to redirect_to new_user_session_path
end
end
end
end
......@@ -79,6 +79,20 @@ describe HelpController do
expect(assigns[:help_index]).to eq '[protocol-relative](//example.com)'
end
end
context 'restricted visibility set to public' do
before do
sign_out(user)
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
end
it 'redirects to sign_in path' do
get :index
expect(response).to redirect_to(new_user_session_path)
end
end
end
describe 'GET #show' do
......
......@@ -89,5 +89,17 @@ describe 'Explore Groups', :js do
end
it_behaves_like 'renders group in public groups area'
context 'when visibility is restricted to public' do
before do
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
end
it 'redirects to the sign in page' do
visit explore_groups_path
expect(page).to have_current_path(new_user_session_path)
end
end
end
end
......@@ -16,6 +16,17 @@ describe 'User explores projects' do
include_examples 'shows public projects'
end
context 'when visibility is restricted to public' do
before do
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
visit(explore_projects_path)
end
it 'redirects to login page' do
expect(page).to have_current_path(new_user_session_path)
end
end
end
context 'when signed in' do
......
import { shallowMount } from '@vue/test-utils';
import { GlAlert } from '@gitlab/ui';
import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
import component from '~/registry/settings/components/registry_settings_app.vue';
import SettingsForm from '~/registry/settings/components/settings_form.vue';
import { createStore } from '~/registry/settings/store/';
import { SET_IS_DISABLED } from '~/registry/settings/store/mutation_types';
import { SET_SETTINGS, SET_INITIAL_STATE } from '~/registry/settings/store/mutation_types';
import { FETCH_SETTINGS_ERROR_MESSAGE } from '~/registry/shared/constants';
import { stringifiedFormOptions } from '../../shared/mock_data';
describe('Registry Settings App', () => {
let wrapper;
......@@ -13,14 +14,14 @@ describe('Registry Settings App', () => {
const findSettingsComponent = () => wrapper.find(SettingsForm);
const findAlert = () => wrapper.find(GlAlert);
const mountComponent = ({ dispatchMock = 'mockResolvedValue', isDisabled = false } = {}) => {
store = createStore();
store.commit(SET_IS_DISABLED, isDisabled);
const mountComponent = ({ dispatchMock = 'mockResolvedValue' } = {}) => {
const dispatchSpy = jest.spyOn(store, 'dispatch');
if (dispatchMock) {
dispatchSpy[dispatchMock]();
}
dispatchSpy[dispatchMock]();
wrapper = shallowMount(component, {
stubs: {
GlSprintf,
},
mocks: {
$toast: {
show: jest.fn(),
......@@ -30,11 +31,16 @@ describe('Registry Settings App', () => {
});
};
beforeEach(() => {
store = createStore();
});
afterEach(() => {
wrapper.destroy();
});
it('renders', () => {
store.commit(SET_SETTINGS, { foo: 'bar' });
mountComponent();
expect(wrapper.element).toMatchSnapshot();
});
......@@ -45,13 +51,15 @@ describe('Registry Settings App', () => {
});
it('renders the setting form', () => {
store.commit(SET_SETTINGS, { foo: 'bar' });
mountComponent();
expect(findSettingsComponent().exists()).toBe(true);
});
describe('isDisabled', () => {
describe('the form is disabled', () => {
beforeEach(() => {
mountComponent({ isDisabled: true });
store.commit(SET_SETTINGS, undefined);
mountComponent();
});
it('the form is hidden', () => {
......@@ -59,9 +67,27 @@ describe('Registry Settings App', () => {
});
it('shows an alert', () => {
expect(findAlert().html()).toContain(
'Currently, the Container Registry tag expiration feature is not available',
const text = findAlert().text();
expect(text).toContain(
'The Container Registry tag expiration and retention policies for this project have not been enabled.',
);
expect(text).toContain('Please contact your administrator.');
});
describe('an admin is visiting the page', () => {
beforeEach(() => {
store.commit(SET_INITIAL_STATE, {
...stringifiedFormOptions,
isAdmin: true,
adminSettingsPath: 'foo',
});
});
it('shows the admin part of the alert message', () => {
const sprintf = findAlert().find(GlSprintf);
expect(sprintf.text()).toBe('administration settings');
expect(sprintf.find(GlLink).attributes('href')).toBe('foo');
});
});
});
......
......@@ -20,7 +20,7 @@ describe('Actions Registry Store', () => {
);
describe('receiveSettingsSuccess', () => {
it('calls SET_SETTINGS when data is present', () => {
it('calls SET_SETTINGS', () => {
testAction(
actions.receiveSettingsSuccess,
'foo',
......@@ -29,15 +29,6 @@ describe('Actions Registry Store', () => {
[],
);
});
it('calls SET_IS_DISABLED when data is not present', () => {
testAction(
actions.receiveSettingsSuccess,
null,
{},
[{ type: types.SET_IS_DISABLED, payload: true }],
[],
);
});
});
describe('fetchSettings', () => {
......
......@@ -29,7 +29,7 @@ describe('Getters registry settings store', () => {
});
});
describe('getIsDisabled', () => {
describe('getIsEdited', () => {
it('returns false when original is equal to settings', () => {
const same = { foo: 'bar' };
expect(getters.getIsEdited({ original: same, settings: same })).toBe(false);
......@@ -41,4 +41,18 @@ describe('Getters registry settings store', () => {
);
});
});
describe('getIsDisabled', () => {
it.each`
original | enableHistoricEntries | result
${undefined} | ${false} | ${true}
${{ foo: 'bar' }} | ${undefined} | ${false}
${{}} | ${false} | ${false}
`(
'returns $result when original is $original and enableHistoricEntries is $enableHistoricEntries',
({ original, enableHistoricEntries, result }) => {
expect(getters.getIsDisabled({ original, enableHistoricEntries })).toBe(result);
},
);
});
});
......@@ -12,14 +12,19 @@ describe('Mutations Registry Store', () => {
describe('SET_INITIAL_STATE', () => {
it('should set the initial state', () => {
const expectedState = { ...mockState, projectId: 'foo', formOptions };
mutations[types.SET_INITIAL_STATE](mockState, {
const payload = {
projectId: 'foo',
enableHistoricEntries: false,
adminSettingsPath: 'foo',
isAdmin: true,
};
const expectedState = { ...mockState, ...payload, formOptions };
mutations[types.SET_INITIAL_STATE](mockState, {
...payload,
...stringifiedFormOptions,
});
expect(mockState.projectId).toEqual(expectedState.projectId);
expect(mockState.formOptions).toEqual(expectedState.formOptions);
expect(mockState).toEqual(expectedState);
});
});
......@@ -41,6 +46,13 @@ describe('Mutations Registry Store', () => {
expect(mockState.settings).toEqual(expectedState.settings);
expect(mockState.original).toEqual(expectedState.settings);
});
it('should keep the default state when settings is not present', () => {
const originalSettings = { ...mockState.settings };
mutations[types.SET_SETTINGS](mockState);
expect(mockState.settings).toEqual(originalSettings);
expect(mockState.original).toEqual(undefined);
});
});
describe('RESET_SETTINGS', () => {
......@@ -50,6 +62,13 @@ describe('Mutations Registry Store', () => {
mutations[types.RESET_SETTINGS](mockState);
expect(mockState.settings).toEqual(mockState.original);
});
it('if original is undefined it should initialize to empty object', () => {
mockState.settings = { foo: 'bar' };
mockState.original = undefined;
mutations[types.RESET_SETTINGS](mockState);
expect(mockState.settings).toEqual({});
});
});
describe('TOGGLE_LOADING', () => {
......@@ -58,11 +77,4 @@ describe('Mutations Registry Store', () => {
expect(mockState.isLoading).toEqual(true);
});
});
describe('SET_IS_DISABLED', () => {
it('should set isDisabled', () => {
mutations[types.SET_IS_DISABLED](mockState, true);
expect(mockState.isDisabled).toEqual(true);
});
});
});
# frozen_string_literal: true
#
require 'spec_helper'
describe Constraints::AdminConstrainer, :do_not_mock_admin_mode do
let(:user) { create(:user) }
let(:session) { {} }
let(:env) { { 'warden' => double(:warden, authenticate?: true, user: user) } }
let(:request) { double(:request, session: session, env: env) }
around do |example|
Gitlab::Session.with_session(session) do
example.run
end
end
describe '#matches' do
context 'feature flag :user_mode_in_session is enabled' do
context 'when user is a regular user' do
it 'forbids access' do
expect(subject.matches?(request)).to be(false)
end
end
context 'when user is an admin' do
let(:user) { create(:admin) }
context 'admin mode is disabled' do
it 'forbids access' do
expect(subject.matches?(request)).to be(false)
end
end
context 'admin mode is enabled' do
before do
current_user_mode = Gitlab::Auth::CurrentUserMode.new(user)
current_user_mode.request_admin_mode!
current_user_mode.enable_admin_mode!(password: user.password)
end
it 'allows access' do
expect(subject.matches?(request)).to be(true)
end
end
end
end
context 'feature flag :user_mode_in_session is disabled' do
before do
stub_feature_flags(user_mode_in_session: false)
end
context 'when user is a regular user' do
it 'forbids access' do
expect(subject.matches?(request)).to be(false)
end
end
context 'when user is an admin' do
let(:user) { create(:admin) }
it 'allows access' do
expect(subject.matches?(request)).to be(true)
end
end
end
end
end
......@@ -2,7 +2,7 @@
require 'spec_helper'
describe Gitlab::ImportExport::Group::TreeSaver do
describe Gitlab::ImportExport::Group::LegacyTreeSaver do
describe 'saves the group tree into a json object' do
let(:shared) { Gitlab::ImportExport::Shared.new(group) }
let(:group_tree_saver) { described_class.new(group: group, current_user: user, shared: shared) }
......@@ -28,7 +28,7 @@ describe Gitlab::ImportExport::Group::TreeSaver do
# except:
# context 'with description override' do
# context 'group members' do
# ^ These are specific for the Group::TreeSaver
# ^ These are specific for the Group::LegacyTreeSaver
context 'JSON' do
let(:saved_group_json) do
group_tree_saver.save
......
......@@ -381,6 +381,20 @@ describe API::Issues do
end.not_to change { project.labels.count }
end
end
context 'when request exceeds the rate limit' do
before do
allow(::Gitlab::ApplicationRateLimiter).to receive(:throttled?).and_return(true)
end
it 'prevents users from creating more issues' do
post api("/projects/#{project.id}/issues", user),
params: { title: 'new issue', labels: 'label, label2', weight: 3, assignee_ids: [user2.id] }
expect(response).to have_gitlab_http_status(:too_many_requests)
expect(json_response['message']['error']).to eq('This endpoint has been requested too many times. Try again later.')
end
end
end
describe 'POST /projects/:id/issues with spam filtering' do
......
......@@ -50,7 +50,7 @@ describe Groups::ImportExport::ExportService do
end
it 'saves the models' do
expect(Gitlab::ImportExport::Group::TreeSaver).to receive(:new).and_call_original
expect(Gitlab::ImportExport::Group::LegacyTreeSaver).to receive(:new).and_call_original
service.execute
end
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册