move folders and templates to other folders

new endpoints:
/services/<service_id>/move-to-folder
/services/<service_id>/move-to-folder/<target_template_folder_id>

* takes in a dict containing lists of `templates` and `folders` uuids.
* sets parent of templates and folders to the folder specified in the
  URL. Or None, if there was no id specified.
* if any template or folder has a differen service id, then the whole
  update fails
* if any folder is an ancestor of the target folder, then the whole
  update fails (as that would cause a cyclical folder structure).
* the whole function is wrapped in a single `transactional` decorator,
  so in case of error nothing will be saved.
This commit is contained in:
Leo Hemsted
2018-11-08 16:44:57 +00:00
parent e4476b9869
commit d9aa220aa6
6 changed files with 257 additions and 8 deletions

View File

@@ -18,7 +18,7 @@ from app.dao.service_inbound_api_dao import save_service_inbound_api
from app.dao.service_permissions_dao import dao_add_service_permission
from app.dao.service_sms_sender_dao import update_existing_sms_sender_with_inbound_number, dao_update_service_sms_sender
from app.dao.services_dao import dao_create_service
from app.dao.templates_dao import dao_create_template
from app.dao.templates_dao import dao_create_template, dao_update_template
from app.dao.users_dao import save_model_user
from app.models import (
ApiKey,
@@ -137,7 +137,9 @@ def create_template(
subject='Template subject',
content='Dear Sir/Madam, Hello. Yours Truly, The Government.',
reply_to=None,
hidden=False
hidden=False,
archived=False,
folder=None,
):
data = {
'name': template_name or '{} Template Name'.format(template_type),
@@ -146,12 +148,18 @@ def create_template(
'service': service,
'created_by': service.created_by,
'reply_to': reply_to,
'hidden': hidden
'hidden': hidden,
'folder': folder,
}
if template_type != SMS_TYPE:
data['subject'] = subject
template = Template(**data)
dao_create_template(template)
if archived:
template.archived = archived
dao_update_template(template)
return template

View File

@@ -4,7 +4,7 @@ import pytest
from app.models import TemplateFolder
from tests.app.db import create_service, create_template_folder
from tests.app.db import create_service, create_template_folder, create_template
def test_get_folders_for_service(admin_request, notify_db_session):
@@ -188,3 +188,157 @@ def test_delete_template_folder_fails_if_folder_contains_templates(admin_request
}
assert TemplateFolder.query.count() == 1
@pytest.mark.parametrize('data', [
{},
{'templates': None, 'folders': []},
{'folders': []},
{'templates': [], 'folders': [None]},
{'templates': [], 'folders': ['not a uuid']},
])
def test_move_to_folder_validates_schema(data, admin_request, notify_db_session):
admin_request.post(
'template_folder.move_to_template_folder',
service_id=uuid.uuid4(),
target_template_folder_id=uuid.uuid4(),
_data=data,
_expected_status=400
)
def test_move_to_folder_moves_folders_and_templates(admin_request, sample_service):
target_folder = create_template_folder(sample_service, name='target')
f1 = create_template_folder(sample_service, name='f1')
f2 = create_template_folder(sample_service, name='f2')
t1 = create_template(sample_service, template_name='t1', folder=f1)
t2 = create_template(sample_service, template_name='t2', folder=f1)
t3 = create_template(sample_service, template_name='t3', folder=f2)
t4 = create_template(sample_service, template_name='t4', folder=target_folder)
admin_request.post(
'template_folder.move_to_template_folder',
service_id=sample_service.id,
target_template_folder_id=target_folder.id,
_data={
'templates': [str(t1.id)],
'folders': [str(f1.id)]
}
)
assert target_folder.parent is None
assert f1.parent == target_folder
assert f2.parent is None # unchanged
assert t1.folder == target_folder # moved out of f1, even though f1 is also being moved
assert t2.folder == f1 # stays in f1, though f1 has moved
assert t3.folder == f2 # unchanged
assert t4.folder == target_folder # unchanged
# versions are all unchanged
assert t1.version == 1
assert t2.version == 1
assert t3.version == 1
assert t4.version == 1
def test_move_to_folder_moves_folders_and_templates_to_top_level_if_no_target(admin_request, sample_service):
f1 = create_template_folder(sample_service, name='f1')
f2 = create_template_folder(sample_service, name='f2')
t1 = create_template(sample_service, template_name='t1', folder=f1)
t2 = create_template(sample_service, template_name='t2', folder=f1)
t3 = create_template(sample_service, template_name='t3', folder=f2)
admin_request.post(
'template_folder.move_to_template_folder',
service_id=sample_service.id,
target_template_folder_id=None,
_data={
'templates': [str(t1.id)],
'folders': [str(f1.id)]
}
)
assert f1.parent is None
assert f2.parent is None # unchanged
assert t1.folder is None # moved out of f1, even though f1 is also being moved
assert t2.folder == f1 # stays in f1, though f1 has moved
assert t3.folder == f2 # unchanged
def test_move_to_folder_rejects_folder_from_other_service(admin_request, notify_db_session):
s1 = create_service(service_name='s1')
s2 = create_service(service_name='s2')
f2 = create_template_folder(s2)
admin_request.post(
'template_folder.move_to_template_folder',
service_id=s1.id,
target_template_folder_id=None,
_data={
'templates': [],
'folders': [str(f2.id)]
},
_expected_status=400
)
def test_move_to_folder_rejects_template_from_other_service(admin_request, notify_db_session):
s1 = create_service(service_name='s1')
s2 = create_service(service_name='s2')
t2 = create_template(s2)
admin_request.post(
'template_folder.move_to_template_folder',
service_id=s1.id,
target_template_folder_id=None,
_data={
'templates': [],
'folders': [str(t2.id)]
},
_expected_status=400
)
def test_move_to_folder_rejects_if_it_would_cause_folder_loop(admin_request, sample_service):
f1 = create_template_folder(sample_service, name='f1')
target_folder = create_template_folder(sample_service, name='target', parent=f1)
admin_request.post(
'template_folder.move_to_template_folder',
service_id=sample_service.id,
target_template_folder_id=target_folder.id,
_data={
'folders': [str(f1.id)]
},
_expected_status=400
)
def test_move_to_folder_skips_archived_templates(admin_request, sample_service):
target_folder = create_template_folder(sample_service)
other_folder = create_template_folder(sample_service)
archived_template = create_template(sample_service, archived=True, folder=None)
unarchived_template = create_template(sample_service, archived=False, folder=other_folder)
archived_timestamp = archived_template.updated_at
admin_request.post(
'template_folder.move_to_template_folder',
service_id=sample_service.id,
target_template_folder_id=target_folder.id,
_data={
'templates': [str(archived_template.id), str(unarchived_template.id)],
'folders': []
}
)
assert archived_template.updated_at == archived_timestamp
assert archived_template.folder is None
assert unarchived_template.folder == target_folder

View File

@@ -28,7 +28,8 @@ from tests.app.db import (
create_inbound_number,
create_reply_to_email,
create_letter_contact,
create_template
create_template,
create_template_folder
)
@@ -329,3 +330,17 @@ def test_is_precompiled_letter_hidden_true_not_name(sample_letter_template):
def test_is_precompiled_letter_name_correct_not_hidden(sample_letter_template):
sample_letter_template.name = PRECOMPILED_TEMPLATE_NAME
assert not sample_letter_template.is_precompiled_letter
def test_template_folder_is_parent(sample_service):
x = None
folders = []
for i in range(5):
x = create_template_folder(sample_service, name=str(i), parent=x)
folders.append(x)
assert folders[0].is_parent_of(folders[1])
assert folders[0].is_parent_of(folders[2])
assert folders[0].is_parent_of(folders[4])
assert folders[1].is_parent_of(folders[2])
assert not folders[1].is_parent_of(folders[0])