Skip to content

Commit 6e3e364

Browse files
committed
Adding delete and has, plus updating get method.
1 parent cfeffe4 commit 6e3e364

File tree

3 files changed

+288
-11
lines changed

3 files changed

+288
-11
lines changed

arangoasync/collection.py

Lines changed: 178 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
from typing import Generic, List, Optional, Tuple, TypeVar, cast
55

66
from arangoasync.errno import (
7+
DOCUMENT_NOT_FOUND,
78
HTTP_BAD_PARAMETER,
89
HTTP_NOT_FOUND,
910
HTTP_PRECONDITION_FAILED,
1011
)
1112
from arangoasync.exceptions import (
1213
CollectionPropertiesError,
14+
DocumentDeleteError,
1315
DocumentGetError,
1416
DocumentInsertError,
1517
DocumentParseError,
@@ -33,6 +35,7 @@
3335
Json,
3436
Jsons,
3537
Params,
38+
RequestHeaders,
3639
)
3740

3841
T = TypeVar("T")
@@ -392,23 +395,23 @@ def response_handler(resp: Response) -> CollectionProperties:
392395
async def get(
393396
self,
394397
document: str | Json,
395-
rev: Optional[str] = None,
396-
check_rev: bool = True,
397398
allow_dirty_read: bool = False,
399+
if_match: Optional[str] = None,
400+
if_none_match: Optional[str] = None,
398401
) -> Result[Optional[U]]:
399402
"""Return a document.
400403
401404
Args:
402405
document (str | dict): Document ID, key or body.
403-
Document body must contain the "_id" or "_key" field.
404-
rev (str | None): Expected document revision. Overrides the
405-
value of "_rev" field in **document** if present.
406-
check_rev (bool): If set to True, revision of **document** (if given)
407-
is compared against the revision of target document.
406+
Document body must contain the "_id" or "_key" field.
408407
allow_dirty_read (bool): Allow reads from followers in a cluster.
408+
if_match (str | None): The document is returned, if it has the same
409+
revision as the given ETag.
410+
if_none_match (str | None): The document is returned, if it has a
411+
different revision than the given ETag.
409412
410413
Returns:
411-
Document or None if not found.
414+
Document or `None` if not found.
412415
413416
Raises:
414417
DocumentRevisionError: If the revision is incorrect.
@@ -417,10 +420,15 @@ async def get(
417420
References:
418421
- `get-a-document <https://docs.arangodb.com/stable/develop/http-api/documents/#get-a-document>`__
419422
""" # noqa: E501
420-
handle, headers = self._prep_from_doc(document, rev, check_rev)
423+
handle, _ = self._prep_from_doc(document)
421424

425+
headers: RequestHeaders = {}
422426
if allow_dirty_read:
423427
headers["x-arango-allow-dirty-read"] = "true"
428+
if if_match is not None:
429+
headers["If-Match"] = if_match
430+
if if_none_match is not None:
431+
headers["If-None-Match"] = if_none_match
424432

