Skip to content

Commit

Permalink
task/DES-abc: Move project asset directory deletion to celery task (#118
Browse files Browse the repository at this point in the history
)

* Move project assset directory deletion to celery task

* Add mock

* Add remove_project_assets to __init__.py
  • Loading branch information
nathanfranklin authored Jan 23, 2023
1 parent 34d028e commit db37557
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 9 deletions.
10 changes: 3 additions & 7 deletions geoapi/services/projects.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import shutil
from pathlib import Path
from typing import List, Optional

Expand All @@ -9,9 +8,9 @@
from sqlalchemy.exc import IntegrityError
from geoapi.services.users import UserService
from geoapi.utils.agave import AgaveUtils, get_system_users
from geoapi.utils.assets import get_project_asset_dir
from geoapi.utils.users import is_anonymous
from geoapi.tasks.external_data import import_from_agave
from geoapi.tasks.projects import remove_project_assets
from geoapi.log import logger
from geoapi.exceptions import ApiException, ObservableProjectAlreadyExists

Expand Down Expand Up @@ -274,11 +273,8 @@ def delete(user: User, projectId: int) -> dict:
db_session.query(Project).filter(Project.id == projectId).delete()
db_session.commit()

assets_folder = get_project_asset_dir(projectId)
try:
shutil.rmtree(assets_folder)
except FileNotFoundError:
pass
remove_project_assets.apply_async(args=[projectId])

return {"status": "ok"}

@staticmethod
Expand Down
1 change: 1 addition & 0 deletions geoapi/tasks/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from geoapi.tasks.lidar import convert_to_potree
from geoapi.tasks.external_data import import_file_from_agave, import_from_agave, refresh_observable_projects
from geoapi.tasks.streetview import publish, from_tapis_to_streetview, process_streetview_sequences
from geoapi.tasks.projects import remove_project_assets
22 changes: 22 additions & 0 deletions geoapi/tasks/projects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from geoapi.celery_app import app
from geoapi.utils.assets import get_project_asset_dir
from geoapi.log import logger
import shutil


@app.task()
def remove_project_assets(project_id):
"""
Remove all assets associated with a project.
The directory containing that project's assets will be deleted.
"""
logger.info(f"Deleting project:{project_id} started")
assets_folder = get_project_asset_dir(project_id)
try:
shutil.rmtree(assets_folder)
logger.info(f"Deleting project:{project_id} finished")
except FileNotFoundError:
logger.info(f"Deleting project:{project_id} completed but caught FileNotFoundError")
pass
15 changes: 14 additions & 1 deletion geoapi/tests/api_tests/test_projects_routes.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import datetime
import uuid
import os
from geoapi.db import db_session
from geoapi.models.users import User
from geoapi.models.project import Project, ProjectUser
from geoapi.utils.assets import get_project_asset_dir


def test_get_projects(test_client, projects_fixture, user1):
Expand Down Expand Up @@ -119,7 +121,7 @@ def test_project_data_allow_public_access(test_client, public_projects_fixture,
assert resp.status_code == 200


def test_delete_empty_project(test_client, projects_fixture, user1):
def test_delete_empty_project(test_client, projects_fixture, remove_project_assets_mock, user1):
resp = test_client.delete(f'/projects/{projects_fixture.id}/', headers={'x-jwt-assertion-test': user1.jwt})
assert resp.status_code == 200
projects = db_session.query(Project).all()
Expand All @@ -128,6 +130,17 @@ def test_delete_empty_project(test_client, projects_fixture, user1):
assert projectUsers == []


def test_delete_project(test_client, projects_fixture, remove_project_assets_mock, image_feature_fixture, user1):
assert os.path.isdir(get_project_asset_dir(projects_fixture.id))
resp = test_client.delete(f'/projects/{projects_fixture.id}/', headers={'x-jwt-assertion-test': user1.jwt})
assert resp.status_code == 200
projects = db_session.query(Project).all()
projectUsers = db_session.query(ProjectUser).all()
assert projects == []
assert projectUsers == []
assert not os.path.isdir(get_project_asset_dir(projects_fixture.id))


def test_delete_unauthorized(test_client, projects_fixture, user2):
resp = test_client.delete(f'/projects/{projects_fixture.id}/', headers={'x-jwt-assertion-test': user2.jwt})
assert resp.status_code == 403
Expand Down
2 changes: 1 addition & 1 deletion geoapi/tests/api_tests/test_projects_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def test_create_project():
assert proj.description == "test description"


def test_delete_project(projects_fixture, user1):
def test_delete_project(projects_fixture, remove_project_assets_mock, user1):
ProjectsService.delete(user1, projects_fixture.id)
projects = db_session.query(Project).all()
assert projects == []
Expand Down
14 changes: 14 additions & 0 deletions geoapi/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,20 @@ def get_system_users_mock(userdata):
yield get_system_users


@pytest.fixture(scope="function")
def remove_project_assets_mock():
# we mock method so that we execute it synchronously (and not as a celery task on worker)
# when testing some routes
with patch('geoapi.services.projects.remove_project_assets') as mock_remove_project:
from geoapi.tasks.projects import remove_project_assets

def remove(args):
remove_project_assets(project_id=args[0])

mock_remove_project.apply_async.side_effect = remove
yield mock_remove_project


@pytest.fixture(scope="function")
def tile_server_ini_file_fixture():
home = os.path.dirname(__file__)
Expand Down

0 comments on commit db37557

Please sign in to comment.