Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/google/cloud/firestore_v1/client.py: 51%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

70 statements  

1# Copyright 2017 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.collection.CollectionReference` 

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

24 :class:`~google.cloud.firestore_v1.document.DocumentReference` 

25""" 

26from __future__ import annotations 

27 

28from typing import TYPE_CHECKING, Any, Generator, Iterable, List, Optional, Union 

29 

30from google.api_core import gapic_v1 

31from google.api_core import retry as retries 

32 

33from google.cloud.firestore_v1.base_client import ( 

34 _CLIENT_INFO, 

35 BaseClient, 

36 _parse_batch_get, 

37 _path_helper, 

38) 

39 

40# Types needed only for Type Hints 

41from google.cloud.firestore_v1.base_document import DocumentSnapshot 

42from google.cloud.firestore_v1.base_transaction import MAX_ATTEMPTS 

43from google.cloud.firestore_v1.batch import WriteBatch 

44from google.cloud.firestore_v1.collection import CollectionReference 

45from google.cloud.firestore_v1.document import DocumentReference 

46from google.cloud.firestore_v1.field_path import FieldPath 

47from google.cloud.firestore_v1.query import CollectionGroup 

48from google.cloud.firestore_v1.services.firestore import client as firestore_client 

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

50 grpc as firestore_grpc_transport, 

51) 

52from google.cloud.firestore_v1.transaction import Transaction 

53from google.cloud.firestore_v1.pipeline import Pipeline 

54from google.cloud.firestore_v1.pipeline_source import PipelineSource 

55 

56if TYPE_CHECKING: # pragma: NO COVER 

57 from google.cloud.firestore_v1.bulk_writer import BulkWriter 

58 import datetime 

59 

60 

61class Client(BaseClient): 

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

63 

64 .. note:: 

65 

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

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

68 

69 Args: 

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

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

72 from the environment. 

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

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

75 back to the default inferred from the environment. 

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

77 If not passed, falls back to :attr:`DEFAULT_DATABASE`. 

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

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

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

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

82 or partner tool. 

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

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

85 should be set through client_options. 

86 """ 

87 

88 def __init__( 

89 self, 

90 project=None, 

91 credentials=None, 

92 database=None, 

93 client_info=_CLIENT_INFO, 

94 client_options=None, 

95 ) -> None: 

96 super(Client, self).__init__( 

97 project=project, 

98 credentials=credentials, 

99 database=database, 

100 client_info=client_info, 

101 client_options=client_options, 

102 ) 

103 

104 @property 

105 def _firestore_api(self): 

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

107 Returns: 

108 :class:`~google.cloud.gapic.firestore.v1`.firestore_client.FirestoreClient: 

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

110 """ 

111 return self._firestore_api_helper( 

112 firestore_grpc_transport.FirestoreGrpcTransport, 

113 firestore_client.FirestoreClient, 

114 firestore_client, 

115 ) 

116 

117 def collection(self, *collection_path: str) -> CollectionReference: 

118 """Get a reference to a collection. 

119 

120 For a top-level collection: 

121 

122 .. code-block:: python 

123 

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

125 

126 For a sub-collection: 

127 

128 .. code-block:: python 

129 

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

131 >>> # is the same as 

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

133 

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

135 

136 Args: 

137 collection_path: Can either be 

138 

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

140 * A tuple of collection path segments 

141 

142 Returns: 

143 :class:`~google.cloud.firestore_v1.collection.CollectionReference`: 

144 A reference to a collection in the Firestore database. 

145 """ 

146 return CollectionReference(*_path_helper(collection_path), client=self) 

147 

148 def collection_group(self, collection_id: str) -> CollectionGroup: 

149 """ 

150 Creates and returns a new Query that includes all documents in the 

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

152 given collection_id. 

153 

154 .. code-block:: python 

155 

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

157 

158 Args: 

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

160 

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

162 path will be included. Cannot contain a slash. 

163 

164 Returns: 

165 :class:`~google.cloud.firestore_v1.query.CollectionGroup`: 

166 The created Query. 

167 """ 

168 return CollectionGroup(self._get_collection_reference(collection_id)) 

169 

170 def document(self, *document_path: str) -> DocumentReference: 

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

172 

173 For a top-level document: 

174 

175 .. code-block:: python 

176 

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

178 >>> # is the same as 

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

180 

181 For a document in a sub-collection: 

182 

183 .. code-block:: python 

184 

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

186 >>> # is the same as 

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

188 

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

190 

191 Args: 

192 document_path): Can either be 

193 

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

195 * A tuple of document path segments 

196 

197 Returns: 

198 :class:`~google.cloud.firestore_v1.document.DocumentReference`: 

199 A reference to a document in a collection. 

200 """ 

201 return DocumentReference( 

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

203 ) 

204 

205 def get_all( 

206 self, 

207 references: list, 

208 field_paths: Iterable[str] | None = None, 

209 transaction: Transaction | None = None, 

210 retry: retries.Retry | object | None = gapic_v1.method.DEFAULT, 

211 timeout: float | None = None, 

212 *, 

213 read_time: datetime.datetime | None = None, 

214 ) -> Generator[DocumentSnapshot, Any, None]: 

