Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/google/cloud/firestore_v1/async_client.py: 44%

68 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-09 06:27 +0000

1# Copyright 2020 Google LLC All rights reserved. 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14 

15"""Client for interacting with the Google Cloud Firestore API. 

16 

17This is the base from which all interactions with the API occur. 

18 

19In the hierarchy of API concepts 

20 

21* a :class:`~google.cloud.firestore_v1.client.Client` owns a 

22 :class:`~google.cloud.firestore_v1.async_collection.AsyncCollectionReference` 

23* a :class:`~google.cloud.firestore_v1.client.Client` owns a 

24 :class:`~google.cloud.firestore_v1.async_document.AsyncDocumentReference` 

25""" 

26 

27from google.api_core import gapic_v1 

28from google.api_core import retry as retries 

29 

30from google.cloud.firestore_v1.base_client import ( 

31 BaseClient, 

32 _CLIENT_INFO, 

33 _parse_batch_get, # type: ignore 

34 _path_helper, 

35) 

36 

37from google.cloud.firestore_v1.async_query import AsyncCollectionGroup 

38from google.cloud.firestore_v1.async_batch import AsyncWriteBatch 

39from google.cloud.firestore_v1.async_collection import AsyncCollectionReference 

40from google.cloud.firestore_v1.async_document import ( 

41 AsyncDocumentReference, 

42 DocumentSnapshot, 

43) 

44from google.cloud.firestore_v1.async_transaction import AsyncTransaction 

45from google.cloud.firestore_v1.field_path import FieldPath 

46from google.cloud.firestore_v1.services.firestore import ( 

47 async_client as firestore_client, 

48) 

49from google.cloud.firestore_v1.services.firestore.transports import ( 

50 grpc_asyncio as firestore_grpc_transport, 

51) 

52from typing import Any, AsyncGenerator, Iterable, List, Optional, Union, TYPE_CHECKING 

53 

54if TYPE_CHECKING: 

55 from google.cloud.firestore_v1.bulk_writer import BulkWriter # pragma: NO COVER 

56 

57 

58class AsyncClient(BaseClient): 

59 """Client for interacting with Google Cloud Firestore API. 

60 

61 .. note:: 

62 

63 Since the Cloud Firestore API requires the gRPC transport, no 

64 ``_http`` argument is accepted by this class. 

65 

66 Args: 

67 project (Optional[str]): The project which the client acts on behalf 

68 of. If not passed, falls back to the default inferred 

69 from the environment. 

70 credentials (Optional[~google.auth.credentials.Credentials]): The 

71 OAuth2 Credentials to use for this client. If not passed, falls 

72 back to the default inferred from the environment. 

73 database (Optional[str]): The database name that the client targets. 

74 For now, :attr:`DEFAULT_DATABASE` (the default value) is the 

75 only valid database. 

76 client_info (Optional[google.api_core.gapic_v1.client_info.ClientInfo]): 

77 The client info used to send a user-agent string along with API 

78 requests. If ``None``, then default info will be used. Generally, 

79 you only need to set this if you're developing your own library 

80 or partner tool. 

81 client_options (Union[dict, google.api_core.client_options.ClientOptions]): 

82 Client options used to set user options on the client. API Endpoint 

83 should be set through client_options. 

84 """ 

85 

86 def __init__( 

87 self, 

88 project=None, 

89 credentials=None, 

90 database=None, 

91 client_info=_CLIENT_INFO, 

92 client_options=None, 

93 ) -> None: 

94 super(AsyncClient, self).__init__( 

95 project=project, 

96 credentials=credentials, 

97 database=database, 

98 client_info=client_info, 

99 client_options=client_options, 

100 ) 

101 

102 def _to_sync_copy(self): 

103 from google.cloud.firestore_v1.client import Client 

104 

105 if not getattr(self, "_sync_copy", None): 

106 self._sync_copy = Client( 

107 project=self.project, 

108 credentials=self._credentials, 

109 database=self._database, 

110 client_info=self._client_info, 

111 client_options=self._client_options, 

112 ) 

113 return self._sync_copy 

114 

115 @property 

116 def _firestore_api(self): 

117 """Lazy-loading getter GAPIC Firestore API. 

118 Returns: 

119 :class:`~google.cloud.gapic.firestore.v1`.async_firestore_client.FirestoreAsyncClient: 

120 The GAPIC client with the credentials of the current client. 

121 """ 

122 return self._firestore_api_helper( 

123 firestore_grpc_transport.FirestoreGrpcAsyncIOTransport, 

124 firestore_client.FirestoreAsyncClient, 

125 firestore_client, 

126 ) 

127 

128 @property 

129 def _target(self): 

130 """Return the target (where the API is). 

131 Eg. "firestore.googleapis.com" 

132 

133 Returns: 

134 str: The location of the API. 

135 """ 

136 return self._target_helper(firestore_client.FirestoreAsyncClient) 

137 

138 def collection(self, *collection_path: str) -> AsyncCollectionReference: 