425433
request = Request(
426434
method=Method.GET,
@@ -432,7 +440,66 @@ def response_handler(resp: Response) -> Optional[U]:
432440
if resp.is_success:
433441
return self._doc_deserializer.loads(resp.raw_body)
434442
elif resp.status_code == HTTP_NOT_FOUND:
435-
return None
443+
if resp.error_code == DOCUMENT_NOT_FOUND:
444+
return None
445+
else:
446+
raise DocumentGetError(resp, request)
447+
elif resp.status_code == HTTP_PRECONDITION_FAILED:
448+
raise DocumentRevisionError(resp, request)
449+
else:
450+
raise DocumentGetError(resp, request)
451+
452+
return await self._executor.execute(request, response_handler)
453+
454+
async def has(
455+
self,
456+
document: str | Json,
457+
allow_dirty_read: bool = False,
458+
if_match: Optional[str] = None,
459+
if_none_match: Optional[str] = None,
460+
) -> Result[bool]:
461+
"""Check if a document exists in the collection.
462+
463+
Args:
464+
document (str | dict): Document ID, key or body.
465+
Document body must contain the "_id" or "_key" field.
466+
allow_dirty_read (bool): Allow reads from followers in a cluster.
467+
if_match (str | None): The document is returned, if it has the same
468+
revision as the given ETag.
469+
if_none_match (str | None): The document is returned, if it has a
470+
different revision than the given ETag.
471+
472+
Returns:
473+
`True` if the document exists, `False` otherwise.
474+
475+
Raises:
476+
DocumentRevisionError: If the revision is incorrect.
477+
DocumentGetError: If retrieval fails.
478+
479+
References:
480+
- `get-a-document-header <https://docs.arangodb.com/stable/develop/http-api/documents/#get-a-document-header>`__
481+
""" # noqa: E501
482+
handle, _ = self._prep_from_doc(document)
483+
484+
headers: RequestHeaders = {}
485+
if allow_dirty_read:
486+
headers["x-arango-allow-dirty-read"] = "true"
487+
if if_match is not None:
488+
headers["If-Match"] = if_match
489+
if if_none_match is not None:
490+
headers["If-None-Match"] = if_none_match
491+
492+
request = Request(
493+
method=Method.HEAD,
494+
endpoint=f"/_api/document/{handle}",
495+
headers=headers,
496+
)
497+
498+
def response_handler(resp: Response) -> bool:
499+
if resp.is_success:
500+
return True
501+
elif resp.status_code == HTTP_NOT_FOUND:
502+
return False
436503
elif resp.status_code == HTTP_PRECONDITION_FAILED:
437504
raise DocumentRevisionError(resp, request)
438505
else:
@@ -558,6 +625,7 @@ async def update(
558625
merge_objects: Optional[bool] = None,
559626
refill_index_caches: Optional[bool] = None,
560627
version_attribute: Optional[str] = None,
628+
if_match: Optional[str] = None,
561629
) -> Result[bool | Json]:
562630
"""Insert a new document.
563631
@@ -587,6 +655,8 @@ async def update(
587655
or cache-enabled persistent indexes.
588656
version_attribute (str | None): Support for simple external versioning to
589657
document operations.
658+
if_match (str | None): You can conditionally update a document based on a
659+
target revision id by using the "if-match" HTTP header.
590660
591661
Returns:
592662
bool | dict: Document metadata (e.g. document id, key, revision) or `True`
@@ -619,10 +689,15 @@ async def update(
619689
if version_attribute is not None:
620690
params["versionAttribute"] = version_attribute
621691

692+
headers: RequestHeaders = {}
693+
if if_match is not None:
694+
headers["If-Match"] = if_match
695+
622696
request = Request(
623697
method=Method.PATCH,
624698
endpoint=f"/_api/document/{self._extract_id(cast(Json, document))}",
625699
params=params,
700+
headers=headers,
626701
data=self._doc_serializer.dumps(document),
627702
)
628703

@@ -650,6 +725,7 @@ async def replace(
650725
silent: Optional[bool] = None,
651726
refill_index_caches: Optional[bool] = None,
652727
version_attribute: Optional[str] = None,
728+
if_match: Optional[str] = None,
653729
) -> Result[bool | Json]:
654730
"""Replace a document.
655731
@@ -673,6 +749,8 @@ async def replace(
673749
or cache-enabled persistent indexes.
674750
version_attribute (str | None): Support for simple external versioning to
675751
document operations.
752+
if_match (str | None): You can conditionally replace a document based on a
753+
target revision id by using the "if-match" HTTP header.
676754
677755
Returns:
678756
bool | dict: Document metadata (e.g. document id, key, revision) or `True`
@@ -701,10 +779,15 @@ async def replace(
701779
if version_attribute is not None:
702780
params["versionAttribute"] = version_attribute
703781

782+
headers: RequestHeaders = {}
783+
if if_match is not None:
784+
headers["If-Match"] = if_match
785+
704786
request = Request(
705787
method=Method.PUT,
706788
endpoint=f"/_api/document/{self._extract_id(cast(Json, document))}",
707789
params=params,
790+
headers=headers,
708791
data=self._doc_serializer.dumps(document),
709792
)
710793

@@ -721,3 +804,88 @@ def response_handler(resp: Response) -> bool | Json:
721804
raise DocumentReplaceError(resp, request, msg)
722805

723806
return await self._executor.execute(request, response_handler)
807+
808+
async def delete(
809+
self,
810+
document: T,
811+
ignore_revs: Optional[bool] = None,
812+
ignore_missing: bool = False,
813+
wait_for_sync: Optional[bool] = None,
814+
return_old: Optional[bool] = None,
815+
silent: Optional[bool] = None,
816+
refill_index_caches: Optional[bool] = None,
817+
if_match: Optional[str] = None,
818+
) -> Result[bool | Json]:
819+
"""Delete a document.
820+
821+
Args:
822+
document (dict): Document ID, key or body. The body must contain the
823+
"_key" or "_id" field.
824+
ignore_revs (bool | None): If set to `True`, the `_rev` attribute in the
825+
document is ignored. If this is set to `False`, then the `_rev`
826+
attribute given in the body document is taken as a precondition.
827+
The document is only replaced if the current revision is the one
828+
specified.
829+
ignore_missing (bool): Do not raise an exception on missing document.
830+
This parameter has no effect in transactions where an exception is
831+
always raised on failures.
832+
wait_for_sync (bool | None): Wait until operation has been synced to disk.
833+
return_old (bool | None): Additionally return the complete old document
834+
under the attribute `old` in the result.
835+
silent (bool | None): If set to `True`, no document metadata is returned.
836+
This can be used to save resources.
837+
refill_index_caches (bool | None): Whether to add new entries to
838+
in-memory index caches if document updates affect the edge index
839+
or cache-enabled persistent indexes.
840+
if_match (bool | None): You can conditionally remove a document based
841+
on a target revision id by using the "if-match" HTTP header.
842+
843+
Returns:
844+
bool | dict: Document metadata (e.g. document id, key, revision) or `True`
845+
if **silent** is set to `True` and the document was found.
846+
847+
Raises:
848+
DocumentRevisionError: If precondition was violated.
849+
DocumentDeleteError: If deletion fails.
850+
851+
References:
852+
- `remove-a-document <https://docs.arangodb.com/stable/develop/http-api/documents/#remove-a-document>`__
853+
""" # noqa: E501
854+
params: Params = {}
855+
if ignore_revs is not None:
856+
params["ignoreRevs"] = ignore_revs
857+
if wait_for_sync is not None:
858+
params["waitForSync"] = wait_for_sync
859+
if return_old is not None:
860+
params["returnOld"] = return_old
861+
if silent is not None:
862+
params["silent"] = silent
863+
if refill_index_caches is not None:
864+
params["refillIndexCaches"] = refill_index_caches
865+
866+
headers: RequestHeaders = {}
867+
if if_match is not None:
868+
headers["If-Match"] = if_match
869+
870+
request = Request(
871+
method=Method.DELETE,
872+
endpoint=f"/_api/document/{self._extract_id(cast(Json, document))}",
873+
params=params,
874+
headers=headers,
875+
)
876+
877+
def response_handler(resp: Response) -> bool | Json:
878+
if resp.is_success:
879+
if silent is True:
880+
return True
881+
return self._executor.deserialize(resp.raw_body)
882+
msg: Optional[str] = None
883+
if resp.status_code == HTTP_PRECONDITION_FAILED:
884+
raise DocumentRevisionError(resp, request)
885+
elif resp.status_code == HTTP_NOT_FOUND:
886+
if resp.error_code == DOCUMENT_NOT_FOUND and ignore_missing:
887+
return False
888+
msg = "Document, collection or transaction not found."
889+
raise DocumentDeleteError(resp, request, msg)
890+
891+
return await self._executor.execute(request, response_handler)

arangoasync/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,10 @@ class DeserializationError(ArangoClientError):
227227
"""Failed to deserialize the server response."""
228228

229229

230+
class DocumentDeleteError(ArangoServerError):
231+
"""Failed to delete document."""
232+
233+
230234
class DocumentGetError(ArangoServerError):
231235
"""Failed to retrieve document."""
232236

0 commit comments

Comments
 (0)