mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 19:01:39 -04:00
Directories can be deleted in /api/files/delete.
This commit is contained in:
parent
3256c89aff
commit
3aa7b47d61
4 changed files with 39 additions and 65 deletions
|
|
@ -1,5 +1,4 @@
|
|||
import pathlib
|
||||
import shutil
|
||||
from http import HTTPStatus
|
||||
from multiprocessing import Manager
|
||||
from typing import List
|
||||
|
|
@ -48,7 +47,8 @@ async def get_file(_: Request, body: schema.FileRequest):
|
|||
|
||||
@bp.post('/delete')
|
||||
@openapi.definition(
|
||||
summary='Delete a single file. Returns an error if WROL Mode is enabled.',
|
||||
summary='Delete files or directories. Directories are deleted recursively.'
|
||||
' Returns an error if WROL Mode is enabled.',
|
||||
body=schema.DeleteRequest,
|
||||
)
|
||||
@validate(schema.DeleteRequest)
|
||||
|
|
@ -56,7 +56,7 @@ async def delete_file(_: Request, body: schema.DeleteRequest):
|
|||
paths = [i for i in body.paths if i]
|
||||
if not paths:
|
||||
raise InvalidFile('paths cannot be empty')
|
||||
lib.delete(*paths)
|
||||
await lib.delete(*paths)
|
||||
return response.empty()
|
||||
|
||||
|
||||
|
|
@ -251,28 +251,6 @@ async def post_rename(_: Request, body: schema.Rename):
|
|||
return response.empty(HTTPStatus.NO_CONTENT)
|
||||
|
||||
|
||||
@bp.post('/delete_directory')
|
||||
@openapi.definition(
|
||||
summary='Delete a directory in the media directory.',
|
||||
body=schema.Directory,
|
||||
)
|
||||
@validate(schema.Directory)
|
||||
@wrol_mode_check
|
||||
async def delete_directory(_: Request, body: schema.Directory):
|
||||
path = get_media_directory() / body.path
|
||||
if path.is_dir():
|
||||
lib.delete_directory(path, recursive=True)
|
||||
else:
|
||||
raise FileNotFoundError(f'No directory found: {path}')
|
||||
|
||||
if PYTEST:
|
||||
await lib.refresh_files([path, ])
|
||||
else:
|
||||
background_task(lib.refresh_files([path, ]))
|
||||
|
||||
return response.empty()
|
||||
|
||||
|
||||
@bp.post('/tag')
|
||||
@validate(schema.TagFileGroupPost)
|
||||
async def post_tag_file_group(_, body: schema.TagFileGroupPost):
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ def list_directories_contents(directories_: List[str]) -> Dict:
|
|||
|
||||
|
||||
@wrol_mode_check
|
||||
def delete(*paths: Union[str, pathlib.Path]):
|
||||
async def delete(*paths: Union[str, pathlib.Path]):
|
||||
"""
|
||||
Delete a file or directory in the media directory.
|
||||
|
||||
|
|
@ -166,10 +166,16 @@ def delete(*paths: Union[str, pathlib.Path]):
|
|||
raise FileGroupIsTagged(f"Cannot delete {file_group} because it is tagged")
|
||||
for path in paths:
|
||||
if path.is_dir():
|
||||
delete_directory(path)
|
||||
delete_directory(path, recursive=True)
|
||||
else:
|
||||
path.unlink()
|
||||
|
||||
coro = refresh_files(paths)
|
||||
if PYTEST:
|
||||
await coro
|
||||
else:
|
||||
background_task(coro)
|
||||
|
||||
|
||||
FILE_NAME_REGEX = re.compile(r'[_ .]')
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ from unittest import mock
|
|||
|
||||
import pytest
|
||||
|
||||
from wrolpi.errors import WROLModeEnabled
|
||||
from wrolpi.files import lib
|
||||
from wrolpi.files.models import FileGroup
|
||||
from wrolpi.tags import TagFile
|
||||
|
|
@ -113,7 +112,7 @@ def test_list_files_api(test_session, test_client, make_files_structure, test_di
|
|||
|
||||
|
||||
def test_delete_file(test_session, test_client, make_files_structure, test_directory):
|
||||
files = ['bar.txt', 'baz/']
|
||||
files = ['bar.txt', 'baz/', 'foo']
|
||||
make_files_structure(files)
|
||||
|
||||
# Delete a file.
|
||||
|
|
@ -503,8 +502,8 @@ async def test_directory_crud(test_session, test_async_client, test_directory, a
|
|||
|
||||
# Deletion is recursive.
|
||||
assert (test_directory / 'foo/baz/asdf.txt').is_file()
|
||||
content = dict(path='foo/baz')
|
||||
request, response = await test_async_client.post('/api/files/delete_directory', content=json.dumps(content))
|
||||
content = dict(paths=['foo/baz', ])
|
||||
request, response = await test_async_client.post('/api/files/delete', content=json.dumps(content))
|
||||
assert response.status_code == HTTPStatus.NO_CONTENT
|
||||
assert not (test_directory / 'foo/baz').exists()
|
||||
assert not (test_directory / 'foo/baz/asdf.txt').exists()
|
||||
|
|
@ -573,27 +572,16 @@ async def test_rename_directory(test_session, test_directory, make_files_structu
|
|||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_directory_tagged(test_session, test_directory, make_files_structure, test_async_client,
|
||||
tag_factory, insert_file_group):
|
||||
"""Cannot delete a directory if it contained a tagged FileGroup."""
|
||||
bar, = make_files_structure({
|
||||
'foo/bar.txt': 'bar'
|
||||
}, file_groups=True, session=test_session)
|
||||
bar: FileGroup
|
||||
tag = tag_factory()
|
||||
bar.add_tag(tag)
|
||||
test_session.commit()
|
||||
async def test_delete_directory_recursive(test_session, test_directory, make_files_structure, test_async_client,
|
||||
assert_files):
|
||||
make_files_structure(['dir/foo', 'dir/bar', 'empty'])
|
||||
await lib.refresh_files()
|
||||
assert test_session.query(FileGroup).count() == 3
|
||||
|
||||
# Cannot deleted tagged file.
|
||||
content = dict(path=str(bar.primary_path.parent))
|
||||
request, response = await test_async_client.post('/api/files/delete_directory', content=json.dumps(content))
|
||||
assert response.status_code == HTTPStatus.CONFLICT
|
||||
assert (test_directory / 'foo').is_dir()
|
||||
|
||||
# Remove tag, directory is deleted recursively.
|
||||
bar.remove_tag(tag)
|
||||
test_session.commit()
|
||||
content = dict(path=str(bar.primary_path.parent))
|
||||
request, response = await test_async_client.post('/api/files/delete_directory', content=json.dumps(content))
|
||||
content = {'paths': ['dir/', ]}
|
||||
request, response = await test_async_client.post('/api/files/delete', content=json.dumps(content))
|
||||
assert response.status_code == HTTPStatus.NO_CONTENT
|
||||
assert not (test_directory / 'foo').exists()
|
||||
assert test_session.query(FileGroup).count() == 1
|
||||
|
||||
empty = test_session.query(FileGroup).one()
|
||||
assert empty.primary_path.name == 'empty'
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ from wrolpi.tags import TagFile
|
|||
from wrolpi.vars import PROJECT_DIR
|
||||
|
||||
|
||||
def test_delete(test_session, make_files_structure, test_directory):
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete(test_session, make_files_structure, test_directory):
|
||||
"""
|
||||
File in the media directory can be deleted.
|
||||
"""
|
||||
|
|
@ -31,34 +32,35 @@ def test_delete(test_session, make_files_structure, test_directory):
|
|||
'baz/',
|
||||
])
|
||||
|
||||
lib.delete('bar.txt')
|
||||
await lib.delete('bar.txt')
|
||||
assert (test_directory / 'archives/foo.txt').is_file()
|
||||
assert not (test_directory / 'bar.txt').exists()
|
||||
assert (test_directory / 'baz').is_dir()
|
||||
|
||||
lib.delete('archives/foo.txt')
|
||||
await lib.delete('archives/foo.txt')
|
||||
assert not (test_directory / 'archives/foo.txt').exists()
|
||||
assert not (test_directory / 'bar.txt').exists()
|
||||
|
||||
# Can also delete directories.
|
||||
lib.delete('baz')
|
||||
await lib.delete('baz')
|
||||
assert not (test_directory / 'baz').exists()
|
||||
|
||||
with pytest.raises(InvalidFile):
|
||||
lib.delete('does not exist')
|
||||
await lib.delete('does not exist')
|
||||
|
||||
# Cannot delete the media directory.
|
||||
with pytest.raises(InvalidFile):
|
||||
lib.delete('.')
|
||||
await lib.delete('.')
|
||||
|
||||
|
||||
def test_delete_link(test_session, test_directory):
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_link(test_session, test_directory):
|
||||
"""Links can be deleted."""
|
||||
foo, bar = test_directory / 'foo', test_directory / 'bar'
|
||||
foo.touch()
|
||||
bar.symlink_to(foo)
|
||||
|
||||
lib.delete(bar)
|
||||
await lib.delete(bar)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -74,11 +76,11 @@ async def test_delete_tagged(test_session, make_files_structure, tag_factory, vi
|
|||
|
||||
# Neither file can be deleted.
|
||||
with pytest.raises(FileGroupIsTagged):
|
||||
lib.delete('foo/bar.txt')
|
||||
await lib.delete('foo/bar.txt')
|
||||
with pytest.raises(FileGroupIsTagged):
|
||||
lib.delete('foo/bar.mp4')
|
||||
await lib.delete('foo/bar.mp4')
|
||||
with pytest.raises(FileGroupIsTagged):
|
||||
lib.delete('foo')
|
||||
await lib.delete('foo')
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -87,7 +89,7 @@ async def test_delete_nested(test_session, make_files_structure):
|
|||
make_files_structure(['foo/bar'])
|
||||
|
||||
with pytest.raises(InvalidFile):
|
||||
lib.delete('foo', 'foo/bar')
|
||||
await lib.delete('foo', 'foo/bar')
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue