diff --git a/agave/blueprints/rest_api.py b/agave/blueprints/rest_api.py index 99499876..53572bdb 100644 --- a/agave/blueprints/rest_api.py +++ b/agave/blueprints/rest_api.py @@ -95,10 +95,22 @@ def wrapper_resource_class(cls): """ DELETE /resource/{id} Use "delete" method (if exists) to create the chalice endpoint + This method only validate if exists the object and retrieve it. + The logic to delete / deactivate is completely your responsibility. """ if hasattr(cls, 'delete'): route = self.delete(path + '/{id}') - route(cls.delete) + + @copy_attributes(cls) + def delete(id: str): + try: + model = cls.model.objects.get(id=id) + except DoesNotExist: + raise NotFoundError('Not valid id') + else: + return cls.delete(model) + + route(delete) """ PATCH /resource/{id} Enable PATCH method if Resource.update method exist. It validates @@ -199,6 +211,13 @@ def query(): if self.user_id_filter_required(): query_params.user_id = self.current_user_id filters = cls.get_query_filter(query_params) + if ( + hasattr(query_params, 'active') + and query_params.active is not None + ): + filters &= Q( + deactivated_at__exists=not query_params.active + ) if query_params.count: result = _count(filters) elif hasattr(cls, 'query'): diff --git a/agave/version.py b/agave/version.py index 2fb25139..124e4620 100644 --- a/agave/version.py +++ b/agave/version.py @@ -1 +1 @@ -__version__ = '0.1.6' +__version__ = '0.1.7' diff --git a/examples/chalicelib/resources/accounts.py b/examples/chalicelib/resources/accounts.py index 37c4b2f0..627d4d8f 100644 --- a/examples/chalicelib/resources/accounts.py +++ b/examples/chalicelib/resources/accounts.py @@ -36,12 +36,7 @@ def update( return Response(account.to_dict(), status_code=200) @staticmethod - def delete(id: str) -> Response: - try: - account = AccountModel.objects.get(id=id) - except DoesNotExist: - raise NotFoundError('Not valid id') - + def delete(account: AccountModel) -> Response: account.deactivated_at = dt.datetime.utcnow().replace(microsecond=0) account.save() return Response(account.to_dict(), status_code=200) diff --git a/examples/chalicelib/validators.py b/examples/chalicelib/validators.py index 9aa1c785..80055022 100644 --- a/examples/chalicelib/validators.py +++ b/examples/chalicelib/validators.py @@ -7,7 +7,7 @@ class AccountQuery(QueryParams): name: Optional[str] = None user_id: Optional[str] = None - is_active: Optional[bool] = None + active: Optional[bool] = None class TransactionQuery(QueryParams): diff --git a/tests/blueprint/test_blueprint.py b/tests/blueprint/test_blueprint.py index 947a2ee5..e08f547c 100644 --- a/tests/blueprint/test_blueprint.py +++ b/tests/blueprint/test_blueprint.py @@ -1,3 +1,4 @@ +import datetime as dt from urllib.parse import urlencode import pytest @@ -88,6 +89,11 @@ def test_delete_resource(client: Client, account: Account) -> None: assert account.deactivated_at is not None +def test_delete_resource_not_exists(client: Client) -> None: + resp = client.http.delete('/accounts/1234') + assert resp.status_code == 404 + + @pytest.mark.usefixtures('accounts') def test_query_count_resource(client: Client) -> None: query_params = dict(count=1, name='Frida Kahlo') @@ -120,6 +126,32 @@ def test_query_all_resource(client: Client) -> None: assert len(resp.json_body['items']) == 2 +def test_query_all_filter_active(client: Client, account: Account) -> None: + query_params = dict(active=True) + # Query active items + resp = client.http.get(f'/accounts?{urlencode(query_params)}') + assert resp.status_code == 200 + items = resp.json_body['items'] + assert len(items) == 4 + assert all(item['deactivated_at'] is None for item in items) + + # Deactivate Item + account.deactivated_at = dt.datetime.utcnow() + account.save() + resp = client.http.get(f'/accounts?{urlencode(query_params)}') + assert resp.status_code == 200 + items = resp.json_body['items'] + assert len(items) == 3 + + # Query deactivated items + query_params = dict(active=False) + resp = client.http.get(f'/accounts?{urlencode(query_params)}') + assert resp.status_code == 200 + items = resp.json_body['items'] + assert len(items) == 1 + assert items[0]['deactivated_at'] is not None + + @pytest.mark.usefixtures('accounts') @patch(USER_ID_FILTER_REQUIRED, MagicMock(return_value=True)) def test_query_user_id_filter_required(client: Client) -> None: