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

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

63 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 

53 

54if TYPE_CHECKING: # pragma: NO COVER 

55 from google.cloud.firestore_v1.bulk_writer import BulkWriter 

56 import datetime 

57 

58 

59class Client(BaseClient): 

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

61 

62 .. note:: 

63 

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

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

66 

67 Args: 

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

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

70 from the environment. 

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

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

73 back to the default inferred from the environment. 

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

75 If not passed, falls back to :attr:`DEFAULT_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(Client, self).__init__( 

95 project=project, 

96 credentials=credentials, 

97 database=database, 

98 client_info=client_info, 

99 client_options=client_options, 

100 ) 

101 

102 @property 

103 def _firestore_api(self): 

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

105 Returns: 

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

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

108 """ 

109 return self._firestore_api_helper( 

110 firestore_grpc_transport.FirestoreGrpcTransport, 

111 firestore_client.FirestoreClient, 

112 firestore_client, 

113 ) 

114 

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

116 """Get a reference to a collection. 

117 

118 For a top-level collection: 

119 

120 .. code-block:: python 

121 

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

123 

124 For a sub-collection: 

125 

126 .. code-block:: python 

127 

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

129 >>> # is the same as 

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

131 

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

133 

134 Args: 

135 collection_path: Can either be 

136 

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

138 * A tuple of collection path segments 

139 

140 Returns: 

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

142 A reference to a collection in the Firestore database. 

143 """ 

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

145 

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

147 """ 

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

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

150 given collection_id. 

151 

152 .. code-block:: python 

153 

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

155 

156 Args: 

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

158 

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

160 path will be included. Cannot contain a slash. 

161 

162 Returns: 

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

164 The created Query. 

165 """ 

166 return CollectionGroup(self._get_collection_reference(collection_id)) 

167 

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

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

170 

171 For a top-level document: 

172 

173 .. code-block:: python 

174 

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

176 >>> # is the same as 

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

178 

179 For a document in a sub-collection: 

180 

181 .. code-block:: python 

182 

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

184 >>> # is the same as 

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

186 

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

188 

189 Args: 

190 document_path): Can either be 

191 

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

193 * A tuple of document path segments 

194 

195 Returns: 

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

197 A reference to a document in a collection. 

198 """ 

199 return DocumentReference( 

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

201 ) 

202 

203 def get_all( 

204 self, 

205 references: list, 

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

207 transaction: Transaction | None = None, 

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

209 timeout: float | None = None, 

210 *, 

211 read_time: datetime.datetime | None = None, 

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

213 """Retrieve a batch of documents. 

214 

215 .. note:: 

216 

217 Documents returned by this method are not guaranteed to be 

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

219 

220 .. note:: 

221 

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

223 will only return one result. 

224 

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

226 more information on **field paths**. 

227 

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

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

230 allowed). 

231 

232 Args: 

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

234 references to be retrieved. 

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

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

237 projection of document fields in the returned results. If 

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

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

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

241 retrieved in. 

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

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

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

245 system-specified value. 

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

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

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

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

250 

251 Yields: 

252 .DocumentSnapshot: The next document snapshot that fulfills the 

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

254 """ 

255 request, reference_map, kwargs = self._prep_get_all( 

256 references, field_paths, transaction, retry, timeout, read_time 

257 ) 

258 

259 response_iterator = self._firestore_api.batch_get_documents( 

260 request=request, 

261 metadata=self._rpc_metadata, 

262 **kwargs, 

263 ) 

264 

265 for get_doc_response in response_iterator: 

266 yield _parse_batch_get(get_doc_response, reference_map, self) 

267 

268 def collections( 

269 self, 

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

271 timeout: float | None = None, 

272 *, 

273 read_time: datetime.datetime | None = None, 

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

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

276 

277 Args: 

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

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

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

281 system-specified value. 

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

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

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

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

286 

287 Returns: 

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

289 iterator of subcollections of the current document. 

290 """ 

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

292 

293 iterator = self._firestore_api.list_collection_ids( 

294 request=request, 

295 metadata=self._rpc_metadata, 

296 **kwargs, 

297 ) 

298 

299 for collection_id in iterator: 

300 yield self.collection(collection_id) 

301 

302 def recursive_delete( 

303 self, 

304 reference: Union[CollectionReference, DocumentReference], 

305 *, 

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

307 chunk_size: int = 5000, 

308 ) -> int: 

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

310 name. 

311 

312 Passing a CollectionReference leads to each document in the collection 

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

314 

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

316 descendents. 

317 

318 Args: 

319 reference (Union[ 

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

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

322 ]) 

323 The reference to be deleted. 

324 

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

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

327 if you want to override the default throttling behavior. 

328 

329 """ 

330 if bulk_writer is None: 

331 bulk_writer = self.bulk_writer() 

332 

333 return self._recursive_delete( 

334 reference, 

335 bulk_writer, 

336 chunk_size=chunk_size, 

337 ) 

338 

339 def _recursive_delete( 

340 self, 

341 reference: Union[CollectionReference, DocumentReference], 

342 bulk_writer: "BulkWriter", 

343 *, 

344 chunk_size: int = 5000, 

345 depth: int = 0, 

346 ) -> int: 

347 """Recursion helper for `recursive_delete.""" 

348 

349 num_deleted: int = 0 

350 

351 if isinstance(reference, CollectionReference): 

352 chunk: List[DocumentSnapshot] 

353 for chunk in ( 

354 reference.recursive() 

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

356 ._chunkify(chunk_size) 

357 ): 

358 doc_snap: DocumentSnapshot 

359 for doc_snap in chunk: 

360 num_deleted += 1 

361 bulk_writer.delete(doc_snap.reference) 

362 

363 elif isinstance(reference, DocumentReference): 

364 col_ref: CollectionReference 

365 for col_ref in reference.collections(): 

366 num_deleted += self._recursive_delete( 

367 col_ref, 

368 bulk_writer, 

369 chunk_size=chunk_size, 

370 depth=depth + 1, 

371 ) 

372 num_deleted += 1 

373 bulk_writer.delete(reference) 

374 

375 else: 

376 raise TypeError( 

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

378 ) 

379 

380 if depth == 0: 

381 bulk_writer.close() 

382 

383 return num_deleted 

384 

385 def batch(self) -> WriteBatch: 

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

387 

388 Returns: 

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

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

391 sending the changes all at once. 

392 """ 

393 return WriteBatch(self) 

394 

395 def transaction( 

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

397 ) -> Transaction: 

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

399 

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

401 more information on transactions and the constructor arguments. 

402 

403 Args: 

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

405 ``client``) to pass along to the 

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

407 constructor. 

408 

409 Returns: 

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

411 A transaction attached to this client. 

412 """ 

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