pangea-sdk 4.4.0__py3-none-any.whl → 5.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1256 @@
1
+ # Copyright 2022 Pangea Cyber Corporation
2
+ # Author: Pangea Cyber Corporation
3
+ from __future__ import annotations
4
+
5
+ import enum
6
+ import io
7
+ from typing import Dict, List, NewType, Optional, Tuple, Union
8
+
9
+ from pangea.response import APIRequestModel, PangeaResponse, PangeaResponseResult, TransferMethod
10
+ from pangea.services.base import ServiceBase
11
+ from pangea.services.share.file_format import FileFormat
12
+ from pangea.utils import get_file_size, get_file_upload_params
13
+
14
+ Metadata = NewType("Metadata", Dict[str, str])
15
+ Tags = NewType("Tags", List[str])
16
+
17
+
18
+ class ItemOrder(str, enum.Enum):
19
+ ASC = "asc"
20
+ DESC = "desc"
21
+
22
+ def __str__(self):
23
+ return str(self.value)
24
+
25
+ def __repr__(self):
26
+ return str(self.value)
27
+
28
+
29
+ class ArchiveFormat(str, enum.Enum):
30
+ TAR = "tar"
31
+ ZIP = "zip"
32
+
33
+ def __str__(self):
34
+ return str(self.value)
35
+
36
+ def __repr__(self):
37
+ return str(self.value)
38
+
39
+
40
+ class LinkType(str, enum.Enum):
41
+ UPLOAD = "upload"
42
+ DOWNLOAD = "download"
43
+ EDITOR = "editor"
44
+
45
+ def __str__(self):
46
+ return str(self.value)
47
+
48
+ def __repr__(self):
49
+ return str(self.value)
50
+
51
+
52
+ class AuthenticatorType(str, enum.Enum):
53
+ EMAIL_OTP = "email_otp"
54
+ PASSWORD = "password"
55
+ SMS_OTP = "sms_otp"
56
+ SOCIAL = "social"
57
+
58
+ def __str__(self):
59
+ return str(self.value)
60
+
61
+ def __repr__(self):
62
+ return str(self.value)
63
+
64
+
65
+ class ItemOrderBy(str, enum.Enum):
66
+ ID = "id"
67
+ CREATED_AT = "created_at"
68
+ NAME = "name"
69
+ PARENT_ID = "parent_id"
70
+ TYPE = "type"
71
+ UPDATED_AT = "updated_at"
72
+
73
+ def __str__(self):
74
+ return str(self.value)
75
+
76
+ def __repr__(self):
77
+ return str(self.value)
78
+
79
+
80
+ class ShareLinkOrderBy(str, enum.Enum):
81
+ ID = "id"
82
+ BUCKET_ID = "bucket_id"
83
+ TARGET = "target"
84
+ LINK_TYPE = "link_type"
85
+ ACCESS_COUNT = "access_count"
86
+ MAX_ACCESS_COUNT = "max_access_count"
87
+ CREATED_AT = "created_at"
88
+ EXPIRES_AT = "expires_at"
89
+ LAST_ACCESSED_AT = "last_accessed_at"
90
+ LINK = "link"
91
+
92
+ def __str__(self):
93
+ return str(self.value)
94
+
95
+ def __repr__(self):
96
+ return str(self.value)
97
+
98
+
99
+ class DeleteRequest(APIRequestModel):
100
+ id: Optional[str] = None
101
+ """The ID of the object to delete."""
102
+
103
+ force: Optional[bool] = None
104
+ """If true, delete a folder even if it's not empty. Deletes the contents of folder as well."""
105
+
106
+ bucket_id: Optional[str] = None
107
+ """The bucket to use, if not the default."""
108
+
109
+
110
+ class ItemData(PangeaResponseResult):
111
+ billable_size: Optional[int] = None
112
+ """The number of billable bytes (includes Metadata, Tags, etc.) for the object."""
113
+
114
+ created_at: str
115
+ """The date and time the object was created."""
116
+
117
+ id: str
118
+ """The ID of a stored object."""
119
+
120
+ md5: Optional[str] = None
121
+ """The MD5 hash of the file contents. Cannot be written to."""
122
+
123
+ metadata: Optional[Metadata] = None
124
+ """A set of string-based key/value pairs used to provide additional data about an object."""
125
+
126
+ metadata_protected: Optional[Metadata] = None
127
+ """Protected (read-only) metadata."""
128
+
129
+ sha256: Optional[str] = None
130
+ """The SHA256 hash of the file contents. Cannot be written to."""
131
+
132
+ sha512: Optional[str] = None
133
+ """The SHA512 hash of the file contents. Cannot be written to."""
134
+
135
+ size: Optional[int] = None
136
+ """The size of the object in bytes."""
137
+
138
+ tags: Optional[Tags] = None
139
+ """A list of user-defined tags."""
140
+
141
+ tags_protected: Optional[Tags] = None
142
+ """Protected (read-only) flags."""
143
+
144
+ type: str
145
+ """The type of the item (file or dir). Cannot be written to."""
146
+
147
+ updated_at: str
148
+ """The date and time the object was last updated."""
149
+
150
+ name: str
151
+ """The name of the object."""
152
+
153
+ folder: str
154
+ """The full path to the folder the object is stored in."""
155
+
156
+ parent_id: Optional[str] = None
157
+ """The parent ID (a folder). Blanks means the root folder."""
158
+
159
+ external_bucket_key: Optional[str] = None
160
+ """The key in the external bucket that contains this file."""
161
+
162
+
163
+ class DeleteResult(PangeaResponseResult):
164
+ count: int
165
+ """Number of objects deleted."""
166
+
167
+
168
+ class FolderCreateRequest(APIRequestModel):
169
+ name: Optional[str] = None
170
+ """The name of an object."""
171
+
172
+ metadata: Optional[Metadata] = None
173
+ """A set of string-based key/value pairs used to provide additional data about an object."""
174
+
175
+ parent_id: Optional[str] = None
176
+ """The ID of a stored object."""
177
+
178
+ folder: Optional[str] = None
179
+ """The folder to place the folder in. Must match `parent_id` if also set."""
180
+
181
+ tags: Optional[Tags] = None
182
+ """A list of user-defined tags"""
183
+
184
+ bucket_id: Optional[str] = None
185
+ """The bucket to use, if not the default."""
186
+
187
+
188
+ class FolderCreateResult(PangeaResponseResult):
189
+ object: ItemData
190
+ """Information on the created folder."""
191
+
192
+
193
+ class GetRequest(APIRequestModel):
194
+ id: Optional[str] = None
195
+ """The ID of the object to retrieve."""
196
+
197
+ password: Optional[str] = None
198
+ """If the file was protected with a password, the password to decrypt with."""
199
+
200
+ transfer_method: Optional[TransferMethod] = None
201
+ """The requested transfer method for the file data."""
202
+
203
+ bucket_id: Optional[str] = None
204
+ """The bucket to use, if not the default."""
205
+
206
+
207
+ class GetResult(PangeaResponseResult):
208
+ object: ItemData
209
+ """File information."""
210
+
211
+ dest_url: Optional[str] = None
212
+ """A URL where the file can be downloaded from. (transfer_method: dest-url)"""
213
+
214
+
215
+ class PutRequest(APIRequestModel):
216
+ transfer_method: Optional[TransferMethod] = None
217
+ """The transfer method used to upload the file data."""
218
+
219
+ bucket_id: Optional[str] = None
220
+ """The bucket to use, if not the default."""
221
+
222
+ size: Optional[int] = None
223
+ """The size (in bytes) of the file. If the upload doesn't match, the call will fail."""
224
+
225
+ crc32c: Optional[str] = None
226
+ """The hexadecimal-encoded CRC32C hash of the file data, which will be verified by the server if provided."""
227
+
228
+ sha256: Optional[str] = None
229
+ """The SHA256 hash of the file data, which will be verified by the server if provided."""
230
+
231
+ md5: Optional[str] = None
232
+ """The hexadecimal-encoded MD5 hash of the file data, which will be verified by the server if provided."""
233
+
234
+ name: Optional[str] = None
235
+ """The name of the object to store."""
236
+
237
+ format: Optional[FileFormat] = None
238
+ """The format of the file, which will be verified by the server if provided. Uploads not matching the supplied format will be rejected."""
239
+
240
+ metadata: Optional[Metadata] = None
241
+ """A set of string-based key/value pairs used to provide additional data about an object."""
242
+
243
+ mimetype: Optional[str] = None
244
+ """The MIME type of the file, which will be verified by the server if provided. Uploads not matching the supplied MIME type will be rejected."""
245
+
246
+ parent_id: Optional[str] = None
247
+ """The parent ID of the object (a folder). Leave blank to keep in the root folder."""
248
+
249
+ folder: Optional[str] = None
250
+ """The path to the parent folder. Leave blank for the root folder. Path must resolve to `parent_id` if also set."""
251
+
252
+ password: Optional[str] = None
253
+ """An optional password to protect the file with. Downloading the file will require this password."""
254
+
255
+ password_algorithm: Optional[str] = None
256
+ """An optional password algorithm to protect the file with. See symmetric vault password_algorithm."""
257
+
258
+ sha1: Optional[str] = None
259
+ """The hexadecimal-encoded SHA1 hash of the file data, which will be verified by the server if provided."""
260
+
261
+ sha512: Optional[str] = None
262
+ """The hexadecimal-encoded SHA512 hash of the file data, which will be verified by the server if provided."""
263
+
264
+ tags: Optional[Tags] = None
265
+ """A list of user-defined tags"""
266
+
267
+
268
+ class PutResult(PangeaResponseResult):
269
+ object: ItemData
270
+
271
+
272
+ class UpdateRequest(APIRequestModel):
273
+ id: Optional[str]
274
+ """An identifier for the file to update."""
275
+
276
+ folder: Optional[str] = None
277
+ """
278
+ Set the parent (folder). Leave blank for the root folder. Path must resolve
279
+ to `parent_id` if also set.
280
+ """
281
+
282
+ add_metadata: Optional[Metadata] = None
283
+ """A list of Metadata key/values to set in the object. If a provided key exists, the value will be replaced."""
284
+
285
+ add_password: Optional[str] = None
286
+ """Protect the file with the supplied password."""
287
+
288
+ add_password_algorithm: Optional[str] = None
289
+ """The algorithm to use to password protect the file."""
290
+
291
+ add_tags: Optional[Tags] = None
292
+ """A list of Tags to add. It is not an error to provide a tag which already exists."""
293
+
294
+ name: Optional[str] = None
295
+ """Sets the object's Name."""
296
+
297
+ metadata: Optional[Metadata] = None
298
+ """Set the object's metadata."""
299
+
300
+ remove_metadata: Optional[Metadata] = None
301
+ """A list of metadata key/values to remove in the object. It is not an error for a provided key to not exist. If a provided key exists but doesn't match the provided value, it will not be removed."""
302
+
303
+ remove_password: Optional[str] = None
304
+ """Remove the supplied password from the file."""
305
+
306
+ remove_tags: Optional[Tags] = None
307
+ """A list of tags to remove. It is not an error to provide a tag which is not present."""
308
+
309
+ parent_id: Optional[str] = None
310
+ """Set the parent (folder) of the object. Can be an empty string for the root folder."""
311
+
312
+ tags: Optional[Tags] = None
313
+ """Set the object's tags."""
314
+
315
+ updated_at: Optional[str] = None
316
+ """The date and time the object was last updated. If included, the update will fail if this doesn't match the date and time of the last update for the object."""
317
+
318
+ bucket_id: Optional[str] = None
319
+ """The bucket to use, if not the default."""
320
+
321
+
322
+ class UpdateResult(PangeaResponseResult):
323
+ object: ItemData
324
+
325
+
326
+ class FilterList(APIRequestModel):
327
+ created_at: Optional[str] = None
328
+ """Only records where created_at equals this value."""
329
+
330
+ created_at__gt: Optional[str] = None
331
+ """Only records where created_at is greater than this value."""
332
+
333
+ created_at__gte: Optional[str] = None
334
+ """Only records where created_at is greater than or equal to this value."""
335
+
336
+ created_at__lt: Optional[str] = None
337
+ """Only records where created_at is less than this value."""
338
+
339
+ created_at__lte: Optional[str] = None
340
+ """Only records where created_at is less than or equal to this value."""
341
+
342
+ folder: Optional[str] = None
343
+ """Only records where the object exists in the supplied parent folder path name."""
344
+
345
+ id: Optional[str] = None
346
+ """Only records where id equals this value."""
347
+
348
+ id__in: Optional[List[str]] = None
349
+ """Only records where id equals one of the provided substrings."""
350
+
351
+ name: Optional[str] = None
352
+ """Only records where name equals this value."""
353
+
354
+ name__contains: Optional[List[str]] = None
355
+ """Only records where name includes each substring."""
356
+
357
+ name__in: Optional[List[str]] = None
358
+ """Only records where name equals one of the provided substrings."""
359
+
360
+ parent_id: Optional[str] = None
361
+ """Only records where parent_id equals this value."""
362
+
363
+ parent_id__in: Optional[List[str]] = None
364
+ """Only records where parent_id equals one of the provided substrings."""
365
+
366
+ size: Optional[int] = None
367
+ """Only records where size equals this value."""
368
+
369
+ size__gt: Optional[int] = None
370
+ """Only records where size is greater than this value."""
371
+
372
+ size__gte: Optional[int] = None
373
+ """Only records where size is greater than or equal to this value."""
374
+
375
+ size__lt: Optional[int] = None
376
+ """Only records where size is less than to this value."""
377
+
378
+ size__lte: Optional[int] = None
379
+ """Only records where size is less than or equal to this value."""
380
+
381
+ tags: Optional[List[str]] = None
382
+ """A list of tags that all must be present."""
383
+
384
+ type: Optional[str] = None
385
+ """Only records where type equals this value."""
386
+
387
+ type__contains: Optional[List[str]] = None
388
+ """Only records where type includes each substring."""
389
+
390
+ type__in: Optional[List[str]] = None
391
+ """Only records where type equals one of the provided substrings."""
392
+
393
+ updated_at: Optional[str] = None
394
+ """Only records where updated_at equals this value."""
395
+
396
+ updated_at__gt: Optional[str] = None
397
+ """Only records where updated_at is greater than this value."""
398
+
399
+ updated_at__gte: Optional[str] = None
400
+ """Only records where updated_at is greater than or equal to this value."""
401
+
402
+ updated_at__lt: Optional[str] = None
403
+ """Only records where updated_at is less than this value."""
404
+
405
+ updated_at__lte: Optional[str] = None
406
+ """Only records where updated_at is less than or equal to this value."""
407
+
408
+
409
+ class ListRequest(APIRequestModel):
410
+ filter: Optional[Union[Dict[str, str], FilterList]] = None
411
+ last: Optional[str] = None
412
+ """Reflected value from a previous response to obtain the next page of results."""
413
+
414
+ order: Optional[ItemOrder] = None
415
+ """Order results asc(ending) or desc(ending)."""
416
+
417
+ order_by: Optional[ItemOrderBy] = None
418
+ """Which field to order results by."""
419
+
420
+ size: Optional[int] = None
421
+ """Maximum results to include in the response."""
422
+
423
+ include_external_bucket_key: bool = False
424
+ """If true, include the `external_bucket_key` in results."""
425
+
426
+ bucket_id: Optional[str] = None
427
+ """The bucket to use, if not the default."""
428
+
429
+
430
+ class ListResult(PangeaResponseResult):
431
+ count: int
432
+ """The total number of objects matched by the list request."""
433
+
434
+ last: Optional[str] = None
435
+ """Used to fetch the next page of the current listing when provided in a repeated request's last parameter."""
436
+
437
+ objects: List[ItemData]
438
+
439
+
440
+ class GetArchiveRequest(APIRequestModel):
441
+ ids: List[str] = []
442
+ """The IDs of the objects to include in the archive. Folders include all children."""
443
+
444
+ format: Optional[ArchiveFormat] = None
445
+ """The format to use to build the archive."""
446
+
447
+ transfer_method: Optional[TransferMethod] = None
448
+ """The requested transfer method for the file data."""
449
+
450
+ bucket_id: Optional[str] = None
451
+ """The bucket to use, if not the default."""
452
+
453
+
454
+ class GetArchiveResult(PangeaResponseResult):
455
+ count: int
456
+ """Number of objects included in the archive."""
457
+
458
+ dest_url: Optional[str] = None
459
+ """A location where the archive can be downloaded from. (transfer_method: dest-url)"""
460
+
461
+ objects: List[ItemData] = []
462
+ """A list of all objects included in the archive."""
463
+
464
+
465
+ class Authenticator(PangeaResponseResult):
466
+ auth_type: AuthenticatorType
467
+ """An authentication mechanism."""
468
+
469
+ auth_context: str
470
+ """An email address, a phone number or a password to access share link."""
471
+
472
+
473
+ class ShareLinkItemBase(PangeaResponseResult):
474
+ targets: List[str] = []
475
+ """List of storage IDs."""
476
+
477
+ link_type: Optional[LinkType] = None
478
+ """Type of link."""
479
+
480
+ expires_at: Optional[str] = None
481
+ """The date and time the share link expires."""
482
+
483
+ max_access_count: Optional[int] = None
484
+ """The maximum number of times a user can be authenticated to access the share link."""
485
+
486
+ authenticators: Optional[List[Authenticator]] = None
487
+ """A list of authenticators."""
488
+
489
+ title: Optional[str] = None
490
+ """An optional title to use in accessing shares."""
491
+
492
+ message: Optional[str] = None
493
+ """An optional message to use in accessing shares."""
494
+
495
+ notify_email: Optional[str] = None
496
+ """An email address"""
497
+
498
+ tags: Optional[Tags] = None
499
+ """A list of user-defined tags"""
500
+
501
+
502
+ class ShareLinkCreateItem(ShareLinkItemBase):
503
+ pass
504
+
505
+
506
+ class ShareLinkCreateRequest(APIRequestModel):
507
+ links: List[ShareLinkCreateItem] = []
508
+ bucket_id: Optional[str] = None
509
+ """The bucket to use, if not the default."""
510
+
511
+
512
+ class ShareLinkItem(ShareLinkItemBase):
513
+ id: str
514
+ """The ID of a share link."""
515
+
516
+ access_count: int
517
+ """The number of times a user has authenticated to access the share link."""
518
+
519
+ created_at: str
520
+ """The date and time the share link was created."""
521
+
522
+ last_accessed_at: Optional[str] = None
523
+ """The date and time the share link was last accessed."""
524
+
525
+ link: Optional[str] = None
526
+ """A URL to access the file/folders shared with a link."""
527
+
528
+ bucket_id: str
529
+ """The ID of a share bucket resource."""
530
+
531
+
532
+ class ShareLinkCreateResult(PangeaResponseResult):
533
+ share_link_objects: List[ShareLinkItem] = []
534
+
535
+
536
+ class ShareLinkGetRequest(APIRequestModel):
537
+ id: str
538
+ """The ID of a share link."""
539
+
540
+
541
+ class ShareLinkGetResult(PangeaResponseResult):
542
+ share_link_object: ShareLinkItem
543
+
544
+
545
+ class FilterShareLinkList(APIRequestModel):
546
+ id: Optional[str] = None
547
+ id__contains: Optional[List[str]] = None
548
+ id__in: Optional[List[str]] = None
549
+ target: Optional[str] = None
550
+ target__contains: Optional[List[str]] = None
551
+ target__in: Optional[List[str]] = None
552
+ link_type: Optional[str] = None
553
+ link_type__contains: Optional[List[str]] = None
554
+ link_type__in: Optional[List[str]] = None
555
+ access_count: Optional[int] = None
556
+ access_count__gt: Optional[int] = None
557
+ access_count__gte: Optional[int] = None
558
+ access_count__lt: Optional[int] = None
559
+ access_count__lte: Optional[int] = None
560
+ max_access_count: Optional[int] = None
561
+ max_access_count__gt: Optional[int] = None
562
+ max_access_count__gte: Optional[int] = None
563
+ max_access_count__lt: Optional[int] = None
564
+ max_access_count__lte: Optional[int] = None
565
+ created_at: Optional[str] = None
566
+ created_at__gt: Optional[str] = None
567
+ created_at__gte: Optional[str] = None
568
+ created_at__lt: Optional[str] = None
569
+ created_at__lte: Optional[str] = None
570
+ expires_at: Optional[str] = None
571
+ expires_at__gt: Optional[str] = None
572
+ expires_at__gte: Optional[str] = None
573
+ expires_at__lt: Optional[str] = None
574
+ expires_at__lte: Optional[str] = None
575
+ last_accessed_at: Optional[str] = None
576
+ last_accessed_at__gt: Optional[str] = None
577
+ last_accessed_at__gte: Optional[str] = None
578
+ last_accessed_at__lt: Optional[str] = None
579
+ last_accessed_at__lte: Optional[str] = None
580
+ link: Optional[str] = None
581
+ link__contains: Optional[List[str]] = None
582
+ link__in: Optional[List[str]] = None
583
+ title: Optional[str] = None
584
+ title__contains: Optional[List[str]] = None
585
+ title__in: Optional[List[str]] = None
586
+ message: Optional[str] = None
587
+ message__contains: Optional[List[str]] = None
588
+ message__in: Optional[List[str]] = None
589
+ notify_email: Optional[str] = None
590
+ notify_email__contains: Optional[List[str]] = None
591
+ notify_email__in: Optional[List[str]] = None
592
+ tags: Optional[List[str]] = None
593
+
594
+
595
+ class ShareLinkListRequest(APIRequestModel):
596
+ filter: Optional[Union[FilterShareLinkList, Dict[str, str]]] = None
597
+
598
+ last: Optional[str] = None
599
+ """Reflected value from a previous response to obtain the next page of results."""
600
+
601
+ order: Optional[ItemOrder] = None
602
+ """Order results asc(ending) or desc(ending)."""
603
+
604
+ order_by: Optional[ShareLinkOrderBy] = None
605
+ """Which field to order results by."""
606
+
607
+ size: Optional[int] = None
608
+ """Maximum results to include in the response."""
609
+
610
+ bucket_id: Optional[str] = None
611
+ """The bucket to use, if not the default."""
612
+
613
+
614
+ class ShareLinkListResult(PangeaResponseResult):
615
+ count: int
616
+ """The total number of share links matched by the list request."""
617
+
618
+ last: Optional[str] = None
619
+ """Used to fetch the next page of the current listing when provided in a repeated request's last parameter."""
620
+
621
+ share_link_objects: List[ShareLinkItem] = []
622
+
623
+
624
+ class ShareLinkDeleteRequest(APIRequestModel):
625
+ ids: List[str]
626
+ bucket_id: Optional[str] = None
627
+ """The bucket to use, if not the default."""
628
+
629
+
630
+ class ShareLinkDeleteResult(PangeaResponseResult):
631
+ share_link_objects: List[ShareLinkItem] = []
632
+
633
+
634
+ class ShareLinkSendItem(APIRequestModel):
635
+ id: str
636
+ email: str
637
+
638
+
639
+ class ShareLinkSendRequest(APIRequestModel):
640
+ links: List[ShareLinkSendItem]
641
+
642
+ sender_email: str
643
+ """An email address."""
644
+
645
+ sender_name: Optional[str]
646
+ """The sender name information. Can be sender's full name for example."""
647
+
648
+
649
+ class ShareLinkSendResult(PangeaResponseResult):
650
+ share_link_objects: List[ShareLinkItem]
651
+
652
+
653
+ class Bucket(PangeaResponseResult):
654
+ id: str
655
+ """The ID of a share bucket resource."""
656
+
657
+ default: bool
658
+ """If true, is the default bucket."""
659
+
660
+ name: str
661
+ """The bucket's friendly name."""
662
+
663
+ transfer_methods: List[TransferMethod]
664
+
665
+
666
+ class BucketsResult(PangeaResponseResult):
667
+ buckets: List[Bucket]
668
+ """A list of available buckets."""
669
+
670
+
671
+ class Share(ServiceBase):
672
+ """Share service client."""
673
+
674
+ service_name = "share"
675
+
676
+ def buckets(self) -> PangeaResponse[BucketsResult]:
677
+ """
678
+ Buckets
679
+
680
+ Get information on the accessible buckets.
681
+
682
+ OperationId: share_post_v1_buckets
683
+
684
+ Returns:
685
+ A PangeaResponse. Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/share).
686
+
687
+ Examples:
688
+ response = share.buckets()
689
+ """
690
+ return self.request.post("v1/buckets", BucketsResult)
691
+
692
+ def delete(
693
+ self,
694
+ id: Optional[str] = None,
695
+ force: Optional[bool] = None,
696
+ bucket_id: Optional[str] = None,
697
+ ) -> PangeaResponse[DeleteResult]:
698
+ """
699
+ Delete
700
+
701
+ Delete object by ID or path. If both are supplied, the path must match
702
+ that of the object represented by the ID.
703
+
704
+ OperationId: share_post_v1_delete
705
+
706
+ Args:
707
+ id (str, optional): The ID of the object to delete.
708
+ force (bool, optional): If true, delete a folder even if it's not empty.
709
+ bucket_id (str, optional): The bucket to use, if not the default.
710
+
711
+ Returns:
712
+ A PangeaResponse. Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/share).
713
+
714
+ Examples:
715
+ response = share.delete(id="pos_3djfmzg2db4c6donarecbyv5begtj2bm")
716
+ """
717
+ input = DeleteRequest(id=id, force=force, bucket_id=bucket_id)
718
+ return self.request.post("v1/delete", DeleteResult, data=input.model_dump(exclude_none=True))
719
+
720
+ def folder_create(
721
+ self,
722
+ name: Optional[str] = None,
723
+ metadata: Optional[Metadata] = None,
724
+ parent_id: Optional[str] = None,
725
+ folder: Optional[str] = None,
726
+ tags: Optional[Tags] = None,
727
+ bucket_id: Optional[str] = None,
728
+ ) -> PangeaResponse[FolderCreateResult]:
729
+ """
730
+ Create a folder
731
+
732
+ Create a folder, either by name or path and parent_id.
733
+
734
+ OperationId: share_post_v1_folder_create
735
+
736
+ Args:
737
+ name (str, optional): The name of an object.
738
+ metadata (Metadata, optional): A set of string-based key/value pairs used to provide additional data about an object.
739
+ parent_id (str, optional): The ID of a stored object.
740
+ folder (str, optional): The folder to place the folder in. Must
741
+ match `parent_id` if also set.
742
+ tags (Tags, optional): A list of user-defined tags.
743
+ bucket_id (str, optional): The bucket to use, if not the default.
744
+
745
+ Returns:
746
+ A PangeaResponse. Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/share).
747
+
748
+ Examples:
749
+ response = share.folder_create(
750
+ metadata={
751
+ "created_by": "jim",
752
+ "priority": "medium",
753
+ },
754
+ parent_id="pos_3djfmzg2db4c6donarecbyv5begtj2bm",
755
+ folder="/",
756
+ tags=["irs_2023", "personal"],
757
+ )
758
+ """
759
+ input = FolderCreateRequest(
760
+ name=name, metadata=metadata, parent_id=parent_id, folder=folder, tags=tags, bucket_id=bucket_id
761
+ )
762
+ return self.request.post("v1/folder/create", FolderCreateResult, data=input.model_dump(exclude_none=True))
763
+
764
+ def get(
765
+ self,
766
+ id: Optional[str] = None,
767
+ transfer_method: Optional[TransferMethod] = None,
768
+ bucket_id: Optional[str] = None,
769
+ password: Optional[str] = None,
770
+ ) -> PangeaResponse[GetResult]:
771
+ """
772
+ Get an object
773
+
774
+ Get object. If both ID and Path are supplied, the call will fail if the
775
+ target object doesn't match both properties.
776
+
777
+ OperationId: share_post_v1_get
778
+
779
+ Args:
780
+ id (str, optional): The ID of the object to retrieve.
781
+ transfer_method (TransferMethod, optional): The requested transfer method for the file data.
782
+ bucket_id (str, optional): The bucket to use, if not the default.
783
+ password (str, optional): If the file was protected with a password, the password to decrypt with.
784
+
785
+ Returns:
786
+ A PangeaResponse. Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/share).
787
+
788
+ Examples:
789
+ response = share.get(
790
+ id="pos_3djfmzg2db4c6donarecbyv5begtj2bm",
791
+ folder="/",
792
+ )
793
+ """
794
+ input = GetRequest(id=id, transfer_method=transfer_method, bucket_id=bucket_id, password=password)
795
+ return self.request.post("v1/get", GetResult, data=input.model_dump(exclude_none=True))
796
+
797
+ def get_archive(
798
+ self,
799
+ ids: List[str] = [],
800
+ format: Optional[ArchiveFormat] = None,
801
+ transfer_method: Optional[TransferMethod] = None,
802
+ bucket_id: Optional[str] = None,
803
+ ) -> PangeaResponse[GetArchiveResult]:
804
+ """
805
+ Get archive
806
+
807
+ Get an archive file of multiple objects.
808
+
809
+ OperationId: share_post_v1_get_archive
810
+
811
+ Args:
812
+ ids (List[str]): The IDs of the objects to include in the archive. Folders include all children.
813
+ format (ArchiveFormat, optional): The format to use for the built archive.
814
+ transfer_method (TransferMethod, optional): The requested transfer method for the file data.
815
+ bucket_id (str, optional): The bucket to use, if not the default.
816
+
817
+ Returns:
818
+ A PangeaResponse. Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/share).
819
+
820
+ Examples:
821
+ response = share.get_archive(
822
+ ids=["pos_3djfmzg2db4c6donarecbyv5begtj2bm"],
823
+ )
824
+ """
825
+ if (
826
+ transfer_method is not None
827
+ and transfer_method != TransferMethod.DEST_URL
828
+ and transfer_method != TransferMethod.MULTIPART
829
+ ):
830
+ raise ValueError(f"Only {TransferMethod.DEST_URL} and {TransferMethod.MULTIPART} are supported")
831
+
832
+ input = GetArchiveRequest(ids=ids, format=format, transfer_method=transfer_method, bucket_id=bucket_id)
833
+ return self.request.post("v1/get_archive", GetArchiveResult, data=input.model_dump(exclude_none=True))
834
+
835
+ def list(
836
+ self,
837
+ filter: Optional[Union[Dict[str, str], FilterList]] = None,
838
+ last: Optional[str] = None,
839
+ order: Optional[ItemOrder] = None,
840
+ order_by: Optional[ItemOrderBy] = None,
841
+ size: Optional[int] = None,
842
+ bucket_id: Optional[str] = None,
843
+ ) -> PangeaResponse[ListResult]:
844
+ """
845
+ List
846
+
847
+ List or filter/search records.
848
+
849
+ OperationId: share_post_v1_list
850
+
851
+ Args:
852
+ filter (Union[Dict[str, str], FilterList], optional):
853
+ last (str, optional): Reflected value from a previous response to obtain the next page of results.
854
+ order (ItemOrder, optional): Order results asc(ending) or desc(ending).
855
+ order_by (ItemOrderBy, optional): Which field to order results by.
856
+ size (int, optional): Maximum results to include in the response.
857
+ bucket_id (str, optional): The bucket to use, if not the default.
858
+
859
+ Returns:
860
+ A PangeaResponse. Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/share).
861
+
862
+ Examples:
863
+ response = share.list()
864
+ """
865
+ input = ListRequest(filter=filter, last=last, order=order, order_by=order_by, size=size, bucket_id=bucket_id)
866
+ return self.request.post("v1/list", ListResult, data=input.model_dump(exclude_none=True))
867
+
868
+ def put(
869
+ self,
870
+ file: io.BufferedReader,
871
+ name: Optional[str] = None,
872
+ folder: Optional[str] = None,
873
+ format: Optional[FileFormat] = None,
874
+ metadata: Optional[Metadata] = None,
875
+ mimetype: Optional[str] = None,
876
+ parent_id: Optional[str] = None,
877
+ tags: Optional[Tags] = None,
878
+ transfer_method: Optional[TransferMethod] = TransferMethod.POST_URL,
879
+ crc32c: Optional[str] = None,
880
+ md5: Optional[str] = None,
881
+ sha1: Optional[str] = None,
882
+ sha256: Optional[str] = None,
883
+ sha512: Optional[str] = None,
884
+ size: Optional[int] = None,
885
+ bucket_id: Optional[str] = None,
886
+ password: Optional[str] = None,
887
+ password_algorithm: Optional[str] = None,
888
+ ) -> PangeaResponse[PutResult]:
889
+ """
890
+ Upload a file
891
+
892
+ Upload a file.
893
+
894
+ OperationId: share_post_v1_put
895
+
896
+ Args:
897
+ file (io.BufferedReader):
898
+ name (str, optional): The name of the object to store.
899
+ folder (str, optional): The path to the parent folder. Leave blank
900
+ for the root folder. Path must resolve to `parent_id` if also set.
901
+ format (FileFormat, optional): The format of the file, which will be verified by the server if provided. Uploads not matching the supplied format will be rejected.
902
+ metadata (Metadata, optional): A set of string-based key/value pairs used to provide additional data about an object.
903
+ mimetype (str, optional): The MIME type of the file, which will be verified by the server if provided. Uploads not matching the supplied MIME type will be rejected.
904
+ parent_id (str, optional): The parent ID of the object (a folder). Leave blank to keep in the root folder.
905
+ tags (Tags, optional): A list of user-defined tags.
906
+ transfer_method (TransferMethod, optional): The transfer method used to upload the file data.
907
+ crc32c (str, optional): The hexadecimal-encoded CRC32C hash of the file data, which will be verified by the server if provided.
908
+ md5 (str, optional): The hexadecimal-encoded MD5 hash of the file data, which will be verified by the server if provided.
909
+ sha1 (str, optional): The hexadecimal-encoded SHA1 hash of the file data, which will be verified by the server if provided.
910
+ sha256 (str, optional): The SHA256 hash of the file data, which will be verified by the server if provided.
911
+ sha512 (str, optional): The hexadecimal-encoded SHA512 hash of the file data, which will be verified by the server if provided.
912
+ size (str, optional): The size (in bytes) of the file. If the upload doesn't match, the call will fail.
913
+ bucket_id (str, optional): The bucket to use, if not the default.
914
+ password (str, optional): An optional password to protect the file with. Downloading the file will require this password.
915
+ password_algorithm (str, optional): An optional password algorithm to protect the file with. See symmetric vault password_algorithm.
916
+ Returns:
917
+ A PangeaResponse. Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/share).
918
+
919
+ Examples:
920
+ try:
921
+ with open("./path/to/file.pdf", "rb") as f:
922
+ response = share.put(file=f)
923
+ print(f"Response: {response.result}")
924
+ except pe.PangeaAPIException as e:
925
+ print(f"Request Error: {e.response.summary}")
926
+ for err in e.errors:
927
+ print(f"\\t{err.detail} \\n")
928
+ """
929
+ files: List[Tuple] = [("upload", (name, file, "application/octet-stream"))]
930
+
931
+ if transfer_method == TransferMethod.POST_URL:
932
+ params = get_file_upload_params(file)
933
+ crc32c = params.crc_hex
934
+ sha256 = params.sha256_hex
935
+ size = params.size
936
+ elif size is None and get_file_size(file=file) == 0:
937
+ # Needed to upload zero byte files
938
+ size = 0
939
+
940
+ input = PutRequest(
941
+ name=name,
942
+ format=format,
943
+ metadata=metadata,
944
+ mimetype=mimetype,
945
+ parent_id=parent_id,
946
+ folder=folder,
947
+ tags=tags,
948
+ transfer_method=transfer_method,
949
+ crc32c=crc32c,
950
+ md5=md5,
951
+ sha1=sha1,
952
+ sha256=sha256,
953
+ sha512=sha512,
954
+ size=size,
955
+ bucket_id=bucket_id,
956
+ password=password,
957
+ password_algorithm=password_algorithm,
958
+ )
959
+ data = input.model_dump(exclude_none=True)
960
+ return self.request.post("v1/put", PutResult, data=data, files=files)
961
+
962
+ def request_upload_url(
963
+ self,
964
+ name: Optional[str] = None,
965
+ folder: Optional[str] = None,
966
+ format: Optional[FileFormat] = None,
967
+ metadata: Optional[Metadata] = None,
968
+ mimetype: Optional[str] = None,
969
+ parent_id: Optional[str] = None,
970
+ tags: Optional[Tags] = None,
971
+ transfer_method: Optional[TransferMethod] = TransferMethod.PUT_URL,
972
+ md5: Optional[str] = None,
973
+ sha1: Optional[str] = None,
974
+ sha512: Optional[str] = None,
975
+ crc32c: Optional[str] = None,
976
+ sha256: Optional[str] = None,
977
+ size: Optional[int] = None,
978
+ bucket_id: Optional[str] = None,
979
+ ) -> PangeaResponse[PutResult]:
980
+ """
981
+ Request upload URL
982
+
983
+ Request an upload URL.
984
+
985
+ OperationId: share_post_v1_put 2
986
+
987
+ Args:
988
+ name (str, optional): The name of the object to store.
989
+ folder (str, optional): The path to the parent folder. Leave blank
990
+ for the root folder. Path must resolve to `parent_id` if also set.
991
+ format (FileFormat, optional): The format of the file, which will be verified by the server if provided. Uploads not matching the supplied format will be rejected.
992
+ metadata (Metadata, optional): A set of string-based key/value pairs used to provide additional data about an object.
993
+ mimetype (str, optional): The MIME type of the file, which will be verified by the server if provided. Uploads not matching the supplied MIME type will be rejected.
994
+ parent_id (str, optional): The parent ID of the object (a folder). Leave blank to keep in the root folder.
995
+ tags (Tags, optional): A list of user-defined tags.
996
+ transfer_method (TransferMethod, optional): The transfer method used to upload the file data.
997
+ md5 (str, optional): The hexadecimal-encoded MD5 hash of the file data, which will be verified by the server if provided.
998
+ sha1 (str, optional): The hexadecimal-encoded SHA1 hash of the file data, which will be verified by the server if provided.
999
+ sha512 (str, optional): The hexadecimal-encoded SHA512 hash of the file data, which will be verified by the server if provided.
1000
+ crc32c (str, optional): The hexadecimal-encoded CRC32C hash of the file data, which will be verified by the server if provided.
1001
+ sha256 (str, optional): The SHA256 hash of the file data, which will be verified by the server if provided.
1002
+ size (str, optional): The size (in bytes) of the file. If the upload doesn't match, the call will fail.
1003
+ bucket_id (str, optional): The bucket to use, if not the default.
1004
+
1005
+ Returns:
1006
+ A PangeaResponse. Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/share).
1007
+
1008
+ Examples:
1009
+ response = share.request_upload_url(
1010
+ transfer_method=TransferMethod.POST_URL,
1011
+ crc32c="515f7c32",
1012
+ sha256="c0b56b1a154697f79d27d57a3a2aad4c93849aa2239cd23048fc6f45726271cc",
1013
+ size=222089,
1014
+ metadata={
1015
+ "created_by": "jim",
1016
+ "priority": "medium",
1017
+ },
1018
+ parent_id="pos_3djfmzg2db4c6donarecbyv5begtj2bm",
1019
+ folder="/",
1020
+ tags=["irs_2023", "personal"],
1021
+ )
1022
+ """
1023
+ input = PutRequest(
1024
+ name=name,
1025
+ format=format,
1026
+ metadata=metadata,
1027
+ mimetype=mimetype,
1028
+ parent_id=parent_id,
1029
+ folder=folder,
1030
+ tags=tags,
1031
+ transfer_method=transfer_method,
1032
+ crc32c=crc32c,
1033
+ md5=md5,
1034
+ sha1=sha1,
1035
+ sha256=sha256,
1036
+ sha512=sha512,
1037
+ size=size,
1038
+ bucket_id=bucket_id,
1039
+ )
1040
+
1041
+ data = input.model_dump(exclude_none=True)
1042
+ return self.request.request_presigned_url("v1/put", PutResult, data=data)
1043
+
1044
+ def update(
1045
+ self,
1046
+ id: Optional[str] = None,
1047
+ folder: Optional[str] = None,
1048
+ add_metadata: Optional[Metadata] = None,
1049
+ remove_metadata: Optional[Metadata] = None,
1050
+ metadata: Optional[Metadata] = None,
1051
+ add_tags: Optional[Tags] = None,
1052
+ remove_tags: Optional[Tags] = None,
1053
+ tags: Optional[Tags] = None,
1054
+ parent_id: Optional[str] = None,
1055
+ updated_at: Optional[str] = None,
1056
+ bucket_id: Optional[str] = None,
1057
+ ) -> PangeaResponse[UpdateResult]:
1058
+ """
1059
+ Update a file
1060
+
1061
+ Update a file.
1062
+
1063
+ OperationId: share_post_v1_update
1064
+
1065
+ Args:
1066
+ id (str, optional): An identifier for the file to update.
1067
+ path (str, optional): Set the parent (folder). Leave blank for the
1068
+ root folder. Path must resolve to `parent_id` if also set.
1069
+ add_metadata (Metadata, optional): A list of Metadata key/values to set in the object. If a provided key exists, the value will be replaced.
1070
+ remove_metadata (Metadata, optional): A list of Metadata key/values to remove in the object. It is not an error for a provided key to not exist. If a provided key exists but doesn't match the provided value, it will not be removed.
1071
+ metadata (Metadata, optional): Set the object's Metadata.
1072
+ add_tags (Tags, optional): A list of Tags to add. It is not an error to provide a tag which already exists.
1073
+ remove_tags (Tags, optional): A list of Tags to remove. It is not an error to provide a tag which is not present.
1074
+ tags (Tags, optional): Set the object's Tags.
1075
+ parent_id (str, optional): Set the parent (folder) of the object.
1076
+ updated_at (str, optional): The date and time the object was last updated. If included, the update will fail if this doesn't match what's stored.
1077
+ bucket_id (str, optional): The bucket to use, if not the default.
1078
+
1079
+ Returns:
1080
+ A PangeaResponse. Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/share).
1081
+
1082
+ Examples:
1083
+ response = share.update(
1084
+ id="pos_3djfmzg2db4c6donarecbyv5begtj2bm",
1085
+ remove_metadata={
1086
+ "created_by": "jim",
1087
+ "priority": "medium",
1088
+ },
1089
+ remove_tags=["irs_2023", "personal"],
1090
+ )
1091
+ """
1092
+ input = UpdateRequest(
1093
+ id=id,
1094
+ folder=folder,
1095
+ add_metadata=add_metadata,
1096
+ remove_metadata=remove_metadata,
1097
+ metadata=metadata,
1098
+ add_tags=add_tags,
1099
+ remove_tags=remove_tags,
1100
+ tags=tags,
1101
+ parent_id=parent_id,
1102
+ updated_at=updated_at,
1103
+ bucket_id=bucket_id,
1104
+ )
1105
+ return self.request.post("v1/update", UpdateResult, data=input.model_dump(exclude_none=True))
1106
+
1107
+ def share_link_create(
1108
+ self, links: List[ShareLinkCreateItem], bucket_id: Optional[str] = None
1109
+ ) -> PangeaResponse[ShareLinkCreateResult]:
1110
+ """
1111
+ Create share links
1112
+
1113
+ Create a share link.
1114
+
1115
+ OperationId: share_post_v1_share_link_create
1116
+
1117
+ Args:
1118
+ links (List[ShareLinkCreateItem]):
1119
+ bucket_id (str, optional): The bucket to use, if not the default.
1120
+
1121
+ Returns:
1122
+ A PangeaResponse. Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/share).
1123
+
1124
+ Examples:
1125
+ response = share.share_link_create(
1126
+ links=[
1127
+ {
1128
+ targets: ["pos_3djfmzg2db4c6donarecbyv5begtj2bm"],
1129
+ link_type: LinkType.DOWNLOAD,
1130
+ authenticators: [
1131
+ {
1132
+ "auth_type": AuthenticatorType.PASSWORD,
1133
+ "auth_context": "my_fav_Pa55word",
1134
+ }
1135
+ ],
1136
+ }
1137
+ ],
1138
+ )
1139
+ """
1140
+ input = ShareLinkCreateRequest(links=links, bucket_id=bucket_id)
1141
+ return self.request.post(
1142
+ "v1/share/link/create", ShareLinkCreateResult, data=input.model_dump(exclude_none=True)
1143
+ )
1144
+
1145
+ def share_link_get(self, id: str) -> PangeaResponse[ShareLinkGetResult]:
1146
+ """
1147
+ Get share link
1148
+
1149
+ Get a share link.
1150
+
1151
+ OperationId: share_post_v1_share_link_get
1152
+
1153
+ Args:
1154
+ id (str, optional): The ID of a share link.
1155
+
1156
+ Returns:
1157
+ A PangeaResponse. Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/share).
1158
+
1159
+ Examples:
1160
+ response = share.share_link_get(
1161
+ id="psl_3djfmzg2db4c6donarecbyv5begtj2bm"
1162
+ )
1163
+ """
1164
+ input = ShareLinkGetRequest(id=id)
1165
+ return self.request.post("v1/share/link/get", ShareLinkGetResult, data=input.model_dump(exclude_none=True))
1166
+
1167
+ def share_link_list(
1168
+ self,
1169
+ filter: Optional[Union[Dict[str, str], FilterShareLinkList]] = None,
1170
+ last: Optional[str] = None,
1171
+ order: Optional[ItemOrder] = None,
1172
+ order_by: Optional[ShareLinkOrderBy] = None,
1173
+ size: Optional[int] = None,
1174
+ bucket_id: Optional[str] = None,
1175
+ ) -> PangeaResponse[ShareLinkListResult]:
1176
+ """
1177
+ List share links
1178
+
1179
+ Look up share links by filter options.
1180
+
1181
+ OperationId: share_post_v1_share_link_list
1182
+
1183
+ Args:
1184
+ filter (Union[Dict[str, str], ShareLinkListFilter], optional):
1185
+ last (str, optional): Reflected value from a previous response to obtain the next page of results.
1186
+ order (ItemOrder, optional): Order results asc(ending) or desc(ending).
1187
+ order_by (ItemOrderBy, optional): Which field to order results by.
1188
+ size (int, optional): Maximum results to include in the response.
1189
+ bucket_id (str, optional): The bucket to use, if not the default.
1190
+
1191
+ Returns:
1192
+ A PangeaResponse. Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/share).
1193
+
1194
+ Examples:
1195
+ response = share.share_link_list()
1196
+ """
1197
+ input = ShareLinkListRequest(
1198
+ filter=filter, last=last, order=order, order_by=order_by, size=size, bucket_id=bucket_id
1199
+ )
1200
+ return self.request.post("v1/share/link/list", ShareLinkListResult, data=input.model_dump(exclude_none=True))
1201
+
1202
+ def share_link_delete(
1203
+ self, ids: List[str], bucket_id: Optional[str] = None
1204
+ ) -> PangeaResponse[ShareLinkDeleteResult]:
1205
+ """
1206
+ Delete share links
1207
+
1208
+ Delete share links.
1209
+
1210
+ OperationId: share_post_v1_share_link_delete
1211
+
1212
+ Args:
1213
+ ids (List[str]): list of the share link's id to delete
1214
+ bucket_id (str, optional): The bucket to use, if not the default
1215
+
1216
+ Returns:
1217
+ A PangeaResponse. Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/share).
1218
+
1219
+ Examples:
1220
+ response = share.share_link_delete(
1221
+ ids=["psl_3djfmzg2db4c6donarecbyv5begtj2bm"]
1222
+ )
1223
+ """
1224
+ input = ShareLinkDeleteRequest(ids=ids, bucket_id=bucket_id)
1225
+ return self.request.post(
1226
+ "v1/share/link/delete", ShareLinkDeleteResult, data=input.model_dump(exclude_none=True)
1227
+ )
1228
+
1229
+ def share_link_send(
1230
+ self, links: List[ShareLinkSendItem], sender_email: str, sender_name: Optional[str] = None
1231
+ ) -> PangeaResponse[ShareLinkSendResult]:
1232
+ """
1233
+ Send share links
1234
+
1235
+ Send a secure share-link notification to a set of email addresses. The
1236
+ notification email will contain an Open button that the recipient can
1237
+ use to follow the secured share-link to authenticate and then access the
1238
+ shared content.
1239
+
1240
+ OperationId: share_post_v1_share_link_send
1241
+
1242
+ Args:
1243
+ sender_email: An email address.
1244
+
1245
+ Returns:
1246
+ A PangeaResponse. Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/share).
1247
+
1248
+ Examples:
1249
+ response = share.share_link_send(
1250
+ links=[ShareLinkSendItem(id=link.id, email="foo@example.org")],
1251
+ sender_email="sender@example.org",
1252
+ )
1253
+ """
1254
+
1255
+ input = ShareLinkSendRequest(links=links, sender_email=sender_email, sender_name=sender_name)
1256
+ return self.request.post("v1/share/link/send", ShareLinkSendResult, data=input.model_dump(exclude_none=True))