139 """Get a reference to a collection. 

140 

141 For a top-level collection: 

142 

143 .. code-block:: python 

144 

145 >>> client.collection('top') 

146 

147 For a sub-collection: 

148 

149 .. code-block:: python 

150 

151 >>> client.collection('mydocs/doc/subcol') 

152 >>> # is the same as 

153 >>> client.collection('mydocs', 'doc', 'subcol') 

154 

155 Sub-collections can be nested deeper in a similar fashion. 

156 

157 Args: 

158 collection_path: Can either be 

159 

160 * A single ``/``-delimited path to a collection 

161 * A tuple of collection path segments 

162 

163 Returns: 

164 :class:`~google.cloud.firestore_v1.async_collection.AsyncCollectionReference`: 

165 A reference to a collection in the Firestore database. 

166 """ 

167 return AsyncCollectionReference(*_path_helper(collection_path), client=self) 

168 

169 def collection_group(self, collection_id: str) -> AsyncCollectionGroup: 

170 """ 

171 Creates and returns a new AsyncQuery that includes all documents in the 

172 database that are contained in a collection or subcollection with the 

173 given collection_id. 

174 

175 .. code-block:: python 

176 

177 >>> query = client.collection_group('mygroup') 

178 

179 Args: 

180 collection_id (str) Identifies the collections to query over. 

181 

182 Every collection or subcollection with this ID as the last segment of its 

183 path will be included. Cannot contain a slash. 

184 

185 Returns: 

186 :class:`~google.cloud.firestore_v1.async_query.AsyncCollectionGroup`: 

187 The created AsyncQuery. 

188 """ 

189 return AsyncCollectionGroup(self._get_collection_reference(collection_id)) 

190 

191 def document(self, *document_path: str) -> AsyncDocumentReference: 

192 """Get a reference to a document in a collection. 

193 

194 For a top-level document: 

195 

196 .. code-block:: python 

197 

198 >>> client.document('collek/shun') 

199 >>> # is the same as 

200 >>> client.document('collek', 'shun') 

201 

202 For a document in a sub-collection: 

203 

204 .. code-block:: python 

205 

206 >>> client.document('mydocs/doc/subcol/child') 

207 >>> # is the same as 

208 >>> client.document('mydocs', 'doc', 'subcol', 'child') 

209 

210 Documents in sub-collections can be nested deeper in a similar fashion. 

211 

212 Args: 

213 document_path: Can either be 

214 

215 * A single ``/``-delimited path to a document 

216 * A tuple of document path segments 

217 

218 Returns: 

219 :class:`~google.cloud.firestore_v1.document.AsyncDocumentReference`: 

220 A reference to a document in a collection. 

221 """ 

222 return AsyncDocumentReference( 

223 *self._document_path_helper(*document_path), client=self 

224 ) 

225 

226 async def get_all( 

227 self, 

228 references: List[AsyncDocumentReference], 

229 field_paths: Iterable[str] = None, 

230 transaction=None, 

231 retry: retries.Retry = gapic_v1.method.DEFAULT, 

232 timeout: float = None, 

233 ) -> AsyncGenerator[DocumentSnapshot, Any]: 

234 """Retrieve a batch of documents. 

235 

236 .. note:: 

237 

238 Documents returned by this method are not guaranteed to be 

239 returned in the same order that they are given in ``references``. 

240 

241 .. note:: 

242 

243 If multiple ``references`` refer to the same document, the server 

244 will only return one result. 

245 

246 See :meth:`~google.cloud.firestore_v1.client.Client.field_path` for 

247 more information on **field paths**. 

248 

249 If a ``transaction`` is used and it already has write operations 

250 added, this method cannot be used (i.e. read-after-write is not 

251 allowed). 

252 

253 Args: 

254 references (List[.AsyncDocumentReference, ...]): Iterable of document 

255 references to be retrieved. 

256 field_paths (Optional[Iterable[str, ...]]): An iterable of field 

257 paths (``.``-delimited list of field names) to use as a 

258 projection of document fields in the returned results. If 

259 no value is provided, all fields will be returned. 

260 transaction (Optional[:class:`~google.cloud.firestore_v1.async_transaction.AsyncTransaction`]): 

261 An existing transaction that these ``references`` will be 

262 retrieved in. 

263 retry (google.api_core.retry.Retry): Designation of what errors, if any, 

264 should be retried. Defaults to a system-specified policy. 

265 timeout (float): The timeout for this request. Defaults to a 

266 system-specified value. 

267 

268 Yields: 

269 .DocumentSnapshot: The next document snapshot that fulfills the 

270 query, or :data:`None` if the document does not exist. 

271 """ 

272 request, reference_map, kwargs = self._prep_get_all( 

273 references, field_paths, transaction, retry, timeout 

274 ) 

275 

276 response_iterator = await self._firestore_api.batch_get_documents( 

277 request=request, 

278 metadata=self._rpc_metadata, 

279 **kwargs, 

280 ) 

281 

282 async for get_doc_response in response_iterator: 

283 yield _parse_batch_get(get_doc_response, reference_map, self) 

284 

