diff --git a/fauna/client/client.py b/fauna/client/client.py index 1869995f..3b6255cf 100644 --- a/fauna/client/client.py +++ b/fauna/client/client.py @@ -316,6 +316,7 @@ def _query( query_tags = QueryTags.decode( dec["query_tags"]) if "query_tags" in dec else None txn_ts = dec["txn_ts"] if "txn_ts" in dec else None + schema_version = dec["schema_version"] if "schema_version" in dec else None traceparent = headers.get("traceparent", None) static_type = dec["static_type"] if "static_type" in dec else None @@ -327,6 +328,7 @@ def _query( summary=summary, traceparent=traceparent, txn_ts=txn_ts, + schema_version=schema_version, ) def _check_protocol(self, response_json: Any, status_code): @@ -361,6 +363,7 @@ def _handle_error(self, body: Any, status_code: int): body["query_tags"]) if "query_tags" in body else None stats = QueryStats(body["stats"]) if "stats" in body else None txn_ts = body["txn_ts"] if "txn_ts" in body else None + schema_version = body["schema_version"] if "schema_version" in body else None summary = body["summary"] if "summary" in body else None constraint_failures: Optional[List[ConstraintFailure]] = None @@ -384,6 +387,7 @@ def _handle_error(self, body: Any, status_code: int): query_tags=query_tags, stats=stats, txn_ts=txn_ts, + schema_version=schema_version, ) elif code == "invalid_request": raise InvalidRequestError( @@ -395,6 +399,7 @@ def _handle_error(self, body: Any, status_code: int): query_tags=query_tags, stats=stats, txn_ts=txn_ts, + schema_version=schema_version, ) elif code == "abort": abort = err["abort"] if "abort" in err else None @@ -408,6 +413,7 @@ def _handle_error(self, body: Any, status_code: int): query_tags=query_tags, stats=stats, txn_ts=txn_ts, + schema_version=schema_version, ) else: @@ -420,6 +426,7 @@ def _handle_error(self, body: Any, status_code: int): query_tags=query_tags, stats=stats, txn_ts=txn_ts, + schema_version=schema_version, ) elif status_code == 401: raise AuthenticationError( @@ -431,6 +438,7 @@ def _handle_error(self, body: Any, status_code: int): query_tags=query_tags, stats=stats, txn_ts=txn_ts, + schema_version=schema_version, ) elif status_code == 403: raise AuthorizationError( @@ -442,6 +450,7 @@ def _handle_error(self, body: Any, status_code: int): query_tags=query_tags, stats=stats, txn_ts=txn_ts, + schema_version=schema_version, ) elif status_code == 429: raise ThrottlingError( @@ -453,6 +462,7 @@ def _handle_error(self, body: Any, status_code: int): query_tags=query_tags, stats=stats, txn_ts=txn_ts, + schema_version=schema_version, ) elif status_code == 440: raise QueryTimeoutError( @@ -464,6 +474,7 @@ def _handle_error(self, body: Any, status_code: int): query_tags=query_tags, stats=stats, txn_ts=txn_ts, + schema_version=schema_version, ) elif status_code == 500: raise ServiceInternalError( @@ -475,6 +486,7 @@ def _handle_error(self, body: Any, status_code: int): query_tags=query_tags, stats=stats, txn_ts=txn_ts, + schema_version=schema_version, ) elif status_code == 503: raise ServiceTimeoutError( @@ -486,6 +498,7 @@ def _handle_error(self, body: Any, status_code: int): query_tags=query_tags, stats=stats, txn_ts=txn_ts, + schema_version=schema_version, ) else: raise ServiceError( @@ -497,6 +510,7 @@ def _handle_error(self, body: Any, status_code: int): query_tags=query_tags, stats=stats, txn_ts=txn_ts, + schema_version=schema_version, ) def _set_endpoint(self, endpoint): diff --git a/fauna/encoding/wire_protocol.py b/fauna/encoding/wire_protocol.py index b28b4bcb..7e12b7b5 100644 --- a/fauna/encoding/wire_protocol.py +++ b/fauna/encoding/wire_protocol.py @@ -95,26 +95,35 @@ def stats(self) -> QueryStats: @property def txn_ts(self) -> int: + """The last transaction timestamp of the query. A Unix epoch in microseconds.""" return self._txn_ts + @property + def schema_version(self) -> int: + """The schema version that was used for the query execution.""" + return self._schema_version + def __init__( self, query_tags: Optional[Mapping[str, str]] = None, stats: Optional[QueryStats] = None, summary: Optional[str] = None, txn_ts: Optional[int] = None, + schema_version: Optional[int] = None, ): self._query_tags = query_tags or {} self._stats = stats or QueryStats({}) self._summary = summary or "" self._txn_ts = txn_ts or 0 + self._schema_version = schema_version or 0 def __repr__(self): return f"{self.__class__.__name__}(" \ f"query_tags={repr(self.query_tags)}," \ f"stats={repr(self.stats)}," \ f"summary={repr(self.summary)}," \ - f"txn_ts={repr(self.txn_ts)})" + f"txn_ts={repr(self.txn_ts)}," \ + f"schema_version={repr(self.schema_version)})" class QuerySuccess(QueryInfo): @@ -144,10 +153,16 @@ def __init__( summary: Optional[str], traceparent: Optional[str], txn_ts: Optional[int], + schema_version: Optional[int], ): super().__init__( - query_tags=query_tags, stats=stats, summary=summary, txn_ts=txn_ts) + query_tags=query_tags, + stats=stats, + summary=summary, + txn_ts=txn_ts, + schema_version=schema_version, + ) self._traceparent = traceparent self._static_type = static_type @@ -161,6 +176,7 @@ def __repr__(self): f"summary={repr(self.summary)}," \ f"traceparent={repr(self.traceparent)}," \ f"txn_ts={repr(self.txn_ts)}," \ + f"schema_version={repr(self.schema_version)}," \ f"data={repr(self.data)})" diff --git a/fauna/errors/errors.py b/fauna/errors/errors.py index e06801cb..bb938baf 100644 --- a/fauna/errors/errors.py +++ b/fauna/errors/errors.py @@ -95,6 +95,7 @@ def __init__( query_tags: Optional[Mapping[str, str]] = None, stats: Optional[QueryStats] = None, txn_ts: Optional[int] = None, + schema_version: Optional[int] = None, ): QueryInfo.__init__( self, @@ -102,6 +103,7 @@ def __init__( stats=stats, summary=summary, txn_ts=txn_ts, + schema_version=schema_version, ) FaunaError.__init__( diff --git a/tests/integration/test_query.py b/tests/integration/test_query.py index d7536fa5..73df836c 100644 --- a/tests/integration/test_query.py +++ b/tests/integration/test_query.py @@ -4,7 +4,7 @@ import pytest -from fauna import fql, Page, NullDocument +from fauna import fql, Page, NullDocument, Module from fauna.client import Client, QueryOptions from fauna.errors import QueryCheckError, QueryRuntimeError, AbortError, ClientError, QueryTimeoutError from fauna.encoding import ConstraintFailure @@ -18,6 +18,7 @@ def test_query_smoke_test(subtests, client): assert res.stats.compute_ops > 0 assert res.traceparent != "" assert res.summary == "" + assert res.schema_version > 0 with subtests.test(msg="with debug"): res = client.query(fql('dbg("Hello, World")')) @@ -136,3 +137,14 @@ def test_query_timeout(client, a_collection): client.query( fql("${coll}.byId('123')", coll=a_collection), QueryOptions(query_timeout=timedelta(milliseconds=1))) + + +def test_query_schema_version(client): + coll_name = "schemaVersionTestColl" + + _ = client.query(fql("Collection.byName(${coll})?.delete()", coll=coll_name)) + r = client.query(fql("Collection.create({ name: ${name} })", name=coll_name)) + expected_schema_version = r.txn_ts + + r = client.query(fql("${mod}.all()", mod=Module(coll_name))) + assert expected_schema_version == r.schema_version diff --git a/tests/unit/test_wire_protocol.py b/tests/unit/test_wire_protocol.py index 4b1e55ba..cae4262c 100644 --- a/tests/unit/test_wire_protocol.py +++ b/tests/unit/test_wire_protocol.py @@ -10,6 +10,7 @@ def test_query_success_repr(): summary="human readable", traceparent=None, txn_ts=123, + schema_version=123, ) stats = "{'compute_ops': 1, 'read_ops': 0, 'write_ops': 0, " \ "'query_time_ms': 0, 'storage_bytes_read': 0, 'storage_bytes_write': 0, " \ @@ -20,10 +21,12 @@ def test_query_success_repr(): "summary='human readable'," \ "traceparent=None," \ "txn_ts=123," \ + "schema_version=123," \ "data={'foo': 'bar'})" evaluated: QuerySuccess = eval(repr(qs)) assert evaluated.txn_ts == qs.txn_ts + assert evaluated.schema_version == qs.schema_version assert evaluated.traceparent == qs.traceparent assert evaluated.query_tags == qs.query_tags assert evaluated.data == qs.data @@ -38,6 +41,7 @@ def test_query_info_repr(): stats=QueryStats({'compute_ops': 1}), summary="human readable", txn_ts=123, + schema_version=123, ) stats = "{'compute_ops': 1, 'read_ops': 0, 'write_ops': 0, " \ "'query_time_ms': 0, 'storage_bytes_read': 0, 'storage_bytes_write': 0, " \ @@ -45,13 +49,15 @@ def test_query_info_repr(): assert repr(qi) == "QueryInfo(query_tags={'tag': 'value'}," \ f"stats=QueryStats(stats={stats})," \ "summary='human readable'," \ - "txn_ts=123)" + "txn_ts=123," \ + "schema_version=123)" evaluated: QueryInfo = eval(repr(qi)) assert evaluated.txn_ts == qi.txn_ts assert evaluated.query_tags == qi.query_tags assert evaluated.summary == qi.summary assert evaluated.stats == qi.stats + assert evaluated.schema_version == qi.schema_version def test_query_stats_repr():