Commit 8486c8e5 authored by Rémy Coutable's avatar Rémy Coutable

Merge remote-tracking branch 'origin/master' into ce-to-ee-2017-05-17

Signed-off-by: default avatarRémy Coutable <remy@rymai.me>
parents ac4cbf2e 32ea87d3
...@@ -46,7 +46,8 @@ export default { ...@@ -46,7 +46,8 @@ export default {
}, },
computed: { computed: {
showUnapproveButton() { showUnapproveButton() {
return this.userHasApproved && !this.userCanApprove; const isMerged = this.mr.state === 'merged';
return this.userHasApproved && !this.userCanApprove && !isMerged;
}, },
}, },
methods: { methods: {
......
...@@ -24,15 +24,21 @@ module RoutableActions ...@@ -24,15 +24,21 @@ module RoutableActions
end end
end end
def ensure_canonical_path(routable, requested_path) def ensure_canonical_path(routable, requested_full_path)
return unless request.get? return unless request.get?
canonical_path = routable.full_path canonical_path = routable.full_path
<<<<<<< HEAD
if canonical_path != requested_path if canonical_path != requested_path
if canonical_path.casecmp(requested_path) != 0 if canonical_path.casecmp(requested_path) != 0
flash[:notice] = "#{routable.class.to_s.titleize} '#{requested_path}' was moved to '#{canonical_path}'. Please update any links and bookmarks that may still have the old path." flash[:notice] = "#{routable.class.to_s.titleize} '#{requested_path}' was moved to '#{canonical_path}'. Please update any links and bookmarks that may still have the old path."
=======
if canonical_path != requested_full_path
if canonical_path.casecmp(requested_full_path) != 0
flash[:notice] = "#{routable.class.to_s.titleize} '#{requested_full_path}' was moved to '#{canonical_path}'. Please update any links and bookmarks that may still have the old path."
>>>>>>> origin/master
end end
redirect_to request.original_url.sub(requested_path, canonical_path) redirect_to build_canonical_path(routable)
end end
end end
end end
...@@ -31,4 +31,10 @@ class Groups::ApplicationController < ApplicationController ...@@ -31,4 +31,10 @@ class Groups::ApplicationController < ApplicationController
return render_403 return render_403
end end
end end
def build_canonical_path(group)
params[:group_id] = group.to_param
url_for(params)
end
end end
...@@ -176,4 +176,12 @@ class GroupsController < Groups::ApplicationController ...@@ -176,4 +176,12 @@ class GroupsController < Groups::ApplicationController
@notification_setting = current_user.notification_settings_for(group) @notification_setting = current_user.notification_settings_for(group)
end end
end end
def build_canonical_path(group)
return group_path(group) if action_name == 'show' # root group path
params[:id] = group.to_param
url_for(params)
end
end end
...@@ -29,6 +29,13 @@ class Projects::ApplicationController < ApplicationController ...@@ -29,6 +29,13 @@ class Projects::ApplicationController < ApplicationController
@project = find_routable!(Project, path, extra_authorization_proc: auth_proc) @project = find_routable!(Project, path, extra_authorization_proc: auth_proc)
end end
def build_canonical_path(project)
params[:namespace_id] = project.namespace.to_param
params[:project_id] = project.to_param
url_for(params)
end
def repository def repository
@repository ||= project.repository @repository ||= project.repository
end end
......
...@@ -390,4 +390,11 @@ class ProjectsController < Projects::ApplicationController ...@@ -390,4 +390,11 @@ class ProjectsController < Projects::ApplicationController
def project_view_files_allowed? def project_view_files_allowed?
!project.empty_repo? && can?(current_user, :download_code, project) !project.empty_repo? && can?(current_user, :download_code, project)
end end
def build_canonical_path(project)
params[:namespace_id] = project.namespace.to_param
params[:id] = project.to_param
url_for(params)
end
end end
...@@ -138,4 +138,8 @@ class UsersController < ApplicationController ...@@ -138,4 +138,8 @@ class UsersController < ApplicationController
def projects_for_current_user def projects_for_current_user
ProjectsFinder.new(current_user: current_user).execute ProjectsFinder.new(current_user: current_user).execute
end end
def build_canonical_path(user)
url_for(params.merge(username: user.to_param))
end
end end
...@@ -591,6 +591,38 @@ exist, you should see something like: ...@@ -591,6 +591,38 @@ exist, you should see something like:
![Environment groups](img/environments_dynamic_groups.png) ![Environment groups](img/environments_dynamic_groups.png)
## Monitoring environments
>**Notes:**
>
- For the monitor dashboard to appear, you need to:
- Have enabled the [Kubernetes integration][kube]
- Have your app deployed on Kubernetes
- Have enabled the [Prometheus integration][prom]
- With GitLab 9.2, all deployments to an environment are shown directly on the
monitoring dashboard
If your application is deployed on Kubernetes and you have enabled Prometheus
collecting metrics, you can monitor the performance behavior of your app
through the environments.
Once configured, GitLab will attempt to retrieve performance metrics for any
environment which has had a successful deployment. If monitoring data was
successfully retrieved, a Monitoring button will appear on the environment's
detail page.
![Environment Detail with Metrics](img/prometheus_environment_detail_with_metrics.png)
Clicking on the Monitoring button will display a new page, showing up to the last
8 hours of performance data. It may take a minute or two for data to appear
after initial deployment.
All deployments to an environment are shown directly on the monitoring dashboard
which allows easy correlation between any changes in performance and a new
version of the app, all without leaving GitLab.
![Monitoring dashboard](img/environments_monitoring.png)
## Checkout deployments locally ## Checkout deployments locally
Since 8.13, a reference in the git repository is saved for each deployment, so Since 8.13, a reference in the git repository is saved for each deployment, so
...@@ -633,3 +665,5 @@ Below are some links you may find interesting: ...@@ -633,3 +665,5 @@ Below are some links you may find interesting:
[gitlab-flow]: ../workflow/gitlab_flow.md [gitlab-flow]: ../workflow/gitlab_flow.md
[gitlab runner]: https://docs.gitlab.com/runner/ [gitlab runner]: https://docs.gitlab.com/runner/
[git-strategy]: yaml/README.md#git-strategy [git-strategy]: yaml/README.md#git-strategy
[kube]: ../user/project/integrations/kubernetes.md
[prom]: ../user/project/integrations/prometheus.md
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
> **Notes:** > **Notes:**
- GitLab Geo is part of [GitLab Enterprise Edition Premium][ee]. - GitLab Geo is part of [GitLab Enterprise Edition Premium][ee].
- Introduced in GitLab Enterprise Edition 8.9. - Introduced in GitLab Enterprise Edition 8.9.
We recommend you use it with at least GitLab Enterprise Edition 8.14. We recommend you use it with at least GitLab Enterprise Edition 8.14 for
basic Geo features, or latest version for a better experience.
- You should make sure that all nodes run the same GitLab version. - You should make sure that all nodes run the same GitLab version.
GitLab Geo allows you to replicate your GitLab instance to other geographical GitLab Geo allows you to replicate your GitLab instance to other geographical
......
...@@ -137,18 +137,6 @@ sensitive data in the database. Any secondary node must have the ...@@ -137,18 +137,6 @@ sensitive data in the database. Any secondary node must have the
sudo -i sudo -i
``` ```
1. Edit `/etc/gitlab/gitlab.rb`:
```
geo_postgresql['enable'] = true
```
1. Reconfigure GitLab:
```
sudo gitlab-ctl reconfigure
```
1. Get the contents of `id_rsa.pub` key that was pre-generated by Omnibus GitLab 1. Get the contents of `id_rsa.pub` key that was pre-generated by Omnibus GitLab
and copy them: and copy them:
......
...@@ -171,6 +171,7 @@ The following guide assumes that: ...@@ -171,6 +171,7 @@ The following guide assumes that:
```ruby ```ruby
geo_secondary_role['enable'] = true geo_secondary_role['enable'] = true
geo_postgresql['enable'] = true
``` ```
1. [Reconfigure GitLab][] for the changes to take effect. 1. [Reconfigure GitLab][] for the changes to take effect.
......
...@@ -17,6 +17,7 @@ the settings page with a default template. To configure the template, see the ...@@ -17,6 +17,7 @@ the settings page with a default template. To configure the template, see the
Integration with Prometheus requires the following: Integration with Prometheus requires the following:
1. GitLab 9.0 or higher 1. GitLab 9.0 or higher
1. The [Kubernetes integration must be enabled][kube] on your project
1. Your app must be deployed on [Kubernetes][] 1. Your app must be deployed on [Kubernetes][]
1. Prometheus must be configured to collect Kubernetes metrics 1. Prometheus must be configured to collect Kubernetes metrics
1. Each metric must be have a label to indicate the environment 1. Each metric must be have a label to indicate the environment
...@@ -159,23 +160,28 @@ The queries utilized by GitLab are shown in the following table. ...@@ -159,23 +160,28 @@ The queries utilized by GitLab are shown in the following table.
## Monitoring CI/CD Environments ## Monitoring CI/CD Environments
Once configured, GitLab will attempt to retrieve performance metrics for any Once configured, GitLab will attempt to retrieve performance metrics for any
environment which has had a successful deployment. If monitoring data was environment which has had a successful deployment.
successfully retrieved, a Monitoring button will appear on the environment's
detail page.
![Environment Detail with Metrics](img/prometheus_environment_detail_with_metrics.png) [Learn more about monitoring environments.](../../../ci/environments.md#monitoring-environments)
Clicking on the Monitoring button will display a new page, showing up to the last ## Determining the performance impact of a merge
8 hours of performance data. It may take a minute or two for data to appear
after initial deployment.
## Determining performance impact of a merge > [Introduced][ce-10408] in GitLab 9.2.
> [Introduced][ce-10408] in GitLab 9.1. Developers can view the performance impact of their changes within the merge
request workflow. When a source branch has been deployed to an environment, a
sparkline will appear showing the average memory consumption of the app. The dot
indicates when the current changes were deployed, with up to 30 minutes of
performance data displayed before and after. The sparkline will be updated after
each commit has been deployed.
After a merge request has been approved, a sparkline will appear on the merge request page displaying the average memory usage of the application. The sparkline includes thirty minutes of data prior to the merge, a dot to indicate the merge itself, and then will begin capturing thirty minutes of data after the merge. Once merged and the target branch has been redeployed, the sparkline will switch
to show the new environments this revision has been deployed to.
This sparkline serves as a quick indicator of the impact on memory consumption of the recently merged changes. If there is a problem, action can then be taken to troubleshoot or revert the merge. Performance data will be available for the duration it is persisted on the
Prometheus server.
![Merge Request with Performance Impact](img/merge_request_performance.png)
## Troubleshooting ## Troubleshooting
...@@ -189,6 +195,7 @@ If the "Attempting to load performance data" screen continues to appear, it coul ...@@ -189,6 +195,7 @@ If the "Attempting to load performance data" screen continues to appear, it coul
[autodeploy]: ../../../ci/autodeploy/index.md [autodeploy]: ../../../ci/autodeploy/index.md
[kubernetes]: https://kubernetes.io [kubernetes]: https://kubernetes.io
[kube]: ./kubernetes.md
[prometheus-k8s-sd]: https://prometheus.io/docs/operating/configuration/#<kubernetes_sd_config> [prometheus-k8s-sd]: https://prometheus.io/docs/operating/configuration/#<kubernetes_sd_config>
[prometheus]: https://prometheus.io [prometheus]: https://prometheus.io
[gitlab-prometheus-k8s-monitor]: ../../../administration/monitoring/prometheus/index.md#configuring-prometheus-to-monitor-kubernetes [gitlab-prometheus-k8s-monitor]: ../../../administration/monitoring/prometheus/index.md#configuring-prometheus-to-monitor-kubernetes
......
...@@ -38,11 +38,6 @@ it's reassigned to someone else to take it from there. ...@@ -38,11 +38,6 @@ it's reassigned to someone else to take it from there.
if a user is not member of that project, it can only be if a user is not member of that project, it can only be
assigned to them if they created the issue themselves. assigned to them if they created the issue themselves.
In [GitLab Enterprise Edition Starter and up](https://about.gitlab.com/gitlab-ee/),
you can assign multiple people to an issue.
The interface is exactly the same, except that you can select multiple users in the dropdown.
The multiple assignees are also visible in issue lists and issue boards, and all get the same notifications.
#### 4. Milestone #### 4. Milestone
- Select a [milestone](../milestones/index.md) to attribute that issue to. - Select a [milestone](../milestones/index.md) to attribute that issue to.
......
...@@ -41,6 +41,10 @@ For more examples on artifacts, follow the artifacts reference in ...@@ -41,6 +41,10 @@ For more examples on artifacts, follow the artifacts reference in
## Browsing job artifacts ## Browsing job artifacts
>**Note:**
With GitLab 9.2, PDFs, images, videos and other formats can be previewed directly
in the job artifacts browser without the need to download them.
After a job finishes, if you visit the job's specific page, you can see After a job finishes, if you visit the job's specific page, you can see
that there are two buttons. One is for downloading the artifacts archive and that there are two buttons. One is for downloading the artifacts archive and
the other for browsing its contents. the other for browsing its contents.
......
## Advanced search syntax # Advanced search syntax
If your site administrator has enabled [Elasticsearch integration](../../integration/elasticsearch.md) >**Notes:**
then some advanced search functionality is available. - Introduced in [GitLab Enterprise Premium][ee] 9.2
- The [Elasticsearch integration](../../integration/elasticsearch.md) needs to
be enabled
Full details can be found in the Full details can be found in the
[Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-simple-query-string-query.html#_simple_query_string_syntax) [Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-simple-query-string-query.html#_simple_query_string_syntax)
...@@ -16,3 +18,4 @@ but here's a quick guide: ...@@ -16,3 +18,4 @@ but here's a quick guide:
* To match a partial word, use `*`: `bug find_by_*` * To match a partial word, use `*`: `bug find_by_*`
* To find a term containing one of these symbols, use `\`: `argument \-last` * To find a term containing one of these symbols, use `\`: `argument \-last`
[ee]: https://about.gitlab.com/gitlab-ee/
...@@ -98,3 +98,14 @@ you'll be able to, besides filtering them by **Name**, **Author**, **Assignee**, ...@@ -98,3 +98,14 @@ you'll be able to, besides filtering them by **Name**, **Author**, **Assignee**,
and **Labels**, select multiple issues to add to a list of your choice: and **Labels**, select multiple issues to add to a list of your choice:
![search and select issues to add to board](img/search_issues_board.png) ![search and select issues to add to board](img/search_issues_board.png)
## Advanced search syntax
> Available in [GitLab Enterprise Edition Premium][ee].
If the [Elasticsearch integration](../../integration/elasticsearch.md) is
enabled, then some advanced search functionality is available.
[Learn how to use the advanced search syntax.](advanced_search_syntax.md)
[ee]: https://about.gitlab.com/gitlab-ee/
...@@ -21,7 +21,6 @@ describe Groups::MilestonesController do ...@@ -21,7 +21,6 @@ describe Groups::MilestonesController do
sign_in(user) sign_in(user)
group.add_owner(user) group.add_owner(user)
project.team << [user, :master] project.team << [user, :master]
controller.instance_variable_set(:@group, group)
end end
it_behaves_like 'milestone tabs' it_behaves_like 'milestone tabs'
...@@ -29,7 +28,7 @@ describe Groups::MilestonesController do ...@@ -29,7 +28,7 @@ describe Groups::MilestonesController do
describe "#create" do describe "#create" do
it "creates group milestone with Chinese title" do it "creates group milestone with Chinese title" do
post :create, post :create,
group_id: group.id, group_id: group.to_param,
milestone: { project_ids: [project.id, project2.id], title: title } milestone: { project_ids: [project.id, project2.id], title: title }
expect(response).to redirect_to(group_milestone_path(group, title.to_slug.to_s, title: title)) expect(response).to redirect_to(group_milestone_path(group, title.to_slug.to_s, title: title))
...@@ -37,9 +36,139 @@ describe Groups::MilestonesController do ...@@ -37,9 +36,139 @@ describe Groups::MilestonesController do
end end
it "redirects to new when there are no project ids" do it "redirects to new when there are no project ids" do
post :create, group_id: group.id, milestone: { title: title, project_ids: [""] } post :create, group_id: group.to_param, milestone: { title: title, project_ids: [""] }
expect(response).to render_template :new expect(response).to render_template :new
expect(assigns(:milestone).errors).not_to be_nil expect(assigns(:milestone).errors).not_to be_nil
end end
end end
describe '#ensure_canonical_path' do
before do
sign_in(user)
end
context 'for a GET request' do
context 'when requesting the canonical path' do
context 'non-show path' do
context 'with exactly matching casing' do
it 'does not redirect' do
get :index, group_id: group.to_param
expect(response).not_to have_http_status(301)
end
end
context 'with different casing' do
it 'redirects to the correct casing' do
get :index, group_id: group.to_param.upcase
expect(response).to redirect_to(group_milestones_path(group.to_param))
expect(controller).not_to set_flash[:notice]
end
end
end
context 'show path' do
context 'with exactly matching casing' do
it 'does not redirect' do
get :show, group_id: group.to_param, id: title
expect(response).not_to have_http_status(301)
end
end
context 'with different casing' do
it 'redirects to the correct casing' do
get :show, group_id: group.to_param.upcase, id: title
expect(response).to redirect_to(group_milestone_path(group.to_param, title))
expect(controller).not_to set_flash[:notice]
end
end
end
end
context 'when requesting a redirected path' do
let(:redirect_route) { group.redirect_routes.create(path: 'old-path') }
it 'redirects to the canonical path' do
get :merge_requests, group_id: redirect_route.path, id: title
expect(response).to redirect_to(merge_requests_group_milestone_path(group.to_param, title))
expect(controller).to set_flash[:notice].to(group_moved_message(redirect_route, group))
end
context 'when the old group path is a substring of the scheme or host' do
let(:redirect_route) { group.redirect_routes.create(path: 'http') }
it 'does not modify the requested host' do
get :merge_requests, group_id: redirect_route.path, id: title
expect(response).to redirect_to(merge_requests_group_milestone_path(group.to_param, title))
expect(controller).to set_flash[:notice].to(group_moved_message(redirect_route, group))
end
end
context 'when the old group path is substring of groups' do
# I.e. /groups/oups should not become /grfoo/oups
let(:redirect_route) { group.redirect_routes.create(path: 'oups') }
it 'does not modify the /groups part of the path' do
get :merge_requests, group_id: redirect_route.path, id: title
expect(response).to redirect_to(merge_requests_group_milestone_path(group.to_param, title))
expect(controller).to set_flash[:notice].to(group_moved_message(redirect_route, group))
end
end
context 'when the old group path is substring of groups plus the new path' do
# I.e. /groups/oups/oup should not become /grfoos
let(:redirect_route) { group.redirect_routes.create(path: 'oups/oup') }
it 'does not modify the /groups part of the path' do
get :merge_requests, group_id: redirect_route.path, id: title
expect(response).to redirect_to(merge_requests_group_milestone_path(group.to_param, title))
expect(controller).to set_flash[:notice].to(group_moved_message(redirect_route, group))
end
end
end
end
end
context 'for a non-GET request' do
context 'when requesting the canonical path with different casing' do
it 'does not 404' do
post :create,
group_id: group.to_param,
milestone: { project_ids: [project.id, project2.id], title: title }
expect(response).not_to have_http_status(404)
end
it 'does not redirect to the correct casing' do
post :create,
group_id: group.to_param,
milestone: { project_ids: [project.id, project2.id], title: title }
expect(response).not_to have_http_status(301)
end
end
context 'when requesting a redirected path' do
let(:redirect_route) { group.redirect_routes.create(path: 'old-path') }
it 'returns not found' do
post :create,
group_id: redirect_route.path,
milestone: { project_ids: [project.id, project2.id], title: title }
expect(response).to have_http_status(404)
end
end
end
def group_moved_message(redirect_route, group)
"Group '#{redirect_route.path}' was moved to '#{group.full_path}'. Please update any links and bookmarks that may still have the old path."
end
end end
This diff is collapsed.
...@@ -157,4 +157,74 @@ describe Projects::LabelsController do ...@@ -157,4 +157,74 @@ describe Projects::LabelsController do
end end
end end
end end
describe '#ensure_canonical_path' do
before do
sign_in(user)
end
context 'for a GET request' do
context 'when requesting the canonical path' do
context 'non-show path' do
context 'with exactly matching casing' do
it 'does not redirect' do
get :index, namespace_id: project.namespace, project_id: project.to_param
expect(response).not_to have_http_status(301)
end
end
context 'with different casing' do
it 'redirects to the correct casing' do
get :index, namespace_id: project.namespace, project_id: project.to_param.upcase
expect(response).to redirect_to(namespace_project_labels_path(project.namespace, project))
expect(controller).not_to set_flash[:notice]
end
end
end
end
context 'when requesting a redirected path' do
let!(:redirect_route) { project.redirect_routes.create(path: project.full_path + 'old') }
it 'redirects to the canonical path' do
get :index, namespace_id: project.namespace, project_id: project.to_param + 'old'
expect(response).to redirect_to(namespace_project_labels_path(project.namespace, project))
expect(controller).to set_flash[:notice].to(project_moved_message(redirect_route, project))
end
end
end
end
context 'for a non-GET request' do
context 'when requesting the canonical path with different casing' do
it 'does not 404' do
post :generate, namespace_id: project.namespace, project_id: project
expect(response).not_to have_http_status(404)
end
it 'does not redirect to the correct casing' do
post :generate, namespace_id: project.namespace, project_id: project
expect(response).not_to have_http_status(301)
end
end
context 'when requesting a redirected path' do
let!(:redirect_route) { project.redirect_routes.create(path: project.full_path + 'old') }
it 'returns not found' do
post :generate, namespace_id: project.namespace, project_id: project.to_param + 'old'
expect(response).to have_http_status(404)
end
end
end
def project_moved_message(redirect_route, project)
"Project '#{redirect_route.path}' was moved to '#{project.full_path}'. Please update any links and bookmarks that may still have the old path."
end
end end
...@@ -190,27 +190,6 @@ describe ProjectsController do ...@@ -190,27 +190,6 @@ describe ProjectsController do
end end
end end
context "when requested with case sensitive namespace and project path" do
context "when there is a match with the same casing" do
it "loads the project" do
get :show, namespace_id: public_project.namespace, id: public_project
expect(assigns(:project)).to eq(public_project)
expect(response).to have_http_status(200)
end
end
context "when there is a match with different casing" do
it "redirects to the normalized path" do
get :show, namespace_id: public_project.namespace, id: public_project.path.upcase
expect(assigns(:project)).to eq(public_project)
expect(response).to redirect_to("/#{public_project.full_path}")
expect(controller).not_to set_flash[:notice]
end
end
end
context "when the url contains .atom" do context "when the url contains .atom" do
let(:public_project_with_dot_atom) { build(:empty_project, :public, name: 'my.atom', path: 'my.atom') } let(:public_project_with_dot_atom) { build(:empty_project, :public, name: 'my.atom', path: 'my.atom') }
...@@ -240,6 +219,7 @@ describe ProjectsController do ...@@ -240,6 +219,7 @@ describe ProjectsController do
expect(response).to redirect_to(namespace_project_path) expect(response).to redirect_to(namespace_project_path)
end end
end end
<<<<<<< HEAD
context 'when requesting a redirected path' do context 'when requesting a redirected path' do
let!(:redirect_route) { public_project.redirect_routes.create!(path: "foo/bar") } let!(:redirect_route) { public_project.redirect_routes.create!(path: "foo/bar") }
...@@ -251,6 +231,8 @@ describe ProjectsController do ...@@ -251,6 +231,8 @@ describe ProjectsController do
expect(controller).to set_flash[:notice].to(project_moved_message(redirect_route, public_project)) expect(controller).to set_flash[:notice].to(project_moved_message(redirect_route, public_project))
end end
end end
=======
>>>>>>> origin/master
end end
describe "#update" do describe "#update" do
...@@ -277,34 +259,6 @@ describe ProjectsController do ...@@ -277,34 +259,6 @@ describe ProjectsController do
expect(assigns(:repository).path).to eq(project.repository.path) expect(assigns(:repository).path).to eq(project.repository.path)
expect(response).to have_http_status(302) expect(response).to have_http_status(302)
end end
context 'when requesting the canonical path' do
it "is case-insensitive" do
controller.instance_variable_set(:@project, project)
put :update,
namespace_id: 'FOo',
id: 'baR',
project: project_params
expect(project.repository.path).to include(new_path)
expect(assigns(:repository).path).to eq(project.repository.path)
expect(response).to have_http_status(302)
end
end
context 'when requesting a redirected path' do
let!(:redirect_route) { project.redirect_routes.create!(path: "foo/bar") }
it 'returns not found' do
put :update,
namespace_id: 'foo',
id: 'bar',
project: project_params
expect(response).to have_http_status(404)
end
end
end end
describe "#destroy" do describe "#destroy" do
...@@ -340,31 +294,6 @@ describe ProjectsController do ...@@ -340,31 +294,6 @@ describe ProjectsController do
expect(merge_request.reload.state).to eq('closed') expect(merge_request.reload.state).to eq('closed')
end end
end end
context 'when requesting the canonical path' do
it "is case-insensitive" do
controller.instance_variable_set(:@project, project)
sign_in(admin)
orig_id = project.id
delete :destroy, namespace_id: project.namespace, id: project.path.upcase
expect { Project.find(orig_id) }.to raise_error(ActiveRecord::RecordNotFound)
expect(response).to have_http_status(302)
expect(response).to redirect_to(dashboard_projects_path)
end
end
context 'when requesting a redirected path' do
let!(:redirect_route) { project.redirect_routes.create!(path: "foo/bar") }
it 'returns not found' do
sign_in(admin)
delete :destroy, namespace_id: 'foo', id: 'bar'
expect(response).to have_http_status(404)
end
end
end end
describe 'PUT #new_issue_address' do describe 'PUT #new_issue_address' do
...@@ -486,6 +415,7 @@ describe ProjectsController do ...@@ -486,6 +415,7 @@ describe ProjectsController do
expect(parsed_body["Tags"]).to include("v1.0.0") expect(parsed_body["Tags"]).to include("v1.0.0")
expect(parsed_body["Commits"]).to include("123456") expect(parsed_body["Commits"]).to include("123456")
end end
<<<<<<< HEAD
context 'when requesting a redirected path' do context 'when requesting a redirected path' do
let!(:redirect_route) { public_project.redirect_routes.create!(path: "foo/bar") } let!(:redirect_route) { public_project.redirect_routes.create!(path: "foo/bar") }
...@@ -497,6 +427,8 @@ describe ProjectsController do ...@@ -497,6 +427,8 @@ describe ProjectsController do
expect(controller).to set_flash[:notice].to(project_moved_message(redirect_route, public_project)) expect(controller).to set_flash[:notice].to(project_moved_message(redirect_route, public_project))
end end
end end
=======
>>>>>>> origin/master
end end
describe 'GET edit' do describe 'GET edit' do
...@@ -531,6 +463,112 @@ describe ProjectsController do ...@@ -531,6 +463,112 @@ describe ProjectsController do
end end
end end
<<<<<<< HEAD
=======
describe '#ensure_canonical_path' do
before do
sign_in(user)
end
context 'for a GET request' do
context 'when requesting the canonical path' do
context "with exactly matching casing" do
it "loads the project" do
get :show, namespace_id: public_project.namespace, id: public_project
expect(assigns(:project)).to eq(public_project)
expect(response).to have_http_status(200)
end
end
context "with different casing" do
it "redirects to the normalized path" do
get :show, namespace_id: public_project.namespace, id: public_project.path.upcase
expect(assigns(:project)).to eq(public_project)
expect(response).to redirect_to("/#{public_project.full_path}")
expect(controller).not_to set_flash[:notice]
end
end
end
context 'when requesting a redirected path' do
let!(:redirect_route) { public_project.redirect_routes.create!(path: "foo/bar") }
it 'redirects to the canonical path' do
get :show, namespace_id: 'foo', id: 'bar'
expect(response).to redirect_to(public_project)
expect(controller).to set_flash[:notice].to(project_moved_message(redirect_route, public_project))
end
it 'redirects to the canonical path (testing non-show action)' do
get :refs, namespace_id: 'foo', id: 'bar'
expect(response).to redirect_to(refs_namespace_project_path(namespace_id: public_project.namespace, id: public_project))
expect(controller).to set_flash[:notice].to(project_moved_message(redirect_route, public_project))
end
end
end
context 'for a POST request' do
context 'when requesting the canonical path with different casing' do
it 'does not 404' do
post :toggle_star, namespace_id: public_project.namespace, id: public_project.path.upcase
expect(response).not_to have_http_status(404)
end
it 'does not redirect to the correct casing' do
post :toggle_star, namespace_id: public_project.namespace, id: public_project.path.upcase
expect(response).not_to have_http_status(301)
end
end
context 'when requesting a redirected path' do
let!(:redirect_route) { public_project.redirect_routes.create!(path: "foo/bar") }
it 'returns not found' do
post :toggle_star, namespace_id: 'foo', id: 'bar'
expect(response).to have_http_status(404)
end
end
end
context 'for a DELETE request' do
before do
sign_in(create(:admin))
end
context 'when requesting the canonical path with different casing' do
it 'does not 404' do
delete :destroy, namespace_id: project.namespace, id: project.path.upcase
expect(response).not_to have_http_status(404)
end
it 'does not redirect to the correct casing' do
delete :destroy, namespace_id: project.namespace, id: project.path.upcase
expect(response).not_to have_http_status(301)
end
end
context 'when requesting a redirected path' do
let!(:redirect_route) { project.redirect_routes.create!(path: "foo/bar") }
it 'returns not found' do
delete :destroy, namespace_id: 'foo', id: 'bar'
expect(response).to have_http_status(404)
end
end
end
end
>>>>>>> origin/master
def project_moved_message(redirect_route, project) def project_moved_message(redirect_route, project)
"Project '#{redirect_route.path}' was moved to '#{project.full_path}'. Please update any links and bookmarks that may still have the old path." "Project '#{redirect_route.path}' was moved to '#{project.full_path}'. Please update any links and bookmarks that may still have the old path."
end end
......
...@@ -53,6 +53,7 @@ describe UsersController do ...@@ -53,6 +53,7 @@ describe UsersController do
end end
end end
<<<<<<< HEAD
context 'when requesting the canonical path' do context 'when requesting the canonical path' do
let(:user) { create(:user, username: 'CamelCaseUser') } let(:user) { create(:user, username: 'CamelCaseUser') }
...@@ -87,6 +88,8 @@ describe UsersController do ...@@ -87,6 +88,8 @@ describe UsersController do
end end
end end
=======
>>>>>>> origin/master
context 'when a user by that username does not exist' do context 'when a user by that username does not exist' do
context 'when logged out' do context 'when logged out' do
it 'redirects to login page' do it 'redirects to login page' do
...@@ -131,6 +134,7 @@ describe UsersController do ...@@ -131,6 +134,7 @@ describe UsersController do
expect(assigns(:contributions_calendar).projects.count).to eq(2) expect(assigns(:contributions_calendar).projects.count).to eq(2)
end end
end end
<<<<<<< HEAD
context 'when requesting the canonical path' do context 'when requesting the canonical path' do
let(:user) { create(:user, username: 'CamelCaseUser') } let(:user) { create(:user, username: 'CamelCaseUser') }
...@@ -165,6 +169,8 @@ describe UsersController do ...@@ -165,6 +169,8 @@ describe UsersController do
expect(controller).to set_flash[:notice].to(user_moved_message(redirect_route, user)) expect(controller).to set_flash[:notice].to(user_moved_message(redirect_route, user))
end end
end end
=======
>>>>>>> origin/master
end end
describe 'GET #calendar_activities' do describe 'GET #calendar_activities' do
...@@ -187,6 +193,7 @@ describe UsersController do ...@@ -187,6 +193,7 @@ describe UsersController do
get :calendar_activities, username: user.username get :calendar_activities, username: user.username
expect(response).to render_template('calendar_activities') expect(response).to render_template('calendar_activities')
end end
<<<<<<< HEAD
context 'when requesting the canonical path' do context 'when requesting the canonical path' do
let(:user) { create(:user, username: 'CamelCaseUser') } let(:user) { create(:user, username: 'CamelCaseUser') }
...@@ -219,6 +226,8 @@ describe UsersController do ...@@ -219,6 +226,8 @@ describe UsersController do
expect(controller).to set_flash[:notice].to(user_moved_message(redirect_route, user)) expect(controller).to set_flash[:notice].to(user_moved_message(redirect_route, user))
end end
end end
=======
>>>>>>> origin/master
end end
describe 'GET #snippets' do describe 'GET #snippets' do
...@@ -241,6 +250,7 @@ describe UsersController do ...@@ -241,6 +250,7 @@ describe UsersController do
expect(JSON.parse(response.body)).to have_key('html') expect(JSON.parse(response.body)).to have_key('html')
end end
end end
<<<<<<< HEAD
context 'when requesting the canonical path' do context 'when requesting the canonical path' do
let(:user) { create(:user, username: 'CamelCaseUser') } let(:user) { create(:user, username: 'CamelCaseUser') }
...@@ -273,6 +283,8 @@ describe UsersController do ...@@ -273,6 +283,8 @@ describe UsersController do
expect(controller).to set_flash[:notice].to(user_moved_message(redirect_route, user)) expect(controller).to set_flash[:notice].to(user_moved_message(redirect_route, user))
end end
end end
=======
>>>>>>> origin/master
end end
describe 'GET #exists' do describe 'GET #exists' do
...@@ -321,6 +333,130 @@ describe UsersController do ...@@ -321,6 +333,130 @@ describe UsersController do
end end
end end
<<<<<<< HEAD
=======
describe '#ensure_canonical_path' do
before do
sign_in(user)
end
context 'for a GET request' do
context 'when requesting users at the root path' do
context 'when requesting the canonical path' do
let(:user) { create(:user, username: 'CamelCaseUser') }
context 'with exactly matching casing' do
it 'responds with success' do
get :show, username: user.username
expect(response).to be_success
end
end
context 'with different casing' do
it 'redirects to the correct casing' do
get :show, username: user.username.downcase
expect(response).to redirect_to(user)
expect(controller).not_to set_flash[:notice]
end
end
end
context 'when requesting a redirected path' do
let(:redirect_route) { user.namespace.redirect_routes.create(path: 'old-path') }
it 'redirects to the canonical path' do
get :show, username: redirect_route.path
expect(response).to redirect_to(user)
expect(controller).to set_flash[:notice].to(user_moved_message(redirect_route, user))
end
context 'when the old path is a substring of the scheme or host' do
let(:redirect_route) { user.namespace.redirect_routes.create(path: 'http') }
it 'does not modify the requested host' do
get :show, username: redirect_route.path
expect(response).to redirect_to(user)
expect(controller).to set_flash[:notice].to(user_moved_message(redirect_route, user))
end
end
context 'when the old path is substring of users' do
let(:redirect_route) { user.namespace.redirect_routes.create(path: 'ser') }
it 'redirects to the canonical path' do
get :show, username: redirect_route.path
expect(response).to redirect_to(user)
expect(controller).to set_flash[:notice].to(user_moved_message(redirect_route, user))
end
end
end
end
context 'when requesting users under the /users path' do
context 'when requesting the canonical path' do
let(:user) { create(:user, username: 'CamelCaseUser') }
context 'with exactly matching casing' do
it 'responds with success' do
get :projects, username: user.username
expect(response).to be_success
end
end
context 'with different casing' do
it 'redirects to the correct casing' do
get :projects, username: user.username.downcase
expect(response).to redirect_to(user_projects_path(user))
expect(controller).not_to set_flash[:notice]
end
end
end
context 'when requesting a redirected path' do
let(:redirect_route) { user.namespace.redirect_routes.create(path: 'old-path') }
it 'redirects to the canonical path' do
get :projects, username: redirect_route.path
expect(response).to redirect_to(user_projects_path(user))
expect(controller).to set_flash[:notice].to(user_moved_message(redirect_route, user))
end
context 'when the old path is a substring of the scheme or host' do
let(:redirect_route) { user.namespace.redirect_routes.create(path: 'http') }
it 'does not modify the requested host' do
get :projects, username: redirect_route.path
expect(response).to redirect_to(user_projects_path(user))
expect(controller).to set_flash[:notice].to(user_moved_message(redirect_route, user))
end
end
context 'when the old path is substring of users' do
let(:redirect_route) { user.namespace.redirect_routes.create(path: 'ser') }
# I.e. /users/ser should not become /ufoos/ser
it 'does not modify the /users part of the path' do
get :projects, username: redirect_route.path
expect(response).to redirect_to(user_projects_path(user))
expect(controller).to set_flash[:notice].to(user_moved_message(redirect_route, user))
end
end
end
end
end
end
>>>>>>> origin/master
def user_moved_message(redirect_route, user) def user_moved_message(redirect_route, user)
"User '#{redirect_route.path}' was moved to '#{user.full_path}'. Please update any links and bookmarks that may still have the old path." "User '#{redirect_route.path}' was moved to '#{user.full_path}'. Please update any links and bookmarks that may still have the old path."
end end
......
...@@ -21,6 +21,10 @@ import ApprovalsFooter from '~/vue_merge_request_widget/ee/components/approvals/ ...@@ -21,6 +21,10 @@ import ApprovalsFooter from '~/vue_merge_request_widget/ee/components/approvals/
`); `);
this.initialData = { this.initialData = {
mr: {
state: 'readyToMerge',
},
service: {},
userCanApprove: false, userCanApprove: false,
userHasApproved: true, userHasApproved: true,
approvedBy: [], approvedBy: [],
...@@ -53,7 +57,9 @@ import ApprovalsFooter from '~/vue_merge_request_widget/ee/components/approvals/ ...@@ -53,7 +57,9 @@ import ApprovalsFooter from '~/vue_merge_request_widget/ee/components/approvals/
describe('Computed properties', function () { describe('Computed properties', function () {
it('should correctly set showUnapproveButton when the user can unapprove', function () { it('should correctly set showUnapproveButton when the user can unapprove', function () {
expect(this.approvalsFooter.showUnapproveButton).toBe(true); expect(this.approvalsFooter.showUnapproveButton).toBeTruthy();
this.approvalsFooter.mr.state = 'merged';
expect(this.approvalsFooter.showUnapproveButton).toBeFalsy();
}); });
it('should correctly set showUnapproveButton when the user can not unapprove', function (done) { it('should correctly set showUnapproveButton when the user can not unapprove', function (done) {
......
shared_examples 'milestone tabs' do shared_examples 'milestone tabs' do
def go(path, extra_params = {}) def go(path, extra_params = {})
params = if milestone.is_a?(GlobalMilestone) params = if milestone.is_a?(GlobalMilestone)
{ group_id: group.id, id: milestone.safe_title, title: milestone.title } { group_id: group.to_param, id: milestone.safe_title, title: milestone.title }
else else
{ namespace_id: project.namespace.to_param, project_id: project, id: milestone.iid } { namespace_id: project.namespace.to_param, project_id: project, id: milestone.iid }
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment