Skip to content

Commit

Permalink
Project Settings / Delete Project (#2821)
Browse files Browse the repository at this point in the history
* Stub out project settings management page

Also clean up some templates

* Move delete/confirm utility functions

So they can be reused by non-admin views

* Delete project

* Rafactor UI in light of structural changes

* Don't show releases link if there are none

* Improve modals and nav on mobile UI

* Make releases the default
  • Loading branch information
di authored Jan 29, 2018
1 parent 14a8c15 commit 0c46c65
Show file tree
Hide file tree
Showing 26 changed files with 588 additions and 400 deletions.
90 changes: 84 additions & 6 deletions tests/unit/manage/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@
import uuid

import pretend
import pytest

from pyramid.httpexceptions import HTTPSeeOther
from webob.multidict import MultiDict

from warehouse.manage import views
from warehouse.accounts.interfaces import IUserService
from warehouse.packaging.models import JournalEntry, Role
from warehouse.packaging.models import JournalEntry, Project, Role

from ...common.db.packaging import ProjectFactory, RoleFactory, UserFactory

Expand Down Expand Up @@ -50,6 +51,84 @@ def test_manage_project_settings(self):
"project": project,
}

def test_delete_project_no_confirm(self):
project = pretend.stub(normalized_name='foo')
request = pretend.stub(
POST={},
session=pretend.stub(
flash=pretend.call_recorder(lambda *a, **kw: None),
),
route_path=lambda *a, **kw: "/foo/bar/",
)

with pytest.raises(HTTPSeeOther) as exc:
views.delete_project(project, request)
assert exc.value.status_code == 303
assert exc.value.headers["Location"] == "/foo/bar/"

assert request.session.flash.calls == [
pretend.call("Must confirm the request.", queue="error"),
]

def test_delete_project_wrong_confirm(self):
project = pretend.stub(normalized_name='foo')
request = pretend.stub(
POST={"confirm": "bar"},
session=pretend.stub(
flash=pretend.call_recorder(lambda *a, **kw: None),
),
route_path=lambda *a, **kw: "/foo/bar/",
)

with pytest.raises(HTTPSeeOther) as exc:
views.delete_project(project, request)
assert exc.value.status_code == 303
assert exc.value.headers["Location"] == "/foo/bar/"

assert request.session.flash.calls == [
pretend.call("'bar' is not the same as 'foo'", queue="error"),
]

def test_delete_project(self, db_request):
project = ProjectFactory.create(name="foo")

db_request.route_path = pretend.call_recorder(
lambda *a, **kw: "/the-redirect"
)
db_request.session = pretend.stub(
flash=pretend.call_recorder(lambda *a, **kw: None),
)
db_request.POST["confirm"] = project.normalized_name
db_request.user = UserFactory.create()
db_request.remote_addr = "192.168.1.1"

result = views.delete_project(project, db_request)

assert db_request.session.flash.calls == [
pretend.call(
"Successfully deleted the project 'foo'.",
queue="success"
),
]
assert db_request.route_path.calls == [
pretend.call('manage.projects'),
]
assert isinstance(result, HTTPSeeOther)
assert result.headers["Location"] == "/the-redirect"
assert not (db_request.db.query(Project)
.filter(Project.name == "foo").count())


class TestManageProjectReleases:

def test_manage_project_releases(self):
request = pretend.stub()
project = pretend.stub()

assert views.manage_project_releases(project, request) == {
"project": project,
}


class TestManageProjectRoles:

