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

64 statements  

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

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""" 

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, 

34 _path_helper, 

35) 

36 

37from google.cloud.firestore_v1.query import CollectionGroup 

38from google.cloud.firestore_v1.batch import WriteBatch 

39from google.cloud.firestore_v1.collection import CollectionReference 

40from google.cloud.firestore_v1.document import DocumentReference 

41from google.cloud.firestore_v1.field_path import FieldPath 

42from google.cloud.firestore_v1.transaction import Transaction 

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

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

45 grpc as firestore_grpc_transport, 

46) 

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

48 

49# Types needed only for Type Hints 

50from google.cloud.firestore_v1.base_document import DocumentSnapshot 

51 

52if TYPE_CHECKING: 

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

54 

55 

56class Client(BaseClient): 

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

58 

59 .. note:: 

60 

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

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

63 

64 Args: 

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

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

67 from the environment. 

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

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

70 back to the default inferred from the environment. 

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

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

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

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

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

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

77 or partner tool. 

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

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

80 should be set through client_options. 

81 """ 

82 

83 def __init__( 

84 self, 

85 project=None, 

86 credentials=None, 

87 database=None, 

88 client_info=_CLIENT_INFO, 

89 client_options=None, 

90 ) -> None: 

91 super(Client, self).__init__( 

92 project=project, 

93 credentials=credentials, 

94 database=database, 

95 client_info=client_info, 

96 client_options=client_options, 

97 ) 

98 

99 @property 

100 def _firestore_api(self): 

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

102 Returns: 

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

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

105 """ 

106 return self._firestore_api_helper( 

107 firestore_grpc_transport.FirestoreGrpcTransport, 

108 firestore_client.FirestoreClient, 

109 firestore_client, 

110 ) 

111 

112 @property 

113 def _target(self): 

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

115 Eg. "firestore.googleapis.com" 

116 

117 Returns: 

118 str: The location of the API. 

119 """ 

120 return self._target_helper(firestore_client.FirestoreClient) 

121 

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

123 """Get a reference to a collection. 

124 

125 For a top-level collection: 

126 

127 .. code-block:: python 

128 

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

130 

131 For a sub-collection: 

132 

133 .. code-block:: python 

134 

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

136 >>> # is the same as 

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

138 

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

140 

141 Args: 

142 collection_path: Can either be 

143 

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

145 * A tuple of collection path segments 

146 

147 Returns: 

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

149 A reference to a collection in the Firestore database. 

150 """ 

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

152 

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

154 """ 

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

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

157 given collection_id. 

158 

159 .. code-block:: python 

160 

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

162 

163 Args: 

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

165 

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

167 path will be included. Cannot contain a slash. 

168 

169 Returns: 

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

171 The created Query. 

172 """ 

173 return CollectionGroup(self._get_collection_reference(collection_id)) 

174 

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

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

177 

178 For a top-level document: 

179 

180 .. code-block:: python 

181 

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

183 >>> # is the same as 

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

185 

186 For a document in a sub-collection: 

187 

188 .. code-block:: python 

189 

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

191 >>> # is the same as 

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

193 

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

195 

196 Args: 

197 document_path): Can either be 

198 

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

200 * A tuple of document path segments 

201 

202 Returns: 

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

204 A reference to a document in a collection. 

205 """ 

206 return DocumentReference( 

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

208 ) 

209 

210 def get_all( 

211 self, 

212 references: list, 

213 field_paths: Iterable[str] = None, 

214 transaction: Transaction = None, 

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

216 timeout: float = None, 

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

218 """Retrieve a batch of documents. 

219 

220 .. note:: 

221 

222 Documents returned by this method are not guaranteed to be 

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

224 

225 .. note:: 

226 

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

228 will only return one result. 

229 

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

231 more information on **field paths**. 

232 

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

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

235 allowed). 

236 

237 Args: 

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

239 references to be retrieved. 

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

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

242 projection of document fields in the returned results. If 

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

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

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

246 retrieved in. 

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

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

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

250 system-specified value. 

251 

252 Yields: 

253 .DocumentSnapshot: The next document snapshot that fulfills the 

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

255 """ 

256 request, reference_map, kwargs = self._prep_get_all( 

257 references, field_paths, transaction, retry, timeout 

258 ) 

259 

260 response_iterator = self._firestore_api.batch_get_documents( 

261 request=request, 

262 metadata=self._rpc_metadata, 

263 **kwargs, 

264 ) 

265 

266 for get_doc_response in response_iterator: 

267 yield _parse_batch_get(get_doc_response, reference_map, self) 

268 

269 def collections( 

270 self, 

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

272 timeout: float = None, 

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

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

275 

276 Args: 

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

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

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

280 system-specified value. 

281 

282 Returns: 

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

284 iterator of subcollections of the current document. 

285 """ 

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

287 

288 iterator = self._firestore_api.list_collection_ids( 

289 request=request, 

290 metadata=self._rpc_metadata, 

291 **kwargs, 

292 ) 

293 

294 for collection_id in iterator: 

295 yield self.collection(collection_id) 

296 

297 def recursive_delete( 

298 self, 

299 reference: Union[CollectionReference, DocumentReference], 

300 *, 

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

302 chunk_size: Optional[int] = 5000, 

303 ) -> int: 

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

305 name. 

306 

307 Passing a CollectionReference leads to each document in the collection 

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

309 

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

311 descendents. 

312 

313 Args: 

314 reference (Union[ 

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

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

317 ]) 

318 The reference to be deleted. 

319 

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

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

322 if you want to override the default throttling behavior. 

323 

324 """ 

325 if bulk_writer is None: 

326 bulk_writer = self.bulk_writer() 

327 

328 return self._recursive_delete( 

329 reference, 

330 bulk_writer, 

331 chunk_size=chunk_size, 

332 ) 

333 

334 def _recursive_delete( 

335 self, 

336 reference: Union[CollectionReference, DocumentReference], 

337 bulk_writer: "BulkWriter", 

338 *, 

339 chunk_size: Optional[int] = 5000, 

340 depth: Optional[int] = 0, 

341 ) -> int: 

342 """Recursion helper for `recursive_delete.""" 

343 

344 num_deleted: int = 0 

345 

346 if isinstance(reference, CollectionReference): 

347 chunk: List[DocumentSnapshot] 

348 for chunk in ( 

349 reference.recursive() 

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

351 ._chunkify(chunk_size) 

352 ): 

353 doc_snap: DocumentSnapshot 

354 for doc_snap in chunk: 

355 num_deleted += 1 

356 bulk_writer.delete(doc_snap.reference) 

357 

358 elif isinstance(reference, DocumentReference): 

359 col_ref: CollectionReference 

360 for col_ref in reference.collections(): 

361 num_deleted += self._recursive_delete( 

362 col_ref, 

363 bulk_writer, 

364 chunk_size=chunk_size, 

365 depth=depth + 1, 

366 ) 

367 num_deleted += 1 

368 bulk_writer.delete(reference) 

369 

370 else: 

371 raise TypeError( 

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

373 ) 

374 

375 if depth == 0: 

376 bulk_writer.close() 

377 

378 return num_deleted 

379 

380 def batch(self) -> WriteBatch: 

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

382 

383 Returns: 

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

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

386 sending the changes all at once. 

387 """ 

388 return WriteBatch(self) 

389 

390 def transaction(self, **kwargs) -> Transaction: 

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

392 

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

394 more information on transactions and the constructor arguments. 

395 

396 Args: 

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

398 ``client``) to pass along to the 

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

400 constructor. 

401 

402 Returns: 

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

404 A transaction attached to this client. 

405 """ 

406 return Transaction(self, **kwargs)