dub 0.34.0__py3-none-any.whl → 0.35.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.
Files changed (93) hide show
  1. dub/_version.py +3 -3
  2. dub/basesdk.py +20 -6
  3. dub/{workspaces.py → bounties.py} +349 -69
  4. dub/models/components/__init__.py +114 -26
  5. dub/models/components/analyticsbrowsers.py +18 -1
  6. dub/models/components/analyticscities.py +18 -1
  7. dub/models/components/analyticscontinents.py +18 -1
  8. dub/models/components/analyticscount.py +18 -1
  9. dub/models/components/analyticscountries.py +20 -1
  10. dub/models/components/analyticsdevices.py +18 -1
  11. dub/models/components/analyticsos.py +18 -1
  12. dub/models/components/analyticsreferers.py +18 -1
  13. dub/models/components/analyticsrefererurls.py +18 -1
  14. dub/models/components/analyticsregions.py +18 -1
  15. dub/models/components/analyticstimeseries.py +18 -1
  16. dub/models/components/analyticstoplinks.py +16 -26
  17. dub/models/components/analyticstopurls.py +18 -1
  18. dub/models/components/analyticstriggers.py +18 -1
  19. dub/models/components/commissioncreatedevent.py +123 -63
  20. dub/models/components/domainschema.py +31 -50
  21. dub/models/components/folderschema.py +18 -19
  22. dub/models/components/leadcreatedevent.py +151 -134
  23. dub/models/components/linkclickedevent.py +57 -70
  24. dub/models/components/linkschema.py +63 -64
  25. dub/models/components/linkwebhookevent.py +43 -51
  26. dub/models/components/partneranalyticscount.py +18 -1
  27. dub/models/components/partneranalyticstimeseries.py +18 -1
  28. dub/models/components/partneranalyticstoplinks.py +16 -27
  29. dub/models/components/partnerapplicationsubmittedevent.py +42 -75
  30. dub/models/components/partnerenrolledevent.py +477 -83
  31. dub/models/components/salecreatedevent.py +152 -151
  32. dub/models/errors/badrequest.py +18 -1
  33. dub/models/errors/conflict.py +18 -1
  34. dub/models/errors/forbidden.py +18 -1
  35. dub/models/errors/internalservererror.py +18 -1
  36. dub/models/errors/inviteexpired.py +18 -1
  37. dub/models/errors/notfound.py +18 -1
  38. dub/models/errors/ratelimitexceeded.py +18 -1
  39. dub/models/errors/unauthorized.py +18 -1
  40. dub/models/errors/unprocessableentity.py +18 -1
  41. dub/models/operations/__init__.py +323 -19
  42. dub/models/operations/approvebountysubmission.py +211 -0
  43. dub/models/operations/banpartner.py +14 -19
  44. dub/models/operations/bulkcreatelinks.py +86 -87
  45. dub/models/operations/bulkupdatelinks.py +97 -82
  46. dub/models/operations/checkdomainstatus.py +1 -17
  47. dub/models/operations/createdomain.py +33 -34
  48. dub/models/operations/createfolder.py +18 -19
  49. dub/models/operations/createlink.py +86 -87
  50. dub/models/operations/createpartner.py +560 -168
  51. dub/models/operations/createpartnerlink.py +74 -85
  52. dub/models/operations/createreferralsembedtoken.py +99 -87
  53. dub/models/operations/createtag.py +18 -1
  54. dub/models/operations/deactivatepartner.py +65 -0
  55. dub/models/operations/getcustomer.py +106 -105
  56. dub/models/operations/getcustomers.py +123 -105
  57. dub/models/operations/getlinkinfo.py +18 -1
  58. dub/models/operations/getlinks.py +36 -1
  59. dub/models/operations/getlinkscount.py +32 -1
  60. dub/models/operations/getqrcode.py +29 -1
  61. dub/models/operations/gettags.py +20 -1
  62. dub/models/operations/listbountysubmissions.py +249 -0
  63. dub/models/operations/listcommissions.py +129 -64
  64. dub/models/operations/listdomains.py +18 -1
  65. dub/models/operations/listevents.py +414 -389
  66. dub/models/operations/listfolders.py +18 -1
  67. dub/models/operations/listpartners.py +510 -84
  68. dub/models/operations/registerdomain.py +1 -17
  69. dub/models/operations/rejectbountysubmission.py +219 -0
  70. dub/models/operations/retrieveanalytics.py +65 -66
  71. dub/models/operations/retrievelinks.py +30 -19
  72. dub/models/operations/retrievepartneranalytics.py +25 -28
  73. dub/models/operations/tracklead.py +38 -83
  74. dub/models/operations/tracksale.py +52 -95
  75. dub/models/operations/updatecommission.py +126 -64
  76. dub/models/operations/updatecustomer.py +122 -131
  77. dub/models/operations/updatedomain.py +50 -35
  78. dub/models/operations/updatefolder.py +34 -19
  79. dub/models/operations/updatelink.py +101 -86
  80. dub/models/operations/updatetag.py +34 -1
  81. dub/models/operations/upsertlink.py +86 -87
  82. dub/models/operations/upsertpartnerlink.py +77 -90
  83. dub/partners.py +288 -0
  84. dub/sdk.py +3 -3
  85. dub/utils/__init__.py +10 -1
  86. {dub-0.34.0.dist-info → dub-0.35.0.dist-info}/METADATA +10 -8
  87. dub-0.35.0.dist-info/RECORD +143 -0
  88. dub/models/components/workspaceschema.py +0 -328
  89. dub/models/operations/getworkspace.py +0 -21
  90. dub/models/operations/updateworkspace.py +0 -78
  91. dub-0.34.0.dist-info/RECORD +0 -142
  92. {dub-0.34.0.dist-info → dub-0.35.0.dist-info}/WHEEL +0 -0
  93. {dub-0.34.0.dist-info → dub-0.35.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,211 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ from __future__ import annotations
4
+ from dub.types import BaseModel, Nullable, OptionalNullable, UNSET, UNSET_SENTINEL
5
+ from dub.utils import FieldMetadata, PathParamMetadata, RequestMetadata
6
+ from enum import Enum
7
+ import pydantic
8
+ from pydantic import model_serializer
9
+ from typing import List, Optional
10
+ from typing_extensions import Annotated, NotRequired, TypedDict
11
+
12
+
13
+ class ApproveBountySubmissionRequestBodyTypedDict(TypedDict):
14
+ reward_amount: NotRequired[Nullable[float]]
15
+ r"""The reward amount for the performance-based bounty. Applicable if the bounty reward amount is not set."""
16
+
17
+
18
+ class ApproveBountySubmissionRequestBody(BaseModel):
19
+ reward_amount: Annotated[
20
+ OptionalNullable[float], pydantic.Field(alias="rewardAmount")
21
+ ] = UNSET
22
+ r"""The reward amount for the performance-based bounty. Applicable if the bounty reward amount is not set."""
23
+
24
+ @model_serializer(mode="wrap")
25
+ def serialize_model(self, handler):
26
+ optional_fields = set(["rewardAmount"])
27
+ nullable_fields = set(["rewardAmount"])
28
+ serialized = handler(self)
29
+ m = {}
30
+
31
+ for n, f in type(self).model_fields.items():
32
+ k = f.alias or n
33
+ val = serialized.get(k)
34
+ is_nullable_and_explicitly_set = (
35
+ k in nullable_fields
36
+ and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member
37
+ )
38
+
39
+ if val != UNSET_SENTINEL:
40
+ if (
41
+ val is not None
42
+ or k not in optional_fields
43
+ or is_nullable_and_explicitly_set
44
+ ):
45
+ m[k] = val
46
+
47
+ return m
48
+
49
+
50
+ class ApproveBountySubmissionRequestTypedDict(TypedDict):
51
+ bounty_id: str
52
+ r"""The ID of the bounty"""
53
+ submission_id: str
54
+ r"""The ID of the bounty submission"""
55
+ request_body: NotRequired[ApproveBountySubmissionRequestBodyTypedDict]
56
+
57
+
58
+ class ApproveBountySubmissionRequest(BaseModel):
59
+ bounty_id: Annotated[
60
+ str,
61
+ pydantic.Field(alias="bountyId"),
62
+ FieldMetadata(path=PathParamMetadata(style="simple", explode=False)),
63
+ ]
64
+ r"""The ID of the bounty"""
65
+
66
+ submission_id: Annotated[
67
+ str,
68
+ pydantic.Field(alias="submissionId"),
69
+ FieldMetadata(path=PathParamMetadata(style="simple", explode=False)),
70
+ ]
71
+ r"""The ID of the bounty submission"""
72
+
73
+ request_body: Annotated[
74
+ Optional[ApproveBountySubmissionRequestBody],
75
+ FieldMetadata(request=RequestMetadata(media_type="application/json")),
76
+ ] = None
77
+
78
+ @model_serializer(mode="wrap")
79
+ def serialize_model(self, handler):
80
+ optional_fields = set(["RequestBody"])
81
+ serialized = handler(self)
82
+ m = {}
83
+
84
+ for n, f in type(self).model_fields.items():
85
+ k = f.alias or n
86
+ val = serialized.get(k)
87
+
88
+ if val != UNSET_SENTINEL:
89
+ if val is not None or k not in optional_fields:
90
+ m[k] = val
91
+
92
+ return m
93
+
94
+
95
+ class ApproveBountySubmissionFilesTypedDict(TypedDict):
96
+ url: str
97
+ r"""The URL of the uploaded file."""
98
+ file_name: str
99
+ r"""The original file name."""
100
+ size: float
101
+ r"""The file size in bytes."""
102
+
103
+
104
+ class ApproveBountySubmissionFiles(BaseModel):
105
+ url: str
106
+ r"""The URL of the uploaded file."""
107
+
108
+ file_name: Annotated[str, pydantic.Field(alias="fileName")]
109
+ r"""The original file name."""
110
+
111
+ size: float
112
+ r"""The file size in bytes."""
113
+
114
+
115
+ class ApproveBountySubmissionStatus(str, Enum):
116
+ r"""The status of the submission"""
117
+
118
+ DRAFT = "draft"
119
+ SUBMITTED = "submitted"
120
+ APPROVED = "approved"
121
+ REJECTED = "rejected"
122
+
123
+
124
+ class ApproveBountySubmissionResponseBodyTypedDict(TypedDict):
125
+ r"""The approved bounty submission."""
126
+
127
+ id: str
128
+ r"""The ID of the bounty submission"""
129
+ bounty_id: str
130
+ r"""The ID of the bounty"""
131
+ partner_id: str
132
+ r"""The ID of the partner"""
133
+ description: Nullable[str]
134
+ r"""The description of the submission"""
135
+ urls: Nullable[List[str]]
136
+ r"""The URLs submitted for the submission"""
137
+ files: Nullable[List[ApproveBountySubmissionFilesTypedDict]]
138
+ r"""The files uploaded for the submission"""
139
+ status: ApproveBountySubmissionStatus
140
+ r"""The status of the submission"""
141
+ performance_count: Nullable[float]
142
+ r"""The performance count of the submission"""
143
+ created_at: str
144
+ r"""The date and time the submission was created"""
145
+ completed_at: Nullable[str]
146
+ r"""The date and time the submission was completed"""
147
+ reviewed_at: Nullable[str]
148
+ r"""The date and time the submission was reviewed"""
149
+ rejection_reason: Nullable[str]
150
+ r"""The reason for rejecting the submission"""
151
+ rejection_note: Nullable[str]
152
+ r"""The note for rejecting the submission"""
153
+
154
+
155
+ class ApproveBountySubmissionResponseBody(BaseModel):
156
+ r"""The approved bounty submission."""
157
+
158
+ id: str
159
+ r"""The ID of the bounty submission"""
160
+
161
+ bounty_id: Annotated[str, pydantic.Field(alias="bountyId")]
162
+ r"""The ID of the bounty"""
163
+
164
+ partner_id: Annotated[str, pydantic.Field(alias="partnerId")]
165
+ r"""The ID of the partner"""
166
+
167
+ description: Nullable[str]
168
+ r"""The description of the submission"""
169
+
170
+ urls: Nullable[List[str]]
171
+ r"""The URLs submitted for the submission"""
172
+
173
+ files: Nullable[List[ApproveBountySubmissionFiles]]
174
+ r"""The files uploaded for the submission"""
175
+
176
+ status: ApproveBountySubmissionStatus
177
+ r"""The status of the submission"""
178
+
179
+ performance_count: Annotated[
180
+ Nullable[float], pydantic.Field(alias="performanceCount")
181
+ ]
182
+ r"""The performance count of the submission"""
183
+
184
+ created_at: Annotated[str, pydantic.Field(alias="createdAt")]
185
+ r"""The date and time the submission was created"""
186
+
187
+ completed_at: Annotated[Nullable[str], pydantic.Field(alias="completedAt")]
188
+ r"""The date and time the submission was completed"""
189
+
190
+ reviewed_at: Annotated[Nullable[str], pydantic.Field(alias="reviewedAt")]
191
+ r"""The date and time the submission was reviewed"""
192
+
193
+ rejection_reason: Annotated[Nullable[str], pydantic.Field(alias="rejectionReason")]
194
+ r"""The reason for rejecting the submission"""
195
+
196
+ rejection_note: Annotated[Nullable[str], pydantic.Field(alias="rejectionNote")]
197
+ r"""The note for rejecting the submission"""
198
+
199
+ @model_serializer(mode="wrap")
200
+ def serialize_model(self, handler):
201
+ serialized = handler(self)
202
+ m = {}
203
+
204
+ for n, f in type(self).model_fields.items():
205
+ k = f.alias or n
206
+ val = serialized.get(k)
207
+
208
+ if val != UNSET_SENTINEL:
209
+ m[k] = val
210
+
211
+ return m
@@ -40,31 +40,26 @@ class BanPartnerRequestBody(BaseModel):
40
40
 
41
41
  @model_serializer(mode="wrap")
42
42
  def serialize_model(self, handler):
43
- optional_fields = ["partnerId", "tenantId"]
44
- nullable_fields = ["partnerId", "tenantId"]
45
- null_default_fields = []
46
-
43
+ optional_fields = set(["partnerId", "tenantId"])
44
+ nullable_fields = set(["partnerId", "tenantId"])
47
45
  serialized = handler(self)
48
-
49
46
  m = {}
50
47
 
51
48
  for n, f in type(self).model_fields.items():
52
49
  k = f.alias or n
53
50
  val = serialized.get(k)
54
- serialized.pop(k, None)
55
-
56
- optional_nullable = k in optional_fields and k in nullable_fields
57
- is_set = (
58
- self.__pydantic_fields_set__.intersection({n})
59
- or k in null_default_fields
60
- ) # pylint: disable=no-member
61
-
62
- if val is not None and val != UNSET_SENTINEL:
63
- m[k] = val
64
- elif val != UNSET_SENTINEL and (
65
- not k in optional_fields or (optional_nullable and is_set)
66
- ):
67
- m[k] = val
51
+ is_nullable_and_explicitly_set = (
52
+ k in nullable_fields
53
+ and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member
54
+ )
55
+
56
+ if val != UNSET_SENTINEL:
57
+ if (
58
+ val is not None
59
+ or k not in optional_fields
60
+ or is_nullable_and_explicitly_set
61
+ ):
62
+ m[k] = val
68
63
 
69
64
  return m
70
65
 
@@ -292,99 +292,98 @@ class RequestBody(BaseModel):
292
292
 
293
293
  @model_serializer(mode="wrap")
294
294
  def serialize_model(self, handler):
295
- optional_fields = [
296
- "domain",
297
- "key",
298
- "keyLength",
299
- "externalId",
300
- "tenantId",
301
- "programId",
302
- "partnerId",
303
- "prefix",
304
- "trackConversion",
305
- "archived",
306
- "tagIds",
307
- "tagNames",
308
- "folderId",
309
- "comments",
310
- "expiresAt",
311
- "expiredUrl",
312
- "password",
313
- "proxy",
314
- "title",
315
- "description",
316
- "image",
317
- "video",
318
- "rewrite",
319
- "ios",
320
- "android",
321
- "geo",
322
- "doIndex",
323
- "utm_source",
324
- "utm_medium",
325
- "utm_campaign",
326
- "utm_term",
327
- "utm_content",
328
- "ref",
329
- "webhookIds",
330
- "testVariants",
331
- "testStartedAt",
332
- "testCompletedAt",
333
- "publicStats",
334
- "tagId",
335
- ]
336
- nullable_fields = [
337
- "externalId",
338
- "tenantId",
339
- "programId",
340
- "partnerId",
341
- "folderId",
342
- "comments",
343
- "expiresAt",
344
- "expiredUrl",
345
- "password",
346
- "title",
347
- "description",
348
- "image",
349
- "video",
350
- "ios",
351
- "android",
352
- "geo",
353
- "utm_source",
354
- "utm_medium",
355
- "utm_campaign",
356
- "utm_term",
357
- "utm_content",
358
- "ref",
359
- "webhookIds",
360
- "testVariants",
361
- "testStartedAt",
362
- "testCompletedAt",
363
- "tagId",
364
- ]
365
- null_default_fields = []
366
-
295
+ optional_fields = set(
296
+ [
297
+ "domain",
298
+ "key",
299
+ "keyLength",
300
+ "externalId",
301
+ "tenantId",
302
+ "programId",
303
+ "partnerId",
304
+ "prefix",
305
+ "trackConversion",
306
+ "archived",
307
+ "tagIds",
308
+ "tagNames",
309
+ "folderId",
310
+ "comments",
311
+ "expiresAt",
312
+ "expiredUrl",
313
+ "password",
314
+ "proxy",
315
+ "title",
316
+ "description",
317
+ "image",
318
+ "video",
319
+ "rewrite",
320
+ "ios",
321
+ "android",
322
+ "geo",
323
+ "doIndex",
324
+ "utm_source",
325
+ "utm_medium",
326
+ "utm_campaign",
327
+ "utm_term",
328
+ "utm_content",
329
+ "ref",
330
+ "webhookIds",
331
+ "testVariants",
332
+ "testStartedAt",
333
+ "testCompletedAt",
334
+ "publicStats",
335
+ "tagId",
336
+ ]
337
+ )
338
+ nullable_fields = set(
339
+ [
340
+ "externalId",
341
+ "tenantId",
342
+ "programId",
343
+ "partnerId",
344
+ "folderId",
345
+ "comments",
346
+ "expiresAt",
347
+ "expiredUrl",
348
+ "password",
349
+ "title",
350
+ "description",
351
+ "image",
352
+ "video",
353
+ "ios",
354
+ "android",
355
+ "geo",
356
+ "utm_source",
357
+ "utm_medium",
358
+ "utm_campaign",
359
+ "utm_term",
360
+ "utm_content",
361
+ "ref",
362
+ "webhookIds",
363
+ "testVariants",
364
+ "testStartedAt",
365
+ "testCompletedAt",
366
+ "tagId",
367
+ ]
368
+ )
367
369
  serialized = handler(self)
368
-
369
370
  m = {}
370
371
 
371
372
  for n, f in type(self).model_fields.items():
372
373
  k = f.alias or n
373
374
  val = serialized.get(k)
374
- serialized.pop(k, None)
375
-
376
- optional_nullable = k in optional_fields and k in nullable_fields
377
- is_set = (
378
- self.__pydantic_fields_set__.intersection({n})
379
- or k in null_default_fields
380
- ) # pylint: disable=no-member
381
-
382
- if val is not None and val != UNSET_SENTINEL:
383
- m[k] = val
384
- elif val != UNSET_SENTINEL and (
385
- not k in optional_fields or (optional_nullable and is_set)
386
- ):
387
- m[k] = val
375
+ is_nullable_and_explicitly_set = (
376
+ k in nullable_fields
377
+ and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member
378
+ )
379
+
380
+ if val != UNSET_SENTINEL:
381
+ if (
382
+ val is not None
383
+ or k not in optional_fields
384
+ or is_nullable_and_explicitly_set
385
+ ):
386
+ m[k] = val
388
387
 
389
388
  return m
390
389
 
@@ -261,94 +261,93 @@ class Data(BaseModel):
261
261
 
262
262
  @model_serializer(mode="wrap")
263
263
  def serialize_model(self, handler):
264
- optional_fields = [
265
- "url",
266
- "tenantId",
267
- "programId",
268
- "partnerId",
269
- "trackConversion",
270
- "archived",
271
- "tagIds",
272
- "tagNames",
273
- "folderId",
274
- "comments",
275
- "expiresAt",
276
- "expiredUrl",
277
- "password",
278
- "proxy",
279
- "title",
280
- "description",
281
- "image",
282
- "video",
283
- "rewrite",
284
- "ios",
285
- "android",
286
- "geo",
287
- "doIndex",
288
- "utm_source",
289
- "utm_medium",
290
- "utm_campaign",
291
- "utm_term",
292
- "utm_content",
293
- "ref",
294
- "webhookIds",
295
- "testVariants",
296
- "testStartedAt",
297
- "testCompletedAt",
298
- "publicStats",
299
- "tagId",
300
- ]
301
- nullable_fields = [
302
- "tenantId",
303
- "programId",
304
- "partnerId",
305
- "folderId",
306
- "comments",
307
- "expiresAt",
308
- "expiredUrl",
309
- "password",
310
- "title",
311
- "description",
312
- "image",
313
- "video",
314
- "ios",
315
- "android",
316
- "geo",
317
- "utm_source",
318
- "utm_medium",
319
- "utm_campaign",
320
- "utm_term",
321
- "utm_content",
322
- "ref",
323
- "webhookIds",
324
- "testVariants",
325
- "testStartedAt",
326
- "testCompletedAt",
327
- "tagId",
328
- ]
329
- null_default_fields = []
330
-
264
+ optional_fields = set(
265
+ [
266
+ "url",
267
+ "tenantId",
268
+ "programId",
269
+ "partnerId",
270
+ "trackConversion",
271
+ "archived",
272
+ "tagIds",
273
+ "tagNames",
274
+ "folderId",
275
+ "comments",
276
+ "expiresAt",
277
+ "expiredUrl",
278
+ "password",
279
+ "proxy",
280
+ "title",
281
+ "description",
282
+ "image",
283
+ "video",
284
+ "rewrite",
285
+ "ios",
286
+ "android",
287
+ "geo",
288
+ "doIndex",
289
+ "utm_source",
290
+ "utm_medium",
291
+ "utm_campaign",
292
+ "utm_term",
293
+ "utm_content",
294
+ "ref",
295
+ "webhookIds",
296
+ "testVariants",
297
+ "testStartedAt",
298
+ "testCompletedAt",
299
+ "publicStats",
300
+ "tagId",
301
+ ]
302
+ )
303
+ nullable_fields = set(
304
+ [
305
+ "tenantId",
306
+ "programId",
307
+ "partnerId",
308
+ "folderId",
309
+ "comments",
310
+ "expiresAt",
311
+ "expiredUrl",
312
+ "password",
313
+ "title",
314
+ "description",
315
+ "image",
316
+ "video",
317
+ "ios",
318
+ "android",
319
+ "geo",
320
+ "utm_source",
321
+ "utm_medium",
322
+ "utm_campaign",
323
+ "utm_term",
324
+ "utm_content",
325
+ "ref",
326
+ "webhookIds",
327
+ "testVariants",
328
+ "testStartedAt",
329
+ "testCompletedAt",
330
+ "tagId",
331
+ ]
332
+ )
331
333
  serialized = handler(self)
332
-
333
334
  m = {}
334
335
 
335
336
  for n, f in type(self).model_fields.items():
336
337
  k = f.alias or n
337
338
  val = serialized.get(k)
338
- serialized.pop(k, None)
339
-
340
- optional_nullable = k in optional_fields and k in nullable_fields
341
- is_set = (
342
- self.__pydantic_fields_set__.intersection({n})
343
- or k in null_default_fields
344
- ) # pylint: disable=no-member
345
-
346
- if val is not None and val != UNSET_SENTINEL:
347
- m[k] = val
348
- elif val != UNSET_SENTINEL and (
349
- not k in optional_fields or (optional_nullable and is_set)
350
- ):
351
- m[k] = val
339
+ is_nullable_and_explicitly_set = (
340
+ k in nullable_fields
341
+ and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member
342
+ )
343
+
344
+ if val != UNSET_SENTINEL:
345
+ if (
346
+ val is not None
347
+ or k not in optional_fields
348
+ or is_nullable_and_explicitly_set
349
+ ):
350
+ m[k] = val
352
351
 
353
352
  return m
354
353
 
@@ -371,3 +370,19 @@ class BulkUpdateLinksRequestBody(BaseModel):
371
370
  Optional[List[str]], pydantic.Field(alias="externalIds")
372
371
  ] = None
373
372
  r"""The external IDs of the links to update as stored in your database."""
373
+
374
+ @model_serializer(mode="wrap")
375
+ def serialize_model(self, handler):
376
+ optional_fields = set(["linkIds", "externalIds"])
377
+ serialized = handler(self)
378
+ m = {}
379
+
380
+ for n, f in type(self).model_fields.items():
381
+ k = f.alias or n
382
+ val = serialized.get(k)
383
+
384
+ if val != UNSET_SENTINEL:
385
+ if val is not None or k not in optional_fields:
386
+ m[k] = val
387
+
388
+ return m