285 async def collections( 

286 self, 

287 retry: retries.Retry = gapic_v1.method.DEFAULT, 

288 timeout: float = None, 

289 ) -> AsyncGenerator[AsyncCollectionReference, Any]: 

290 """List top-level collections of the client's database. 

291 

292 Args: 

293 retry (google.api_core.retry.Retry): Designation of what errors, if any, 

294 should be retried. Defaults to a system-specified policy. 

295 timeout (float): The timeout for this request. Defaults to a 

296 system-specified value. 

297 

298 Returns: 

299 Sequence[:class:`~google.cloud.firestore_v1.async_collection.AsyncCollectionReference`]: 

300 iterator of subcollections of the current document. 

301 """ 

302 request, kwargs = self._prep_collections(retry, timeout) 

303 iterator = await self._firestore_api.list_collection_ids( 

304 request=request, 

305 metadata=self._rpc_metadata, 

306 **kwargs, 

307 ) 

308 

309 async for collection_id in iterator: 

310 yield self.collection(collection_id) 

311 

312 async def recursive_delete( 

313 self, 

314 reference: Union[AsyncCollectionReference, AsyncDocumentReference], 

315 *, 

316 bulk_writer: Optional["BulkWriter"] = None, 

317 chunk_size: Optional[int] = 5000, 

318 ): 

319 """Deletes documents and their subcollections, regardless of collection 

320 name. 

321 

322 Passing an AsyncCollectionReference leads to each document in the 

323 collection getting deleted, as well as all of their descendents. 

324 

325 Passing an AsyncDocumentReference deletes that one document and all of 

326 its descendents. 

327 

328 Args: 

329 reference (Union[ 

330 :class:`@google.cloud.firestore_v1.async_collection.CollectionReference`, 

331 :class:`@google.cloud.firestore_v1.async_document.DocumentReference`, 

332 ]) 

333 The reference to be deleted. 

334 

335 bulk_writer (Optional[:class:`@google.cloud.firestore_v1.bulk_writer.BulkWriter`]) 

336 The BulkWriter used to delete all matching documents. Supply this 

337 if you want to override the default throttling behavior. 

338 """ 

339 if bulk_writer is None: 

340 bulk_writer = self.bulk_writer() 

341 

342 return await self._recursive_delete( 

343 reference, 

344 bulk_writer=bulk_writer, 

345 chunk_size=chunk_size, 

346 ) 

347 

348 async def _recursive_delete( 

349 self, 

350 reference: Union[AsyncCollectionReference, AsyncDocumentReference], 

351 bulk_writer: "BulkWriter", 

352 *, 

353 chunk_size: Optional[int] = 5000, 

354 depth: Optional[int] = 0, 

355 ) -> int: 

356 """Recursion helper for `recursive_delete.""" 

357 

358 num_deleted: int = 0 

359 

360 if isinstance(reference, AsyncCollectionReference): 

361 chunk: List[DocumentSnapshot] 

362 async for chunk in reference.recursive().select( 

363 [FieldPath.document_id()] 

364 )._chunkify(chunk_size): 

365 doc_snap: DocumentSnapshot 

366 for doc_snap in chunk: 

367 num_deleted += 1 

368 bulk_writer.delete(doc_snap.reference) 

369 

370 elif isinstance(reference, AsyncDocumentReference): 

371 col_ref: AsyncCollectionReference 

372 async for col_ref in reference.collections(): 

373 num_deleted += await self._recursive_delete( 

374 col_ref, 

375 bulk_writer=bulk_writer, 

376 depth=depth + 1, 

377 chunk_size=chunk_size, 

378 ) 

379 num_deleted += 1 

380 bulk_writer.delete(reference) 

381 

382 else: 

383 raise TypeError( 

384 f"Unexpected type for reference: {reference.__class__.__name__}" 

385 ) 

386 

387 if depth == 0: 

388 bulk_writer.close() 

389 

390 return num_deleted 

391 

392 def batch(self) -> AsyncWriteBatch: 

393 """Get a batch instance from this client. 

394 

395 Returns: 

396 :class:`~google.cloud.firestore_v1.async_batch.AsyncWriteBatch`: 

397 A "write" batch to be used for accumulating document changes and 

398 sending the changes all at once. 

399 """ 

400 return AsyncWriteBatch(self) 

401 

402 def transaction(self, **kwargs) -> AsyncTransaction: 

403 """Get a transaction that uses this client. 

404 

405 See :class:`~google.cloud.firestore_v1.async_transaction.AsyncTransaction` for 

406 more information on transactions and the constructor arguments. 

407 

408 Args: 

409 kwargs (Dict[str, Any]): The keyword arguments (other than 

410 ``client``) to pass along to the 

411 :class:`~google.cloud.firestore_v1.async_transaction.AsyncTransaction` 

412 constructor. 

413 

414 Returns: 

415 :class:`~google.cloud.firestore_v1.async_transaction.AsyncTransaction`: 

416 A transaction attached to this client. 

417 """ 

418 return AsyncTransaction(self, **kwargs)