Skip to content

Commit

Permalink
feat: Provide methods to delete documents and collections (#94)
Browse files Browse the repository at this point in the history
* Provide methods to delete documents and collections

Chunks and extracts are managed by the system, so they don't need
explicit deletion endpoints - they're deleted whenever the document that
produced them is deleted.

* Comments & refactor

* Use cascade delete rather than doing it explicitly

* Comments and formatting
  • Loading branch information
kerinin authored Feb 22, 2024
1 parent 8db2e71 commit b5e4393
Show file tree
Hide file tree
Showing 10 changed files with 596 additions and 46 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,11 @@ If you're in a `poetry shell`, you can omit the `poetry run`:
* Formatting: `poetry run ruff format`
* Type Checking: `poetry run mypy dewy`
To regenerate the OpenAPI spec and client libraries:
```sh
poetry poe extract-openapi
poetry poe update-client
```
<p align="right">(<a href="#readme-top">back to top</a>)</p
Expand Down
163 changes: 163 additions & 0 deletions dewy-client/dewy_client/api/kb/delete_collection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
from http import HTTPStatus
from typing import Any, Dict, Optional, Union

import httpx

from ... import errors
from ...client import AuthenticatedClient, Client
from ...models.collection import Collection
from ...models.http_validation_error import HTTPValidationError
from ...types import Response


def _get_kwargs(
name: str,
) -> Dict[str, Any]:
_kwargs: Dict[str, Any] = {
"method": "delete",
"url": f"/api/collections/{name}",
}

return _kwargs


def _parse_response(
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
) -> Optional[Union[Collection, HTTPValidationError]]:
if response.status_code == HTTPStatus.OK:
response_200 = Collection.from_dict(response.json())

return response_200
if response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY:
response_422 = HTTPValidationError.from_dict(response.json())

return response_422
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
else:
return None


def _build_response(
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
) -> Response[Union[Collection, HTTPValidationError]]:
return Response(
status_code=HTTPStatus(response.status_code),
content=response.content,
headers=response.headers,
parsed=_parse_response(client=client, response=response),
)


def sync_detailed(
name: str,
*,
client: Union[AuthenticatedClient, Client],
) -> Response[Union[Collection, HTTPValidationError]]:
"""Delete Collection
Delete a collection and all documents contained within it.
Args:
name (str): The collection name.
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[Union[Collection, HTTPValidationError]]
"""

kwargs = _get_kwargs(
name=name,
)

response = client.get_httpx_client().request(
**kwargs,
)

return _build_response(client=client, response=response)


def sync(
name: str,
*,
client: Union[AuthenticatedClient, Client],
) -> Optional[Union[Collection, HTTPValidationError]]:
"""Delete Collection
Delete a collection and all documents contained within it.
Args:
name (str): The collection name.
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Union[Collection, HTTPValidationError]
"""

return sync_detailed(
name=name,
client=client,
).parsed


async def asyncio_detailed(
name: str,
*,
client: Union[AuthenticatedClient, Client],
) -> Response[Union[Collection, HTTPValidationError]]:
"""Delete Collection
Delete a collection and all documents contained within it.
Args:
name (str): The collection name.
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[Union[Collection, HTTPValidationError]]
"""

kwargs = _get_kwargs(
name=name,
)

response = await client.get_async_httpx_client().request(**kwargs)

return _build_response(client=client, response=response)


async def asyncio(
name: str,
*,
client: Union[AuthenticatedClient, Client],
) -> Optional[Union[Collection, HTTPValidationError]]:
"""Delete Collection
Delete a collection and all documents contained within it.
Args:
name (str): The collection name.
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Union[Collection, HTTPValidationError]
"""

return (
await asyncio_detailed(
name=name,
client=client,
)
).parsed
163 changes: 163 additions & 0 deletions dewy-client/dewy_client/api/kb/delete_document.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
from http import HTTPStatus
from typing import Any, Dict, Optional, Union

import httpx

from ... import errors
from ...client import AuthenticatedClient, Client
from ...models.document import Document
from ...models.http_validation_error import HTTPValidationError
from ...types import Response


def _get_kwargs(
id: int,
) -> Dict[str, Any]:
_kwargs: Dict[str, Any] = {
"method": "delete",
"url": f"/api/documents/{id}",
}

return _kwargs


def _parse_response(
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
) -> Optional[Union[Document, HTTPValidationError]]:
if response.status_code == HTTPStatus.OK:
response_200 = Document.from_dict(response.json())

return response_200
if response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY:
response_422 = HTTPValidationError.from_dict(response.json())

return response_422
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
else:
return None


def _build_response(
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
) -> Response[Union[Document, HTTPValidationError]]:
return Response(
status_code=HTTPStatus(response.status_code),
content=response.content,
headers=response.headers,
parsed=_parse_response(client=client, response=response),
)


def sync_detailed(
id: int,
*,
client: Union[AuthenticatedClient, Client],
) -> Response[Union[Document, HTTPValidationError]]:
"""Delete Document
Delete a document.
Args:
id (int): The document ID.
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[Union[Document, HTTPValidationError]]
"""

kwargs = _get_kwargs(
id=id,
)

response = client.get_httpx_client().request(
**kwargs,
)

return _build_response(client=client, response=response)


def sync(
id: int,
*,
client: Union[AuthenticatedClient, Client],
) -> Optional[Union[Document, HTTPValidationError]]:
"""Delete Document
Delete a document.
Args:
id (int): The document ID.
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Union[Document, HTTPValidationError]
"""

return sync_detailed(
id=id,
client=client,
).parsed


async def asyncio_detailed(
id: int,
*,
client: Union[AuthenticatedClient, Client],
) -> Response[Union[Document, HTTPValidationError]]:
"""Delete Document
Delete a document.
Args:
id (int): The document ID.
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[Union[Document, HTTPValidationError]]
"""

kwargs = _get_kwargs(
id=id,
)

response = await client.get_async_httpx_client().request(**kwargs)

return _build_response(client=client, response=response)


async def asyncio(
id: int,
*,
client: Union[AuthenticatedClient, Client],
) -> Optional[Union[Document, HTTPValidationError]]:
"""Delete Document
Delete a document.
Args:
id (int): The document ID.
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Union[Document, HTTPValidationError]
"""

return (
await asyncio_detailed(
id=id,
client=client,
)
).parsed
21 changes: 20 additions & 1 deletion dewy/collection/router.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Annotated, List

from fastapi import APIRouter, HTTPException, Path, status
from fastapi import APIRouter, HTTPException, Path, Response, status
from loguru import logger

from dewy.collection.models import Collection, CollectionCreate
Expand Down Expand Up @@ -83,3 +83,22 @@ async def get_collection(name: PathCollection, conn: PgConnectionDep) -> Collect
)

return Collection.model_validate(dict(result))


@router.delete("/{name}")
async def delete_collection(conn: PgConnectionDep, name: PathCollection) -> Collection:
"""Delete a collection and all documents contained within it."""
id = await conn.fetchval(
"""
DELETE from collection
WHERE name = $1
RETURNING id
""",
name,
)
if not id:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail=f"No collection named '{name}'"
)

return Response(status_code=status.HTTP_204_NO_CONTENT)
Loading

0 comments on commit b5e4393

Please sign in to comment.