Skip to content

Commit b835963

Browse files
authored
feat: Added read_time as a parameter to various calls (async classes) (#1059)
* feat: Added read_time as a parameter to various calls (async classes) * used TYPE_CHECKING; fixed unit tests * linting + fixing cover * final linting
1 parent b6d754d commit b835963

14 files changed

+723
-78
lines changed

google/cloud/firestore_v1/async_aggregation.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
from google.cloud.firestore_v1.base_aggregation import AggregationResult
3838
from google.cloud.firestore_v1.query_profile import ExplainMetrics, ExplainOptions
3939
import google.cloud.firestore_v1.types.query_profile as query_profile_pb
40+
import datetime
4041

4142

4243
class AsyncAggregationQuery(BaseAggregationQuery):
@@ -55,6 +56,7 @@ async def get(
5556
timeout: float | None = None,
5657
*,
5758
explain_options: Optional[ExplainOptions] = None,
59+
read_time: Optional[datetime.datetime] = None,
5860
) -> QueryResultsList[List[AggregationResult]]:
5961
"""Runs the aggregation query.
6062
@@ -75,6 +77,10 @@ async def get(
7577
(Optional[:class:`~google.cloud.firestore_v1.query_profile.ExplainOptions`]):
7678
Options to enable query profiling for this query. When set,
7779
explain_metrics will be available on the returned generator.
80+
read_time (Optional[datetime.datetime]): If set, reads documents as they were at the given
81+
time. This must be a timestamp within the past one hour, or if Point-in-Time Recovery
82+
is enabled, can additionally be a whole minute timestamp within the past 7 days. If no
83+
timezone is specified in the :class:`datetime.datetime` object, it is assumed to be UTC.
7884
7985
Returns:
8086
QueryResultsList[List[AggregationResult]]: The aggregation query results.
@@ -87,6 +93,7 @@ async def get(
8793
retry=retry,
8894
timeout=timeout,
8995
explain_options=explain_options,
96+
read_time=read_time,
9097
)
9198
try:
9299
result = [aggregation async for aggregation in stream_result]
@@ -106,6 +113,7 @@ async def _make_stream(
106113
retry: retries.AsyncRetry | object | None = gapic_v1.method.DEFAULT,
107114
timeout: Optional[float] = None,
108115
explain_options: Optional[ExplainOptions] = None,
116+
read_time: Optional[datetime.datetime] = None,
109117
) -> AsyncGenerator[List[AggregationResult] | query_profile_pb.ExplainMetrics, Any]:
110118
"""Internal method for stream(). Runs the aggregation query.
111119
@@ -130,6 +138,10 @@ async def _make_stream(
130138
(Optional[:class:`~google.cloud.firestore_v1.query_profile.ExplainOptions`]):
131139
Options to enable query profiling for this query. When set,
132140
explain_metrics will be available on the returned generator.
141+
read_time (Optional[datetime.datetime]): If set, reads documents as they were at the given
142+
time. This must be a timestamp within the past one hour, or if Point-in-Time Recovery
143+
is enabled, can additionally be a whole minute timestamp within the past 7 days. If no
144+
timezone is specified in the :class:`datetime.datetime` object, it is assumed to be UTC.
133145
134146
Yields:
135147
List[AggregationResult] | query_profile_pb.ExplainMetrics:
@@ -143,6 +155,7 @@ async def _make_stream(
143155
retry,
144156
timeout,
145157
explain_options,
158+
read_time,
146159
)
147160

148161
response_iterator = await self._client._firestore_api.run_aggregation_query(
@@ -167,6 +180,7 @@ def stream(
167180
timeout: Optional[float] = None,
168181
*,
169182
explain_options: Optional[ExplainOptions] = None,
183+
read_time: Optional[datetime.datetime] = None,
170184
) -> AsyncStreamGenerator[List[AggregationResult]]:
171185
"""Runs the aggregation query.
172186
@@ -190,6 +204,10 @@ def stream(
190204
(Optional[:class:`~google.cloud.firestore_v1.query_profile.ExplainOptions`]):
191205
Options to enable query profiling for this query. When set,
192206
explain_metrics will be available on the returned generator.
207+
read_time (Optional[datetime.datetime]): If set, reads documents as they were at the given
208+
time. This must be a timestamp within the past one hour, or if Point-in-Time Recovery
209+
is enabled, can additionally be a whole minute timestamp within the past 7 days. If no
210+
timezone is specified in the :class:`datetime.datetime` object, it is assumed to be UTC.
193211
194212
Returns:
195213
`AsyncStreamGenerator[List[AggregationResult]]`:
@@ -201,5 +219,6 @@ def stream(
201219
retry=retry,
202220
timeout=timeout,
203221
explain_options=explain_options,
222+
read_time=read_time,
204223
)
205224
return AsyncStreamGenerator(inner_generator, explain_options)

google/cloud/firestore_v1/async_client.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,10 @@
4848
grpc_asyncio as firestore_grpc_transport,
4949
)
5050

51-
if TYPE_CHECKING:
52-
from google.cloud.firestore_v1.bulk_writer import BulkWriter # pragma: NO COVER
51+
if TYPE_CHECKING: # pragma: NO COVER
52+
import datetime
53+
54+
from google.cloud.firestore_v1.bulk_writer import BulkWriter
5355

5456

5557
class AsyncClient(BaseClient):
@@ -227,6 +229,8 @@ async def get_all(
227229
transaction: AsyncTransaction | None = None,
228230
retry: retries.AsyncRetry | object | None = gapic_v1.method.DEFAULT,
229231
timeout: float | None = None,
232+
*,
233+
read_time: datetime.datetime | None = None,
230234
) -> AsyncGenerator[DocumentSnapshot, Any]:
231235
"""Retrieve a batch of documents.
232236
@@ -261,13 +265,17 @@ async def get_all(
261265
should be retried. Defaults to a system-specified policy.
262266
timeout (float): The timeout for this request. Defaults to a
263267
system-specified value.
268+
read_time (Optional[datetime.datetime]): If set, reads documents as they were at the given
269+
time. This must be a timestamp within the past one hour, or if Point-in-Time Recovery
270+
is enabled, can additionally be a whole minute timestamp within the past 7 days. If no
271+
timezone is specified in the :class:`datetime.datetime` object, it is assumed to be UTC.
264272
265273
Yields:
266274
.DocumentSnapshot: The next document snapshot that fulfills the
267275
query, or :data:`None` if the document does not exist.
268276
"""
269277
request, reference_map, kwargs = self._prep_get_all(
270-
references, field_paths, transaction, retry, timeout
278+
references, field_paths, transaction, retry, timeout, read_time
271279
)
272280

273281
response_iterator = await self._firestore_api.batch_get_documents(
@@ -283,6 +291,8 @@ async def collections(
283291
self,
284292
retry: retries.AsyncRetry | object | None = gapic_v1.method.DEFAULT,
285293
timeout: float | None = None,
294+
*,
295+
read_time: datetime.datetime | None = None,
286296
) -> AsyncGenerator[AsyncCollectionReference, Any]:
287297
"""List top-level collections of the client's database.
288298
@@ -291,12 +301,16 @@ async def collections(
291301
should be retried. Defaults to a system-specified policy.
292302
timeout (float): The timeout for this request. Defaults to a
293303
system-specified value.
304+
read_time (Optional[datetime.datetime]): If set, reads documents as they were at the given
305+
time. This must be a timestamp within the past one hour, or if Point-in-Time Recovery
306+
is enabled, can additionally be a whole minute timestamp within the past 7 days. If no
307+
timezone is specified in the :class:`datetime.datetime` object, it is assumed to be UTC.
294308
295309
Returns:
296310
Sequence[:class:`~google.cloud.firestore_v1.async_collection.AsyncCollectionReference`]:
297311
iterator of subcollections of the current document.
298312
"""
299-
request, kwargs = self._prep_collections(retry, timeout)
313+
request, kwargs = self._prep_collections(retry, timeout, read_time)
300314
iterator = await self._firestore_api.list_collection_ids(
301315
request=request,
302316
metadata=self._rpc_metadata,

google/cloud/firestore_v1/async_collection.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
from google.cloud.firestore_v1.document import DocumentReference
3535

3636
if TYPE_CHECKING: # pragma: NO COVER
37+
import datetime
38+
3739
from google.cloud.firestore_v1.async_stream_generator import AsyncStreamGenerator
3840
from google.cloud.firestore_v1.base_document import DocumentSnapshot
3941
from google.cloud.firestore_v1.query_profile import ExplainOptions
@@ -162,6 +164,8 @@ async def list_documents(
162164
page_size: int | None = None,
163165
retry: retries.AsyncRetry | object | None = gapic_v1.method.DEFAULT,
164166
timeout: float | None = None,
167+
*,
168+
read_time: datetime.datetime | None = None,
165169
) -> AsyncGenerator[DocumentReference, None]:
166170
"""List all subdocuments of the current collection.
167171
@@ -173,14 +177,20 @@ async def list_documents(
173177
should be retried. Defaults to a system-specified policy.
174178
timeout (float): The timeout for this request. Defaults to a
175179
system-specified value.
180+
read_time (Optional[datetime.datetime]): If set, reads documents as they were at the given
181+
time. This must be a timestamp within the past one hour, or if Point-in-Time Recovery
182+
is enabled, can additionally be a whole minute timestamp within the past 7 days. If no
183+
timezone is specified in the :class:`datetime.datetime` object, it is assumed to be UTC.
176184
177185
Returns:
178186
Sequence[:class:`~google.cloud.firestore_v1.collection.DocumentReference`]:
179187
iterator of subdocuments of the current collection. If the
180188
collection does not exist at the time of `snapshot`, the
181189
iterator will be empty
182190
"""
183-
request, kwargs = self._prep_list_documents(page_size, retry, timeout)
191+
request, kwargs = self._prep_list_documents(
192+
page_size, retry, timeout, read_time
193+
)
184194

185195
iterator = await self._client._firestore_api.list_documents(
186196
request=request,
@@ -197,6 +207,7 @@ async def get(
197207
timeout: Optional[float] = None,
198208
*,
199209
explain_options: Optional[ExplainOptions] = None,
210+
read_time: Optional[datetime.datetime] = None,
200211
) -> QueryResultsList[DocumentSnapshot]:
201212
"""Read the documents in this collection.
202213
@@ -216,6 +227,10 @@ async def get(
216227
(Optional[:class:`~google.cloud.firestore_v1.query_profile.ExplainOptions`]):
217228
Options to enable query profiling for this query. When set,
218229
explain_metrics will be available on the returned generator.
230+
read_time (Optional[datetime.datetime]): If set, reads documents as they were at the given
231+
time. This must be a timestamp within the past one hour, or if Point-in-Time Recovery
232+
is enabled, can additionally be a whole minute timestamp within the past 7 days. If no
233+
timezone is specified in the :class:`datetime.datetime` object, it is assumed to be UTC.
219234
220235
If a ``transaction`` is used and it already has write operations added,
221236
this method cannot be used (i.e. read-after-write is not allowed).
@@ -227,6 +242,8 @@ async def get(
227242
query, kwargs = self._prep_get_or_stream(retry, timeout)
228243
if explain_options is not None:
229244
kwargs["explain_options"] = explain_options
245+
if read_time is not None:
246+
kwargs["read_time"] = read_time
230247

231248
return await query.get(transaction=transaction, **kwargs)
232249

@@ -237,6 +254,7 @@ def stream(
237254
timeout: Optional[float] = None,
238255
*,
239256
explain_options: Optional[ExplainOptions] = None,
257+
read_time: Optional[datetime.datetime] = None,
240258
) -> AsyncStreamGenerator[DocumentSnapshot]:
241259
"""Read the documents in this collection.
242260
@@ -268,6 +286,10 @@ def stream(
268286
(Optional[:class:`~google.cloud.firestore_v1.query_profile.ExplainOptions`]):
269287
Options to enable query profiling for this query. When set,
270288
explain_metrics will be available on the returned generator.
289+
read_time (Optional[datetime.datetime]): If set, reads documents as they were at the given
290+
time. This must be a timestamp within the past one hour, or if Point-in-Time Recovery
291+
is enabled, can additionally be a whole minute timestamp within the past 7 days. If no
292+
timezone is specified in the :class:`datetime.datetime` object, it is assumed to be UTC.
271293
272294
Returns:
273295
`AsyncStreamGenerator[DocumentSnapshot]`: A generator of the query
@@ -276,5 +298,7 @@ def stream(
276298
query, kwargs = self._prep_get_or_stream(retry, timeout)
277299
if explain_options:
278300
kwargs["explain_options"] = explain_options
301+
if read_time is not None:
302+
kwargs["read_time"] = read_time
279303

280304
return query.stream(transaction=transaction, **kwargs)

google/cloud/firestore_v1/async_document.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,8 @@ async def get(
329329
transaction=None,
330330
retry: retries.AsyncRetry | object | None = gapic_v1.method.DEFAULT,
331331
timeout: float | None = None,
332+
*,
333+
read_time: datetime.datetime | None = None,
332334
) -> DocumentSnapshot:
333335
"""Retrieve a snapshot of the current document.
334336
@@ -351,6 +353,10 @@ async def get(
351353
should be retried. Defaults to a system-specified policy.
352354
timeout (float): The timeout for this request. Defaults to a
353355
system-specified value.
356+
read_time (Optional[datetime.datetime]): If set, reads documents as they were at the given
357+
time. This must be a timestamp within the past one hour, or if Point-in-Time Recovery
358+
is enabled, can additionally be a whole minute timestamp within the past 7 days. If no
359+
timezone is specified in the :class:`datetime.datetime` object, it is assumed to be UTC.
354360
355361
Returns:
356362
:class:`~google.cloud.firestore_v1.base_document.DocumentSnapshot`:
@@ -362,7 +368,9 @@ async def get(
362368
"""
363369
from google.cloud.firestore_v1.base_client import _parse_batch_get
364370

365-
request, kwargs = self._prep_batch_get(field_paths, transaction, retry, timeout)
371+
request, kwargs = self._prep_batch_get(
372+
field_paths, transaction, retry, timeout, read_time
373+
)
366374

367375
response_iter = await self._client._firestore_api.batch_get_documents(
368376
request=request,
@@ -397,6 +405,8 @@ async def collections(
397405
page_size: int | None = None,
398406
retry: retries.AsyncRetry | object | None = gapic_v1.method.DEFAULT,
399407
timeout: float | None = None,
408+
*,
409+
read_time: datetime.datetime | None = None,
400410
) -> AsyncGenerator:
401411
"""List subcollections of the current document.
402412
@@ -408,14 +418,18 @@ async def collections(
408418
should be retried. Defaults to a system-specified policy.
409419
timeout (float): The timeout for this request. Defaults to a
410420
system-specified value.
421+
read_time (Optional[datetime.datetime]): If set, reads documents as they were at the given
422+
time. This must be a timestamp within the past one hour, or if Point-in-Time Recovery
423+
is enabled, can additionally be a whole minute timestamp within the past 7 days. If no
424+
timezone is specified in the :class:`datetime.datetime` object, it is assumed to be UTC.
411425
412426
Returns:
413427
Sequence[:class:`~google.cloud.firestore_v1.async_collection.AsyncCollectionReference`]:
414428
iterator of subcollections of the current document. If the
415429
document does not exist at the time of `snapshot`, the
416430
iterator will be empty
417431
"""
418-
request, kwargs = self._prep_collections(page_size, retry, timeout)
432+
request, kwargs = self._prep_collections(page_size, retry, timeout, read_time)
419433

420434
iterator = await self._client._firestore_api.list_collection_ids(
421435
request=request,

0 commit comments

Comments
 (0)