Skip to content

Commit d969ce6

Browse files
committed
Adding CRUD for vertex collections
1 parent 0cf44f7 commit d969ce6

File tree

3 files changed

+416
-7
lines changed

3 files changed

+416
-7
lines changed

arangoasync/collection.py

Lines changed: 257 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def get_col_name(doc: str | Json) -> str:
8989
DocumentParseError: If document ID is missing.
9090
"""
9191
try:
92-
doc_id: str = doc["_id"] if isinstance(doc, dict) else doc
92+
doc_id: str = doc if isinstance(doc, str) else doc["_id"]
9393
except KeyError:
9494
raise DocumentParseError('field "_id" required')
9595
else:
@@ -1754,6 +1754,27 @@ def __init__(
17541754
def __repr__(self) -> str:
17551755
return f"<VertexCollection {self.name}>"
17561756

1757+
@staticmethod
1758+
def _parse_result(data: Json) -> Json:
1759+
"""Parse the result from the response.
1760+
1761+
Args:
1762+
data (dict): Response data.
1763+
1764+
Returns:
1765+
dict: Parsed result.
1766+
"""
1767+
result: Json = {}
1768+
if "new" in data or "old" in data:
1769+
result["vertex"] = data["vertex"]
1770+
if "new" in data:
1771+
result["new"] = data["new"]
1772+
if "old" in data:
1773+
result["old"] = data["old"]
1774+
else:
1775+
result = data["vertex"]
1776+
return result
1777+
17571778
@property
17581779
def graph(self) -> str:
17591780
"""Return the graph name.
@@ -1766,6 +1787,7 @@ def graph(self) -> str:
17661787
async def get(
17671788
self,
17681789
vertex: str | Json,
1790+
rev: Optional[str] = None,
17691791
if_match: Optional[str] = None,
17701792
if_none_match: Optional[str] = None,
17711793
) -> Result[Optional[Json]]:
@@ -1774,13 +1796,15 @@ async def get(
17741796
Args:
17751797
vertex (str | dict): Document ID, key or body.
17761798
Document body must contain the "_id" or "_key" field.
1799+
rev (str | None): If this is set a document is only returned if it
1800+
has exactly this revision.
17771801
if_match (str | None): The document is returned, if it has the same
17781802
revision as the given ETag.
17791803
if_none_match (str | None): The document is returned, if it has a
17801804
different revision than the given ETag.
17811805
17821806
Returns:
1783-
Document or `None` if not found.
1807+
dict | None: Document or `None` if not found.
17841808
17851809
Raises:
17861810
DocumentRevisionError: If the revision is incorrect.
@@ -1798,16 +1822,20 @@ async def get(
17981822
if if_none_match is not None:
17991823
headers["If-None-Match"] = if_none_match
18001824

1825+
params: Params = {}
1826+
if rev is not None:
1827+
params["rev"] = rev
1828+
18011829
request = Request(
18021830
method=Method.GET,
18031831
endpoint=f"/_api/gharial/{self._graph}/vertex/{handle}",
18041832
headers=headers,
1833+
params=params,
18051834
)
18061835

18071836
def response_handler(resp: Response) -> Optional[Json]:
18081837
if resp.is_success:
1809-
data: Json = self.deserializer.loads(resp.raw_body)
1810-
return cast(Json, data["vertex"])
1838+
return self._parse_result(self.deserializer.loads(resp.raw_body))
18111839
elif resp.status_code == HTTP_NOT_FOUND:
18121840
if resp.error_code == DOCUMENT_NOT_FOUND:
18131841
return None
@@ -1838,6 +1866,8 @@ async def insert(
18381866
18391867
Returns:
18401868
dict: Document metadata (e.g. document id, key, revision).
1869+
If `return_new` is specified, the result contains the document
1870+
metadata in the "vertex" field and the new document in the "new" field.
18411871
18421872
Raises:
18431873
DocumentInsertError: If insertion fails.
@@ -1864,8 +1894,7 @@ async def insert(
18641894

18651895
def response_handler(resp: Response) -> Json:
18661896
if resp.is_success:
1867-
data: Json = self._executor.deserialize(resp.raw_body)
1868-
return cast(Json, data["vertex"])
1897+
return self._parse_result(self.deserializer.loads(resp.raw_body))
18691898
msg: Optional[str] = None
18701899
if resp.status_code == HTTP_NOT_FOUND:
18711900
msg = (
@@ -1876,6 +1905,228 @@ def response_handler(resp: Response) -> Json:
18761905

18771906
return await self._executor.execute(request, response_handler)
18781907

1908+
async def update(
1909+
self,
1910+
vertex: T,
1911+
wait_for_sync: Optional[bool] = None,
1912+
keep_null: Optional[bool] = None,
1913+
return_new: Optional[bool] = None,
1914+
return_old: Optional[bool] = None,
1915+
if_match: Optional[str] = None,
1916+
) -> Result[Json]:
1917+
"""Insert a new document.
1918+
1919+
Args:
1920+
vertex (dict): Partial or full document with the updated values.
1921+
It must contain the "_key" or "_id" field.
1922+
wait_for_sync (bool | None): Wait until document has been synced to disk.
1923+
keep_null (bool | None): If the intention is to delete existing attributes
1924+
with the patch command, set this parameter to `False`.
1925+
return_new (bool | None): Additionally return the complete new document
1926+
under the attribute `new` in the result.
1927+
return_old (bool | None): Additionally return the complete old document
1928+
under the attribute `old` in the result.
1929+
if_match (str | None): You can conditionally update a document based on a
1930+
target revision id by using the "if-match" HTTP header.
1931+
1932+
Returns:
1933+
dict: Document metadata (e.g. document id, key, revision).
1934+
If `return_new` or "return_old" are specified, the result contains
1935+
the document metadata in the "vertex" field and two additional fields
1936+
("new" and "old").
1937+
1938+
Raises:
1939+
DocumentUpdateError: If update fails.
1940+
1941+
References:
1942+
- `update-a-vertex <https://docs.arangodb.com/stable/develop/http-api/graphs/named-graphs/#update-a-vertex>`__
1943+
""" # noqa: E501
1944+
params: Params = {}
1945+
if wait_for_sync is not None:
1946+
params["waitForSync"] = wait_for_sync
1947+
if keep_null is not None:
1948+
params["keepNull"] = keep_null
1949+
if return_new is not None:
1950+
params["returnNew"] = return_new
1951+
if return_old is not None:
1952+
params["returnOld"] = return_old
1953+
1954+
headers: RequestHeaders = {}
1955+
if if_match is not None:
1956+
headers["If-Match"] = if_match
1957+
1958+
request = Request(
1959+
method=Method.PATCH,
1960+
endpoint=f"/_api/gharial/{self._graph}/vertex/"
1961+
f"{self._prep_from_doc(cast(Json, vertex))}",
1962+
params=params,
1963+
headers=headers,
1964+
data=self._doc_serializer.dumps(vertex),
1965+
)
1966+
1967+
def response_handler(resp: Response) -> Json:
1968+
if resp.is_success:
1969+
return self._parse_result(self.deserializer.loads(resp.raw_body))
1970+
msg: Optional[str] = None
1971+
if resp.status_code == HTTP_PRECONDITION_FAILED:
1972+
raise DocumentRevisionError(resp, request)
1973+
elif resp.status_code == HTTP_NOT_FOUND:
1974+
msg = (
1975+
"Vertex or graph not found, or the collection is not part of "
1976+
"this graph. Error may also occur if the transaction ID is "
1977+
"unknown."
1978+
)
1979+
raise DocumentUpdateError(resp, request, msg)
1980+
1981+
return await self._executor.execute(request, response_handler)
1982+
1983+
async def replace(
1984+
self,
1985+
vertex: T,
1986+
wait_for_sync: Optional[bool] = None,
1987+
keep_null: Optional[bool] = None,
1988+
return_new: Optional[bool] = None,
1989+
return_old: Optional[bool] = None,
1990+
if_match: Optional[str] = None,
1991+
) -> Result[Json]:
1992+
"""Replace a document.
1993+
1994+
Args:
1995+
vertex (dict): New document. It must contain the "_key" or "_id" field.
1996+
wait_for_sync (bool | None): Wait until document has been synced to disk.
1997+
keep_null (bool | None): If the intention is to delete existing attributes
1998+
with the patch command, set this parameter to `False`.
1999+
return_new (bool | None): Additionally return the complete new document
2000+
under the attribute `new` in the result.
2001+
return_old (bool | None): Additionally return the complete old document
2002+
under the attribute `old` in the result.
2003+
if_match (str | None): You can conditionally replace a document based on a
2004+
target revision id by using the "if-match" HTTP header.
2005+
2006+
Returns:
2007+
dict: Document metadata (e.g. document id, key, revision).
2008+
If `return_new` or "return_old" are specified, the result contains
2009+
the document metadata in the "vertex" field and two additional fields
2010+
("new" and "old").
2011+
2012+
Raises:
2013+
DocumentRevisionError: If precondition was violated.
2014+
DocumentReplaceError: If replace fails.
2015+
2016+
References:
2017+
- `replace-a-vertex <https://docs.arangodb.com/stable/develop/http-api/graphs/named-graphs/#replace-a-vertex>`__
2018+
""" # noqa: E501
2019+
params: Params = {}
2020+
if wait_for_sync is not None:
2021+
params["waitForSync"] = wait_for_sync
2022+
if keep_null is not None:
2023+
params["keepNull"] = keep_null
2024+
if return_new is not None:
2025+
params["returnNew"] = return_new
2026+
if return_old is not None:
2027+
params["returnOld"] = return_old
2028+
2029+
headers: RequestHeaders = {}
2030+
if if_match is not None:
2031+
headers["If-Match"] = if_match
2032+
2033+
request = Request(
2034+
method=Method.PUT,
2035+
endpoint=f"/_api/gharial/{self._graph}/vertex/"
2036+
f"{self._prep_from_doc(cast(Json, vertex))}",
2037+
params=params,
2038+
headers=headers,
2039+
data=self._doc_serializer.dumps(vertex),
2040+
)
2041+
2042+
def response_handler(resp: Response) -> Json:
2043+
if resp.is_success:
2044+
return self._parse_result(self.deserializer.loads(resp.raw_body))
2045+
msg: Optional[str] = None
2046+
if resp.status_code == HTTP_PRECONDITION_FAILED:
2047+
raise DocumentRevisionError(resp, request)
2048+
elif resp.status_code == HTTP_NOT_FOUND:
2049+
msg = (
2050+
"Vertex or graph not found, or the collection is not part of "
2051+
"this graph. Error may also occur if the transaction ID is "
2052+
"unknown."
2053+
)
2054+
raise DocumentReplaceError(resp, request, msg)
2055+
2056+
return await self._executor.execute(request, response_handler)
2057+
2058+
async def delete(
2059+
self,
2060+
vertex: T,
2061+
ignore_missing: bool = False,
2062+
wait_for_sync: Optional[bool] = None,
2063+
return_old: Optional[bool] = None,
2064+
if_match: Optional[str] = None,
2065+
) -> Result[bool | Json]:
2066+
"""Delete a document.
2067+
2068+
Args:
2069+
vertex (dict): Document ID, key or body. The body must contain the
2070+
"_key" or "_id" field.
2071+
ignore_missing (bool): Do not raise an exception on missing document.
2072+
wait_for_sync (bool | None): Wait until operation has been synced to disk.
2073+
return_old (bool | None): Additionally return the complete old document
2074+
under the attribute `old` in the result.
2075+
if_match (str | None): You can conditionally replace a document based on a
2076+
target revision id by using the "if-match" HTTP header.
2077+
2078+
Returns:
2079+
bool | dict: `True` if vertex was deleted successfully, `False` if vertex
2080+
was not found and **ignore_missing** was set to `True` (does not apply in
2081+
transactions). Old document is returned if **return_old** is set to
2082+
`True`.
2083+
2084+
Raises:
2085+
DocumentRevisionError: If precondition was violated.
2086+
DocumentDeleteError: If deletion fails.
2087+
2088+
References:
2089+
- `remove-a-vertex <https://docs.arangodb.com/stable/develop/http-api/graphs/named-graphs/#remove-a-vertex>`__
2090+
""" # noqa: E501
2091+
params: Params = {}
2092+
if wait_for_sync is not None:
2093+
params["waitForSync"] = wait_for_sync
2094+
if return_old is not None:
2095+
params["returnOld"] = return_old
2096+
2097+
headers: RequestHeaders = {}
2098+
if if_match is not None:
2099+
headers["If-Match"] = if_match
2100+
2101+
request = Request(
2102+
method=Method.DELETE,
2103+
endpoint=f"/_api/gharial/{self._graph}/vertex/"
2104+
f"{self._prep_from_doc(cast(Json, vertex))}",
2105+
params=params,
2106+
headers=headers,
2107+
)
2108+
2109+
def response_handler(resp: Response) -> bool | Json:
2110+
if resp.is_success:
2111+
data: Json = self.deserializer.loads(resp.raw_body)
2112+
if "old" in data:
2113+
return cast(Json, data["old"])
2114+
return True
2115+
msg: Optional[str] = None
2116+
if resp.status_code == HTTP_PRECONDITION_FAILED:
2117+
raise DocumentRevisionError(resp, request)
2118+
elif resp.status_code == HTTP_NOT_FOUND:
2119+
if resp.error_code == DOCUMENT_NOT_FOUND and ignore_missing:
2120+
return False
2121+
msg = (
2122+
"Vertex or graph not found, or the collection is not part of "
2123+
"this graph. Error may also occur if the transaction ID is "
2124+
"unknown."
2125+
)
2126+
raise DocumentDeleteError(resp, request, msg)
2127+
2128+
return await self._executor.execute(request, response_handler)
2129+
18792130

18802131
class EdgeCollection(Collection[T, U, V]):
18812132
"""Edge collection API wrapper.

0 commit comments

Comments
 (0)