Expand All @@ -76,7 +155,6 @@ def test_get_manage_project_roles(self, db_request):
pretend.call(db_request.POST, user_service=user_service),
]
assert result == {

"project": project,
"roles_by_user": {user.username: [role]},
"form": form_obj,
Expand Down Expand Up @@ -252,7 +330,7 @@ def test_change_role(self, db_request):

assert role.role_name == new_role_name
assert db_request.route_path.calls == [
pretend.call('manage.project.roles', name=project.name),
pretend.call('manage.project.roles', project_name=project.name),
]
assert db_request.session.flash.calls == [
pretend.call("Successfully changed role", queue="success"),
Expand Down Expand Up @@ -282,7 +360,7 @@ def test_change_role_invalid_role_name(self, pyramid_request):
result = views.change_project_role(project, pyramid_request)

assert pyramid_request.route_path.calls == [
pretend.call('manage.project.roles', name=project.name),
pretend.call('manage.project.roles', project_name=project.name),
]
assert isinstance(result, HTTPSeeOther)
assert result.headers["Location"] == "/the-redirect"
Expand Down Expand Up @@ -317,7 +395,7 @@ def test_change_role_when_multiple(self, db_request):

assert db_request.db.query(Role).all() == [maintainer_role]
assert db_request.route_path.calls == [
pretend.call('manage.project.roles', name=project.name),
pretend.call('manage.project.roles', project_name=project.name),
]
assert db_request.session.flash.calls == [
pretend.call("Successfully changed role", queue="success"),
Expand Down Expand Up @@ -441,7 +519,7 @@ def test_delete_role(self, db_request):
result = views.delete_project_role(project, db_request)

assert db_request.route_path.calls == [
pretend.call('manage.project.roles', name=project.name),
pretend.call('manage.project.roles', project_name=project.name),
]
assert db_request.db.query(Role).all() == []
assert db_request.session.flash.calls == [
Expand Down
30 changes: 22 additions & 8 deletions tests/unit/test_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,30 +144,44 @@ def add_policy(name, filename):
),
pretend.call(
"manage.project.settings",
"/manage/project/{name}/settings/",
"/manage/project/{project_name}/settings/",
factory="warehouse.packaging.models:ProjectFactory",
traverse="/{name}",
traverse="/{project_name}",
domain=warehouse,
),
pretend.call(
"manage.project.delete_project",
"/manage/project/{project_name}/delete_project/",
factory="warehouse.packaging.models:ProjectFactory",
traverse="/{project_name}",
domain=warehouse,
),
pretend.call(
"manage.project.releases",
"/manage/project/{project_name}/releases/",
factory="warehouse.packaging.models:ProjectFactory",
traverse="/{project_name}",
domain=warehouse,
),
pretend.call(
"manage.project.roles",
"/manage/project/{name}/collaboration/",
"/manage/project/{project_name}/collaboration/",
factory="warehouse.packaging.models:ProjectFactory",
traverse="/{name}",
traverse="/{project_name}",
domain=warehouse,
),
pretend.call(
"manage.project.change_role",
"/manage/project/{name}/collaboration/change/",
"/manage/project/{project_name}/collaboration/change/",
factory="warehouse.packaging.models:ProjectFactory",
traverse="/{name}",
traverse="/{project_name}",
domain=warehouse,
),
pretend.call(
"manage.project.delete_role",
"/manage/project/{name}/collaboration/delete/",
"/manage/project/{project_name}/collaboration/delete/",
factory="warehouse.packaging.models:ProjectFactory",
traverse="/{name}",
traverse="/{project_name}",
domain=warehouse,
),
pretend.call(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@
from pretend import call, call_recorder, stub
from pyramid.httpexceptions import HTTPSeeOther

from warehouse.admin.utils import confirm_project, remove_project
from warehouse.packaging.models import (
Project, Release, Dependency, File, Role, JournalEntry
)
from warehouse.utils.project import confirm_project, remove_project

from ...common.db.accounts import UserFactory
from ...common.db.packaging import (
Expand All @@ -34,7 +34,7 @@ def test_confirm():
session=stub(flash=call_recorder(lambda *a, **kw: stub())),
)

confirm_project(project, request)
confirm_project(project, request, fail_route='fail_route')

assert request.route_path.calls == []
assert request.session.flash.calls == []
Expand All @@ -49,11 +49,11 @@ def test_confirm_no_input():
)

with pytest.raises(HTTPSeeOther) as err:
confirm_project(project, request)
confirm_project(project, request, fail_route='fail_route')
assert err.value == '/the-redirect'

assert request.route_path.calls == [
call('admin.project.detail', project_name='foobar')
call('fail_route', project_name='foobar')
]
assert request.session.flash.calls == [
call('Must confirm the request.', queue='error')
Expand All @@ -69,11 +69,11 @@ def test_confirm_incorrect_input():
)

with pytest.raises(HTTPSeeOther) as err:
confirm_project(project, request)
confirm_project(project, request, fail_route='fail_route')
assert err.value == '/the-redirect'

assert request.route_path.calls == [
call('admin.project.detail', project_name='foobar')
call('fail_route', project_name='foobar')
]
assert request.session.flash.calls == [
call("'bizbaz' is not the same as 'foobar'", queue='error')
Expand Down
2 changes: 1 addition & 1 deletion warehouse/admin/views/blacklist.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@
from sqlalchemy.orm.exc import NoResultFound

from warehouse.accounts.models import User
from warehouse.admin.utils import remove_project
from warehouse.packaging.models import (
Project, Release, File, Role, BlacklistedProject
)
from warehouse.utils.http import is_safe_url
from warehouse.utils.paginate import paginate_url_factory
from warehouse.utils.project import remove_project


@view_config(
Expand Down
4 changes: 2 additions & 2 deletions warehouse/admin/views/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
from sqlalchemy import or_

from warehouse.accounts.models import User
from warehouse.admin.utils import confirm_project, remove_project
from warehouse.packaging.models import Project, Release, Role, JournalEntry
from warehouse.utils.paginate import paginate_url_factory
from warehouse.utils.project import confirm_project, remove_project
from warehouse.forklift.legacy import MAX_FILESIZE

ONE_MB = 1024 * 1024 # bytes
Expand Down Expand Up @@ -265,7 +265,7 @@ def set_upload_limit(project, request):
require_methods=False,
)
def delete_project(project, request):
confirm_project(project, request)
confirm_project(project, request, fail_route="admin.project.detail")
remove_project(project, request)

return HTTPSeeOther(request.route_path('admin.project.list'))
31 changes: 28 additions & 3 deletions warehouse/manage/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from warehouse.accounts.models import User
from warehouse.manage.forms import CreateRoleForm, ChangeRoleForm
from warehouse.packaging.models import JournalEntry, Role
from warehouse.utils.project import confirm_project, remove_project


@view_config(
Expand All @@ -46,7 +47,7 @@ def manage_projects(request):

@view_config(
route_name="manage.project.settings",
renderer="manage/project.html",
renderer="manage/settings.html",
uses_session=True,
permission="manage",
effective_principals=Authenticated,
Expand All @@ -55,6 +56,30 @@ def manage_project_settings(project, request):
return {"project": project}


@view_config(
route_name="manage.project.delete_project",
uses_session=True,
require_methods=["POST"],
permission="manage",
)
def delete_project(project, request):
confirm_project(project, request, fail_route="manage.project.settings")
remove_project(project, request)

return HTTPSeeOther(request.route_path('manage.projects'))


@view_config(
route_name="manage.project.releases",
renderer="manage/releases.html",
uses_session=True,
permission="manage",
effective_principals=Authenticated,
)
def manage_project_releases(project, request):
return {"project": project}


@view_config(
route_name="manage.project.roles",
renderer="manage/roles.html",
Expand Down Expand Up @@ -208,7 +233,7 @@ def change_project_role(project, request, _form_class=ChangeRoleForm):
request.session.flash("Could not find role", queue="error")

return HTTPSeeOther(
request.route_path('manage.project.roles', name=project.name)
request.route_path('manage.project.roles', project_name=project.name)
)


Expand Down Expand Up @@ -253,5 +278,5 @@ def delete_project_role(project, request):
request.session.flash("Successfully removed role", queue="success")

return HTTPSeeOther(
request.route_path('manage.project.roles', name=project.name)
request.route_path('manage.project.roles', project_name=project.name)
)
30 changes: 22 additions & 8 deletions warehouse/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,30 +110,44 @@ def includeme(config):
config.add_route("manage.projects", "/manage/projects/", domain=warehouse)
config.add_route(
"manage.project.settings",
"/manage/project/{name}/settings/",
"/manage/project/{project_name}/settings/",
factory="warehouse.packaging.models:ProjectFactory",
traverse="/{name}",
traverse="/{project_name}",
domain=warehouse,
)
config.add_route(
"manage.project.delete_project",
"/manage/project/{project_name}/delete_project/",
factory="warehouse.packaging.models:ProjectFactory",
traverse="/{project_name}",
domain=warehouse,
)
config.add_route(
"manage.project.releases",
"/manage/project/{project_name}/releases/",
factory="warehouse.packaging.models:ProjectFactory",
traverse="/{project_name}",
domain=warehouse,
)
config.add_route(
"manage.project.roles",
"/manage/project/{name}/collaboration/",
"/manage/project/{project_name}/collaboration/",
factory="warehouse.packaging.models:ProjectFactory",
traverse="/{name}",
traverse="/{project_name}",
domain=warehouse,
)
config.add_route(
"manage.project.change_role",
"/manage/project/{name}/collaboration/change/",
"/manage/project/{project_name}/collaboration/change/",
factory="warehouse.packaging.models:ProjectFactory",
traverse="/{name}",
traverse="/{project_name}",
domain=warehouse,
)
config.add_route(
"manage.project.delete_role",
"/manage/project/{name}/collaboration/delete/",
"/manage/project/{project_name}/collaboration/delete/",
factory="warehouse.packaging.models:ProjectFactory",
traverse="/{name}",
traverse="/{project_name}",
domain=warehouse,
)

Expand Down
Loading

0 comments on commit 0c46c65

Please sign in to comment.