215 """Retrieve a batch of documents. 

216 

217 .. note:: 

218 

219 Documents returned by this method are not guaranteed to be 

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

221 

222 .. note:: 

223 

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

225 will only return one result. 

226 

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

228 more information on **field paths**. 

229 

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

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

232 allowed). 

233 

234 Args: 

235 references (List[.DocumentReference, ...]): Iterable of document 

236 references to be retrieved. 

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

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

239 projection of document fields in the returned results. If 

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

241 transaction (Optional[:class:`~google.cloud.firestore_v1.transaction.Transaction`]): 

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

243 retrieved in. 

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

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

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

247 system-specified value. 

248 read_time (Optional[datetime.datetime]): If set, reads documents as they were at the given 

249 time. This must be a timestamp within the past one hour, or if Point-in-Time Recovery 

250 is enabled, can additionally be a whole minute timestamp within the past 7 days. If no 

251 timezone is specified in the :class:`datetime.datetime` object, it is assumed to be UTC. 

252 

253 Yields: 

254 .DocumentSnapshot: The next document snapshot that fulfills the 

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

256 """ 

257 request, reference_map, kwargs = self._prep_get_all( 

258 references, field_paths, transaction, retry, timeout, read_time 

259 ) 

260 

261 response_iterator = self._firestore_api.batch_get_documents( 

262 request=request, 

263 metadata=self._rpc_metadata, 

264 **kwargs, 

265 ) 

266 

267 for get_doc_response in response_iterator: 

268 yield _parse_batch_get(get_doc_response, reference_map, self) 

269 

270 def collections( 

271 self, 

272 retry: retries.Retry | object | None = gapic_v1.method.DEFAULT, 

273 timeout: float | None = None, 

274 *, 

275 read_time: datetime.datetime | None = None, 

276 ) -> Generator[Any, Any, None]: 

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

278 

279 Args: 

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

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

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

283 system-specified value. 

284 read_time (Optional[datetime.datetime]): If set, reads documents as they were at the given 

285 time. This must be a timestamp within the past one hour, or if Point-in-Time Recovery 

286 is enabled, can additionally be a whole minute timestamp within the past 7 days. If no 

287 timezone is specified in the :class:`datetime.datetime` object, it is assumed to be UTC. 

288 

289 Returns: 

290 Sequence[:class:`~google.cloud.firestore_v1.collection.CollectionReference`]: 

291 iterator of subcollections of the current document. 

292 """ 

293 request, kwargs = self._prep_collections(retry, timeout, read_time) 

294 

295 iterator = self._firestore_api.list_collection_ids( 

296 request=request, 

297 metadata=self._rpc_metadata, 

298 **kwargs, 

299 ) 

300 

301 for collection_id in iterator: 

302 yield self.collection(collection_id) 

303 

304 def recursive_delete( 

305 self, 

306 reference: Union[CollectionReference, DocumentReference], 

307 *, 

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

309 chunk_size: int = 5000, 

310 ) -> int: 

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

312 name. 

313 

314 Passing a CollectionReference leads to each document in the collection 

315 getting deleted, as well as all of their descendents. 

316 

317 Passing a DocumentReference deletes that one document and all of its 

318 descendents. 

319 

320 Args: 

321 reference (Union[ 

322 :class:`@google.cloud.firestore_v1.collection.CollectionReference`, 

323 :class:`@google.cloud.firestore_v1.document.DocumentReference`, 

324 ]) 

325 The reference to be deleted. 

326 

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

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

329 if you want to override the default throttling behavior. 

330 

331 """ 

332 if bulk_writer is None: 

333 bulk_writer = self.bulk_writer() 

334 

335 return self._recursive_delete( 

336 reference, 

337 bulk_writer, 

338 chunk_size=chunk_size, 

339 ) 

340 

341 def _recursive_delete( 

342 self, 

343 reference: Union[CollectionReference, DocumentReference], 

344 bulk_writer: "BulkWriter", 

345 *, 

346 chunk_size: int = 5000, 

347 depth: int = 0, 

348 ) -> int: 

349 """Recursion helper for `recursive_delete.""" 

350 

351 num_deleted: int = 0 

352 

353 if isinstance(reference, CollectionReference): 

354 chunk: List[DocumentSnapshot] 

355 for chunk in ( 

356 reference.recursive() 

357 .select([FieldPath.document_id()]) 

358 ._chunkify(chunk_size) 

359 ): 

360 doc_snap: DocumentSnapshot 

361 for doc_snap in chunk: 

362 num_deleted += 1 

363 bulk_writer.delete(doc_snap.reference) 

364 

365 elif isinstance(reference, DocumentReference): 

366 col_ref: CollectionReference 

367 for col_ref in reference.collections(): 

368 num_deleted += self._recursive_delete( 

369 col_ref, 

370 bulk_writer, 

371 chunk_size=chunk_size, 

372 depth=depth + 1, 

373 ) 

374 num_deleted += 1 

375 bulk_writer.delete(reference) 

376 

377 else: 

378 raise TypeError( 

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

380 ) 

381 

382 if depth == 0: 

383 bulk_writer.close() 

384 

385 return num_deleted 

386 

387 def batch(self) -> WriteBatch: 

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

389 

390 Returns: 

391 :class:`~google.cloud.firestore_v1.batch.WriteBatch`: 

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

393 sending the changes all at once. 

394 """ 

395 return WriteBatch(self) 

396 

397 def transaction( 

398 self, max_attempts: int = MAX_ATTEMPTS, read_only: bool = False 

399 ) -> Transaction: 

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

401 

402 See :class:`~google.cloud.firestore_v1.transaction.Transaction` for 

403 more information on transactions and the constructor arguments. 

404 

405 Args: 

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

407 ``client``) to pass along to the 

408 :class:`~google.cloud.firestore_v1.transaction.Transaction` 

409 constructor. 

410 

411 Returns: 

412 :class:`~google.cloud.firestore_v1.transaction.Transaction`: 

413 A transaction attached to this client. 

414 """ 

415 return Transaction(self, max_attempts=max_attempts, read_only=read_only) 

416 

417 @property 

418 def _pipeline_cls(self): 

419 return Pipeline 

420 

421 def pipeline(self) -> PipelineSource: 

422 return PipelineSource(self)