dub 0.34.1__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/models/components/__init__.py +108 -26
  4. dub/models/components/analyticsbrowsers.py +18 -1
  5. dub/models/components/analyticscities.py +18 -1
  6. dub/models/components/analyticscontinents.py +18 -1
  7. dub/models/components/analyticscount.py +18 -1
  8. dub/models/components/analyticscountries.py +20 -1
  9. dub/models/components/analyticsdevices.py +18 -1
  10. dub/models/components/analyticsos.py +18 -1
  11. dub/models/components/analyticsreferers.py +18 -1
  12. dub/models/components/analyticsrefererurls.py +18 -1
  13. dub/models/components/analyticsregions.py +18 -1
  14. dub/models/components/analyticstimeseries.py +18 -1
  15. dub/models/components/analyticstoplinks.py +16 -26
  16. dub/models/components/analyticstopurls.py +18 -1
  17. dub/models/components/analyticstriggers.py +18 -1
  18. dub/models/components/commissioncreatedevent.py +96 -64
  19. dub/models/components/domainschema.py +31 -50
  20. dub/models/components/folderschema.py +18 -19
  21. dub/models/components/leadcreatedevent.py +151 -134
  22. dub/models/components/linkclickedevent.py +57 -70
  23. dub/models/components/linkschema.py +63 -64
  24. dub/models/components/linkwebhookevent.py +43 -51
  25. dub/models/components/partneranalyticscount.py +18 -1
  26. dub/models/components/partneranalyticstimeseries.py +18 -1
  27. dub/models/components/partneranalyticstoplinks.py +16 -27
  28. dub/models/components/partnerapplicationsubmittedevent.py +42 -75
  29. dub/models/components/partnerenrolledevent.py +477 -83
  30. dub/models/components/salecreatedevent.py +152 -151
  31. dub/models/errors/badrequest.py +18 -1
  32. dub/models/errors/conflict.py +18 -1
  33. dub/models/errors/forbidden.py +18 -1
  34. dub/models/errors/internalservererror.py +18 -1
  35. dub/models/errors/inviteexpired.py +18 -1
  36. dub/models/errors/notfound.py +18 -1
  37. dub/models/errors/ratelimitexceeded.py +18 -1
  38. dub/models/errors/unauthorized.py +18 -1
  39. dub/models/errors/unprocessableentity.py +18 -1
  40. dub/models/operations/__init__.py +230 -19
  41. dub/models/operations/approvebountysubmission.py +71 -45
  42. dub/models/operations/banpartner.py +14 -19
  43. dub/models/operations/bulkcreatelinks.py +86 -87
  44. dub/models/operations/bulkupdatelinks.py +97 -82
  45. dub/models/operations/checkdomainstatus.py +1 -17
  46. dub/models/operations/createdomain.py +33 -34
  47. dub/models/operations/createfolder.py +18 -19
  48. dub/models/operations/createlink.py +86 -87
  49. dub/models/operations/createpartner.py +560 -168
  50. dub/models/operations/createpartnerlink.py +74 -85
  51. dub/models/operations/createreferralsembedtoken.py +99 -87
  52. dub/models/operations/createtag.py +18 -1
  53. dub/models/operations/deactivatepartner.py +65 -0
  54. dub/models/operations/getcustomer.py +106 -105
  55. dub/models/operations/getcustomers.py +123 -105
  56. dub/models/operations/getlinkinfo.py +18 -1
  57. dub/models/operations/getlinks.py +36 -1
  58. dub/models/operations/getlinkscount.py +32 -1
  59. dub/models/operations/getqrcode.py +29 -1
  60. dub/models/operations/gettags.py +20 -1
  61. dub/models/operations/listbountysubmissions.py +63 -26
  62. dub/models/operations/listcommissions.py +129 -64
  63. dub/models/operations/listdomains.py +18 -1
  64. dub/models/operations/listevents.py +414 -389
  65. dub/models/operations/listfolders.py +18 -1
  66. dub/models/operations/listpartners.py +510 -84
  67. dub/models/operations/registerdomain.py +1 -17
  68. dub/models/operations/rejectbountysubmission.py +71 -26
  69. dub/models/operations/retrieveanalytics.py +65 -66
  70. dub/models/operations/retrievelinks.py +30 -19
  71. dub/models/operations/retrievepartneranalytics.py +25 -28
  72. dub/models/operations/tracklead.py +38 -83
  73. dub/models/operations/tracksale.py +52 -95
  74. dub/models/operations/updatecommission.py +126 -64
  75. dub/models/operations/updatecustomer.py +122 -131
  76. dub/models/operations/updatedomain.py +50 -35
  77. dub/models/operations/updatefolder.py +34 -19
  78. dub/models/operations/updatelink.py +101 -86
  79. dub/models/operations/updatetag.py +34 -1
  80. dub/models/operations/upsertlink.py +86 -87
  81. dub/models/operations/upsertpartnerlink.py +72 -78
  82. dub/partners.py +288 -0
  83. dub/sdk.py +0 -3
  84. dub/utils/__init__.py +10 -1
  85. {dub-0.34.1.dist-info → dub-0.35.0.dist-info}/METADATA +4 -8
  86. dub-0.35.0.dist-info/RECORD +143 -0
  87. dub/models/components/workspaceschema.py +0 -328
  88. dub/models/operations/getworkspace.py +0 -21
  89. dub/models/operations/updateworkspace.py +0 -78
  90. dub/workspaces.py +0 -561
  91. dub-0.34.1.dist-info/RECORD +0 -146
  92. {dub-0.34.1.dist-info → dub-0.35.0.dist-info}/WHEEL +0 -0
  93. {dub-0.34.1.dist-info → dub-0.35.0.dist-info}/licenses/LICENSE +0 -0
@@ -462,82 +462,81 @@ class ListEventsRequest(BaseModel):
462
462
 
463
463
  @model_serializer(mode="wrap")
464
464
  def serialize_model(self, handler):
465
- optional_fields = [
466
- "event",
467
- "domain",
468
- "key",
469
- "linkId",
470
- "externalId",
471
- "tenantId",
472
- "programId",
473
- "partnerId",
474
- "customerId",
475
- "interval",
476
- "start",
477
- "end",
478
- "timezone",
479
- "country",
480
- "city",
481
- "region",
482
- "continent",
483
- "device",
484
- "browser",
485
- "os",
486
- "trigger",
487
- "referer",
488
- "refererUrl",
489
- "url",
490
- "tagIds",
491
- "folderId",
492
- "groupId",
493
- "root",
494
- "saleType",
495
- "query",
496
- "tagId",
497
- "qr",
498
- "utm_source",
499
- "utm_medium",
500
- "utm_campaign",
501
- "utm_term",
502
- "utm_content",
503
- "ref",
504
- "page",
505
- "limit",
506
- "sortOrder",
507
- "sortBy",
508
- "order",
509
- ]
510
- nullable_fields = [
511
- "utm_source",
512
- "utm_medium",
513
- "utm_campaign",
514
- "utm_term",
515
- "utm_content",
516
- "ref",
517
- ]
518
- null_default_fields = []
519
-
465
+ optional_fields = set(
466
+ [
467
+ "event",
468
+ "domain",
469
+ "key",
470
+ "linkId",
471
+ "externalId",
472
+ "tenantId",
473
+ "programId",
474
+ "partnerId",
475
+ "customerId",
476
+ "interval",
477
+ "start",
478
+ "end",
479
+ "timezone",
480
+ "country",
481
+ "city",
482
+ "region",
483
+ "continent",
484
+ "device",
485
+ "browser",
486
+ "os",
487
+ "trigger",
488
+ "referer",
489
+ "refererUrl",
490
+ "url",
491
+ "tagIds",
492
+ "folderId",
493
+ "groupId",
494
+ "root",
495
+ "saleType",
496
+ "query",
497
+ "tagId",
498
+ "qr",
499
+ "utm_source",
500
+ "utm_medium",
501
+ "utm_campaign",
502
+ "utm_term",
503
+ "utm_content",
504
+ "ref",
505
+ "page",
506
+ "limit",
507
+ "sortOrder",
508
+ "sortBy",
509
+ "order",
510
+ ]
511
+ )
512
+ nullable_fields = set(
513
+ [
514
+ "utm_source",
515
+ "utm_medium",
516
+ "utm_campaign",
517
+ "utm_term",
518
+ "utm_content",
519
+ "ref",
520
+ ]
521
+ )
520
522
  serialized = handler(self)
521
-
522
523
  m = {}
523
524
 
524
525
  for n, f in type(self).model_fields.items():
525
526
  k = f.alias or n
526
527
  val = serialized.get(k)
527
- serialized.pop(k, None)
528
-
529
- optional_nullable = k in optional_fields and k in nullable_fields
530
- is_set = (
531
- self.__pydantic_fields_set__.intersection({n})
532
- or k in null_default_fields
533
- ) # pylint: disable=no-member
534
-
535
- if val is not None and val != UNSET_SENTINEL:
536
- m[k] = val
537
- elif val != UNSET_SENTINEL and (
538
- not k in optional_fields or (optional_nullable and is_set)
539
- ):
540
- m[k] = val
528
+ is_nullable_and_explicitly_set = (
529
+ k in nullable_fields
530
+ and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member
531
+ )
532
+
533
+ if val != UNSET_SENTINEL:
534
+ if (
535
+ val is not None
536
+ or k not in optional_fields
537
+ or is_nullable_and_explicitly_set
538
+ ):
539
+ m[k] = val
541
540
 
542
541
  return m
543
542
 
@@ -582,31 +581,30 @@ class ResponseBodySale(BaseModel):
582
581
 
583
582
  @model_serializer(mode="wrap")
584
583
  def serialize_model(self, handler):
585
- optional_fields = ["invoiceId", "paymentProcessor"]
586
- nullable_fields = ["invoiceId"]
587
- null_default_fields = ["invoiceId"]
588
-
584
+ optional_fields = set(["invoiceId", "paymentProcessor"])
585
+ nullable_fields = set(["invoiceId"])
586
+ null_default_fields = set(["invoiceId"])
589
587
  serialized = handler(self)
590
-
591
588
  m = {}
592
589
 
593
590
  for n, f in type(self).model_fields.items():
594
591
  k = f.alias or n
595
592
  val = serialized.get(k)
596
- serialized.pop(k, None)
597
-
598
- optional_nullable = k in optional_fields and k in nullable_fields
599
- is_set = (
600
- self.__pydantic_fields_set__.intersection({n})
601
- or k in null_default_fields
602
- ) # pylint: disable=no-member
603
-
604
- if val is not None and val != UNSET_SENTINEL:
605
- m[k] = val
606
- elif val != UNSET_SENTINEL and (
607
- not k in optional_fields or (optional_nullable and is_set)
608
- ):
609
- m[k] = val
593
+ is_nullable_and_explicitly_set = (
594
+ k in nullable_fields
595
+ and (
596
+ self.__pydantic_fields_set__.intersection({n})
597
+ or k in null_default_fields
598
+ ) # pylint: disable=no-member
599
+ )
600
+
601
+ if val != UNSET_SENTINEL:
602
+ if (
603
+ val is not None
604
+ or k not in optional_fields
605
+ or is_nullable_and_explicitly_set
606
+ ):
607
+ m[k] = val
610
608
 
611
609
  return m
612
610
 
@@ -868,63 +866,55 @@ class ListEventsResponseBodyLink(BaseModel):
868
866
 
869
867
  @model_serializer(mode="wrap")
870
868
  def serialize_model(self, handler):
871
- optional_fields = [
872
- "testVariants",
873
- "clicks",
874
- "leads",
875
- "conversions",
876
- "sales",
877
- "saleAmount",
878
- ]
879
- nullable_fields = [
880
- "externalId",
881
- "tenantId",
882
- "programId",
883
- "partnerId",
884
- "expiredUrl",
885
- "password",
886
- "title",
887
- "description",
888
- "image",
889
- "video",
890
- "ios",
891
- "android",
892
- "geo",
893
- "tags",
894
- "folderId",
895
- "comments",
896
- "utm_source",
897
- "utm_medium",
898
- "utm_campaign",
899
- "utm_term",
900
- "utm_content",
901
- "testVariants",
902
- "userId",
903
- "tagId",
904
- ]
905
- null_default_fields = []
906
-
869
+ optional_fields = set(
870
+ ["testVariants", "clicks", "leads", "conversions", "sales", "saleAmount"]
871
+ )
872
+ nullable_fields = set(
873
+ [
874
+ "externalId",
875
+ "tenantId",
876
+ "programId",
877
+ "partnerId",
878
+ "expiredUrl",
879
+ "password",
880
+ "title",
881
+ "description",
882
+ "image",
883
+ "video",
884
+ "ios",
885
+ "android",
886
+ "geo",
887
+ "tags",
888
+ "folderId",
889
+ "comments",
890
+ "utm_source",
891
+ "utm_medium",
892
+ "utm_campaign",
893
+ "utm_term",
894
+ "utm_content",
895
+ "testVariants",
896
+ "userId",
897
+ "tagId",
898
+ ]
899
+ )
907
900
  serialized = handler(self)
908
-
909
901
  m = {}
910
902
 
911
903
  for n, f in type(self).model_fields.items():
912
904
  k = f.alias or n
913
905
  val = serialized.get(k)
914
- serialized.pop(k, None)
915
-
916
- optional_nullable = k in optional_fields and k in nullable_fields
917
- is_set = (
918
- self.__pydantic_fields_set__.intersection({n})
919
- or k in null_default_fields
920
- ) # pylint: disable=no-member
921
-
922
- if val is not None and val != UNSET_SENTINEL:
923
- m[k] = val
924
- elif val != UNSET_SENTINEL and (
925
- not k in optional_fields or (optional_nullable and is_set)
926
- ):
927
- m[k] = val
906
+ is_nullable_and_explicitly_set = (
907
+ k in nullable_fields
908
+ and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member
909
+ )
910
+
911
+ if val != UNSET_SENTINEL:
912
+ if (
913
+ val is not None
914
+ or k not in optional_fields
915
+ or is_nullable_and_explicitly_set
916
+ ):
917
+ m[k] = val
928
918
 
929
919
  return m
930
920
 
@@ -980,31 +970,26 @@ class ListEventsResponseBodyClick(BaseModel):
980
970
 
981
971
  @model_serializer(mode="wrap")
982
972
  def serialize_model(self, handler):
983
- optional_fields = ["trigger"]
984
- nullable_fields = ["trigger"]
985
- null_default_fields = []
986
-
973
+ optional_fields = set(["trigger"])
974
+ nullable_fields = set(["trigger"])
987
975
  serialized = handler(self)
988
-
989
976
  m = {}
990
977
 
991
978
  for n, f in type(self).model_fields.items():
992
979
  k = f.alias or n
993
980
  val = serialized.get(k)
994
- serialized.pop(k, None)
995
-
996
- optional_nullable = k in optional_fields and k in nullable_fields
997
- is_set = (
998
- self.__pydantic_fields_set__.intersection({n})
999
- or k in null_default_fields
1000
- ) # pylint: disable=no-member
1001
-
1002
- if val is not None and val != UNSET_SENTINEL:
1003
- m[k] = val
1004
- elif val != UNSET_SENTINEL and (
1005
- not k in optional_fields or (optional_nullable and is_set)
1006
- ):
1007
- m[k] = val
981
+ is_nullable_and_explicitly_set = (
982
+ k in nullable_fields
983
+ and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member
984
+ )
985
+
986
+ if val != UNSET_SENTINEL:
987
+ if (
988
+ val is not None
989
+ or k not in optional_fields
990
+ or is_nullable_and_explicitly_set
991
+ ):
992
+ m[k] = val
1008
993
 
1009
994
  return m
1010
995
 
@@ -1012,36 +997,42 @@ class ListEventsResponseBodyClick(BaseModel):
1012
997
  class ResponseBodyCustomerTypedDict(TypedDict):
1013
998
  id: str
1014
999
  r"""The unique ID of the customer. You may use either the customer's `id` on Dub (obtained via `/customers` endpoint) or their `externalId` (unique ID within your system, prefixed with `ext_`, e.g. `ext_123`)."""
1015
- external_id: str
1016
- r"""Unique identifier for the customer in the client's app."""
1017
1000
  name: str
1018
1001
  r"""Name of the customer."""
1002
+ external_id: str
1003
+ r"""Unique identifier for the customer in the client's app."""
1019
1004
  created_at: str
1020
- r"""The date the customer was created."""
1005
+ r"""The date the customer was created (usually the signup date or trial start date)."""
1021
1006
  email: NotRequired[Nullable[str]]
1022
1007
  r"""Email of the customer."""
1023
1008
  avatar: NotRequired[Nullable[str]]
1024
1009
  r"""Avatar URL of the customer."""
1010
+ stripe_customer_id: NotRequired[Nullable[str]]
1011
+ r"""The customer's Stripe customer ID. This is useful for attributing recurring sale events to the partner who referred the customer."""
1025
1012
  country: NotRequired[Nullable[str]]
1026
1013
  r"""Country of the customer."""
1027
1014
  sales: NotRequired[Nullable[float]]
1028
1015
  r"""Total number of sales for the customer."""
1029
1016
  sale_amount: NotRequired[Nullable[float]]
1030
1017
  r"""Total amount of sales for the customer."""
1018
+ first_sale_at: NotRequired[Nullable[str]]
1019
+ r"""The date the customer made their first sale. Useful for calculating the time to first sale and LTV."""
1020
+ subscription_canceled_at: NotRequired[Nullable[str]]
1021
+ r"""The date the customer canceled their subscription. Useful for calculating LTV and churn rate."""
1031
1022
 
1032
1023
 
1033
1024
  class ResponseBodyCustomer(BaseModel):
1034
1025
  id: str
1035
1026
  r"""The unique ID of the customer. You may use either the customer's `id` on Dub (obtained via `/customers` endpoint) or their `externalId` (unique ID within your system, prefixed with `ext_`, e.g. `ext_123`)."""
1036
1027
 
1037
- external_id: Annotated[str, pydantic.Field(alias="externalId")]
1038
- r"""Unique identifier for the customer in the client's app."""
1039
-
1040
1028
  name: str
1041
1029
  r"""Name of the customer."""
1042
1030
 
1031
+ external_id: Annotated[str, pydantic.Field(alias="externalId")]
1032
+ r"""Unique identifier for the customer in the client's app."""
1033
+
1043
1034
  created_at: Annotated[str, pydantic.Field(alias="createdAt")]
1044
- r"""The date the customer was created."""
1035
+ r"""The date the customer was created (usually the signup date or trial start date)."""
1045
1036
 
1046
1037
  email: OptionalNullable[str] = UNSET
1047
1038
  r"""Email of the customer."""
@@ -1049,6 +1040,11 @@ class ResponseBodyCustomer(BaseModel):
1049
1040
  avatar: OptionalNullable[str] = UNSET
1050
1041
  r"""Avatar URL of the customer."""
1051
1042
 
1043
+ stripe_customer_id: Annotated[
1044
+ OptionalNullable[str], pydantic.Field(alias="stripeCustomerId")
1045
+ ] = UNSET
1046
+ r"""The customer's Stripe customer ID. This is useful for attributing recurring sale events to the partner who referred the customer."""
1047
+
1052
1048
  country: OptionalNullable[str] = UNSET
1053
1049
  r"""Country of the customer."""
1054
1050
 
@@ -1060,33 +1056,60 @@ class ResponseBodyCustomer(BaseModel):
1060
1056
  ] = UNSET
1061
1057
  r"""Total amount of sales for the customer."""
1062
1058
 
1059
+ first_sale_at: Annotated[
1060
+ OptionalNullable[str], pydantic.Field(alias="firstSaleAt")
1061
+ ] = UNSET
1062
+ r"""The date the customer made their first sale. Useful for calculating the time to first sale and LTV."""
1063
+
1064
+ subscription_canceled_at: Annotated[
1065
+ OptionalNullable[str], pydantic.Field(alias="subscriptionCanceledAt")
1066
+ ] = UNSET
1067
+ r"""The date the customer canceled their subscription. Useful for calculating LTV and churn rate."""
1068
+
1063
1069
  @model_serializer(mode="wrap")
1064
1070
  def serialize_model(self, handler):
1065
- optional_fields = ["email", "avatar", "country", "sales", "saleAmount"]
1066
- nullable_fields = ["email", "avatar", "country", "sales", "saleAmount"]
1067
- null_default_fields = []
1068
-
1071
+ optional_fields = set(
1072
+ [
1073
+ "email",
1074
+ "avatar",
1075
+ "stripeCustomerId",
1076
+ "country",
1077
+ "sales",
1078
+ "saleAmount",
1079
+ "firstSaleAt",
1080
+ "subscriptionCanceledAt",
1081
+ ]
1082
+ )
1083
+ nullable_fields = set(
1084
+ [
1085
+ "email",
1086
+ "avatar",
1087
+ "stripeCustomerId",
1088
+ "country",
1089
+ "sales",
1090
+ "saleAmount",
1091
+ "firstSaleAt",
1092
+ "subscriptionCanceledAt",
1093
+ ]
1094
+ )
1069
1095
  serialized = handler(self)
1070
-
1071
1096
  m = {}
1072
1097
 
1073
1098
  for n, f in type(self).model_fields.items():
1074
1099
  k = f.alias or n
1075
1100
  val = serialized.get(k)
1076
- serialized.pop(k, None)
1077
-
1078
- optional_nullable = k in optional_fields and k in nullable_fields
1079
- is_set = (
1080
- self.__pydantic_fields_set__.intersection({n})
1081
- or k in null_default_fields
1082
- ) # pylint: disable=no-member
1083
-
1084
- if val is not None and val != UNSET_SENTINEL:
1085
- m[k] = val
1086
- elif val != UNSET_SENTINEL and (
1087
- not k in optional_fields or (optional_nullable and is_set)
1088
- ):
1089
- m[k] = val
1101
+ is_nullable_and_explicitly_set = (
1102
+ k in nullable_fields
1103
+ and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member
1104
+ )
1105
+
1106
+ if val != UNSET_SENTINEL:
1107
+ if (
1108
+ val is not None
1109
+ or k not in optional_fields
1110
+ or is_nullable_and_explicitly_set
1111
+ ):
1112
+ m[k] = val
1090
1113
 
1091
1114
  return m
1092
1115
 
@@ -1285,31 +1308,26 @@ class SaleEvent(BaseModel):
1285
1308
 
1286
1309
  @model_serializer(mode="wrap")
1287
1310
  def serialize_model(self, handler):
1288
- optional_fields = ["metadata"]
1289
- nullable_fields = ["metadata"]
1290
- null_default_fields = []
1291
-
1311
+ optional_fields = set(["metadata"])
1312
+ nullable_fields = set(["metadata"])
1292
1313
  serialized = handler(self)
1293
-
1294
1314
  m = {}
1295
1315
 
1296
1316
  for n, f in type(self).model_fields.items():
1297
1317
  k = f.alias or n
1298
1318
  val = serialized.get(k)
1299
- serialized.pop(k, None)
1300
-
1301
- optional_nullable = k in optional_fields and k in nullable_fields
1302
- is_set = (
1303
- self.__pydantic_fields_set__.intersection({n})
1304
- or k in null_default_fields
1305
- ) # pylint: disable=no-member
1306
-
1307
- if val is not None and val != UNSET_SENTINEL:
1308
- m[k] = val
1309
- elif val != UNSET_SENTINEL and (
1310
- not k in optional_fields or (optional_nullable and is_set)
1311
- ):
1312
- m[k] = val
1319
+ is_nullable_and_explicitly_set = (
1320
+ k in nullable_fields
1321
+ and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member
1322
+ )
1323
+
1324
+ if val != UNSET_SENTINEL:
1325
+ if (
1326
+ val is not None
1327
+ or k not in optional_fields
1328
+ or is_nullable_and_explicitly_set
1329
+ ):
1330
+ m[k] = val
1313
1331
 
1314
1332
  return m
1315
1333
 
@@ -1369,31 +1387,26 @@ class ResponseBodyClick(BaseModel):
1369
1387
 
1370
1388
  @model_serializer(mode="wrap")
1371
1389
  def serialize_model(self, handler):
1372
- optional_fields = ["trigger"]
1373
- nullable_fields = ["trigger"]
1374
- null_default_fields = []
1375
-
1390
+ optional_fields = set(["trigger"])
1391
+ nullable_fields = set(["trigger"])
1376
1392
  serialized = handler(self)
1377
-
1378
1393
  m = {}
1379
1394
 
1380
1395
  for n, f in type(self).model_fields.items():
1381
1396
  k = f.alias or n
1382
1397
  val = serialized.get(k)
1383
- serialized.pop(k, None)
1384
-
1385
- optional_nullable = k in optional_fields and k in nullable_fields
1386
- is_set = (
1387
- self.__pydantic_fields_set__.intersection({n})
1388
- or k in null_default_fields
1389
- ) # pylint: disable=no-member
1390
-
1391
- if val is not None and val != UNSET_SENTINEL:
1392
- m[k] = val
1393
- elif val != UNSET_SENTINEL and (
1394
- not k in optional_fields or (optional_nullable and is_set)
1395
- ):
1396
- m[k] = val
1398
+ is_nullable_and_explicitly_set = (
1399
+ k in nullable_fields
1400
+ and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member
1401
+ )
1402
+
1403
+ if val != UNSET_SENTINEL:
1404
+ if (
1405
+ val is not None
1406
+ or k not in optional_fields
1407
+ or is_nullable_and_explicitly_set
1408
+ ):
1409
+ m[k] = val
1397
1410
 
1398
1411
  return m
1399
1412
 
@@ -1655,63 +1668,55 @@ class ResponseBodyLink(BaseModel):
1655
1668
 
1656
1669
  @model_serializer(mode="wrap")
1657
1670
  def serialize_model(self, handler):
1658
- optional_fields = [
1659
- "testVariants",
1660
- "clicks",
1661
- "leads",
1662
- "conversions",
1663
- "sales",
1664
- "saleAmount",
1665
- ]
1666
- nullable_fields = [
1667
- "externalId",
1668
- "tenantId",
1669
- "programId",
1670
- "partnerId",
1671
- "expiredUrl",
1672
- "password",
1673
- "title",
1674
- "description",
1675
- "image",
1676
- "video",
1677
- "ios",
1678
- "android",
1679
- "geo",
1680
- "tags",
1681
- "folderId",
1682
- "comments",
1683
- "utm_source",
1684
- "utm_medium",
1685
- "utm_campaign",
1686
- "utm_term",
1687
- "utm_content",
1688
- "testVariants",
1689
- "userId",
1690
- "tagId",
1691
- ]
1692
- null_default_fields = []
1693
-
1671
+ optional_fields = set(
1672
+ ["testVariants", "clicks", "leads", "conversions", "sales", "saleAmount"]
1673
+ )
1674
+ nullable_fields = set(
1675
+ [
1676
+ "externalId",
1677
+ "tenantId",
1678
+ "programId",
1679
+ "partnerId",
1680
+ "expiredUrl",
1681
+ "password",
1682
+ "title",
1683
+ "description",
1684
+ "image",
1685
+ "video",
1686
+ "ios",
1687
+ "android",
1688
+ "geo",
1689
+ "tags",
1690
+ "folderId",
1691
+ "comments",
1692
+ "utm_source",
1693
+ "utm_medium",
1694
+ "utm_campaign",
1695
+ "utm_term",
1696
+ "utm_content",
1697
+ "testVariants",
1698
+ "userId",
1699
+ "tagId",
1700
+ ]
1701
+ )
1694
1702
  serialized = handler(self)
1695
-
1696
1703
  m = {}
1697
1704
 
1698
1705
  for n, f in type(self).model_fields.items():
1699
1706
  k = f.alias or n
1700
1707
  val = serialized.get(k)
1701
- serialized.pop(k, None)
1702
-
1703
- optional_nullable = k in optional_fields and k in nullable_fields
1704
- is_set = (
1705
- self.__pydantic_fields_set__.intersection({n})
1706
- or k in null_default_fields
1707
- ) # pylint: disable=no-member
1708
-
1709
- if val is not None and val != UNSET_SENTINEL:
1710
- m[k] = val
1711
- elif val != UNSET_SENTINEL and (
1712
- not k in optional_fields or (optional_nullable and is_set)
1713
- ):
1714
- m[k] = val
1708
+ is_nullable_and_explicitly_set = (
1709
+ k in nullable_fields
1710
+ and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member
1711
+ )
1712
+
1713
+ if val != UNSET_SENTINEL:
1714
+ if (
1715
+ val is not None
1716
+ or k not in optional_fields
1717
+ or is_nullable_and_explicitly_set
1718
+ ):
1719
+ m[k] = val
1715
1720
 
1716
1721
  return m
1717
1722
 
@@ -1719,36 +1724,42 @@ class ResponseBodyLink(BaseModel):
1719
1724
  class ListEventsResponseBodyCustomerTypedDict(TypedDict):
1720
1725
  id: str
1721
1726
  r"""The unique ID of the customer. You may use either the customer's `id` on Dub (obtained via `/customers` endpoint) or their `externalId` (unique ID within your system, prefixed with `ext_`, e.g. `ext_123`)."""
1722
- external_id: str
1723
- r"""Unique identifier for the customer in the client's app."""
1724
1727
  name: str
1725
1728
  r"""Name of the customer."""
1729
+ external_id: str
1730
+ r"""Unique identifier for the customer in the client's app."""
1726
1731
  created_at: str
1727
- r"""The date the customer was created."""
1732
+ r"""The date the customer was created (usually the signup date or trial start date)."""
1728
1733
  email: NotRequired[Nullable[str]]
1729
1734
  r"""Email of the customer."""
1730
1735
  avatar: NotRequired[Nullable[str]]
1731
1736
  r"""Avatar URL of the customer."""
1737
+ stripe_customer_id: NotRequired[Nullable[str]]
1738
+ r"""The customer's Stripe customer ID. This is useful for attributing recurring sale events to the partner who referred the customer."""
1732
1739
  country: NotRequired[Nullable[str]]
1733
1740
  r"""Country of the customer."""
1734
1741
  sales: NotRequired[Nullable[float]]
1735
1742
  r"""Total number of sales for the customer."""
1736
1743
  sale_amount: NotRequired[Nullable[float]]
1737
1744
  r"""Total amount of sales for the customer."""
1745
+ first_sale_at: NotRequired[Nullable[str]]
1746
+ r"""The date the customer made their first sale. Useful for calculating the time to first sale and LTV."""
1747
+ subscription_canceled_at: NotRequired[Nullable[str]]
1748
+ r"""The date the customer canceled their subscription. Useful for calculating LTV and churn rate."""
1738
1749
 
1739
1750
 
1740
1751
  class ListEventsResponseBodyCustomer(BaseModel):
1741
1752
  id: str
1742
1753
  r"""The unique ID of the customer. You may use either the customer's `id` on Dub (obtained via `/customers` endpoint) or their `externalId` (unique ID within your system, prefixed with `ext_`, e.g. `ext_123`)."""
1743
1754
 
1744
- external_id: Annotated[str, pydantic.Field(alias="externalId")]
1745
- r"""Unique identifier for the customer in the client's app."""
1746
-
1747
1755
  name: str
1748
1756
  r"""Name of the customer."""
1749
1757
 
1758
+ external_id: Annotated[str, pydantic.Field(alias="externalId")]
1759
+ r"""Unique identifier for the customer in the client's app."""
1760
+
1750
1761
  created_at: Annotated[str, pydantic.Field(alias="createdAt")]
1751
- r"""The date the customer was created."""
1762
+ r"""The date the customer was created (usually the signup date or trial start date)."""
1752
1763
 
1753
1764
  email: OptionalNullable[str] = UNSET
1754
1765
  r"""Email of the customer."""
@@ -1756,6 +1767,11 @@ class ListEventsResponseBodyCustomer(BaseModel):
1756
1767
  avatar: OptionalNullable[str] = UNSET
1757
1768
  r"""Avatar URL of the customer."""
1758
1769
 
1770
+ stripe_customer_id: Annotated[
1771
+ OptionalNullable[str], pydantic.Field(alias="stripeCustomerId")
1772
+ ] = UNSET
1773
+ r"""The customer's Stripe customer ID. This is useful for attributing recurring sale events to the partner who referred the customer."""
1774
+
1759
1775
  country: OptionalNullable[str] = UNSET
1760
1776
  r"""Country of the customer."""
1761
1777
 
@@ -1767,33 +1783,60 @@ class ListEventsResponseBodyCustomer(BaseModel):
1767
1783
  ] = UNSET
1768
1784
  r"""Total amount of sales for the customer."""
1769
1785
 
1786
+ first_sale_at: Annotated[
1787
+ OptionalNullable[str], pydantic.Field(alias="firstSaleAt")
1788
+ ] = UNSET
1789
+ r"""The date the customer made their first sale. Useful for calculating the time to first sale and LTV."""
1790
+
1791
+ subscription_canceled_at: Annotated[
1792
+ OptionalNullable[str], pydantic.Field(alias="subscriptionCanceledAt")
1793
+ ] = UNSET
1794
+ r"""The date the customer canceled their subscription. Useful for calculating LTV and churn rate."""
1795
+
1770
1796
  @model_serializer(mode="wrap")
1771
1797
  def serialize_model(self, handler):
1772
- optional_fields = ["email", "avatar", "country", "sales", "saleAmount"]
1773
- nullable_fields = ["email", "avatar", "country", "sales", "saleAmount"]
1774
- null_default_fields = []
1775
-
1798
+ optional_fields = set(
1799
+ [
1800
+ "email",
1801
+ "avatar",
1802
+ "stripeCustomerId",
1803
+ "country",
1804
+ "sales",
1805
+ "saleAmount",
1806
+ "firstSaleAt",
1807
+ "subscriptionCanceledAt",
1808
+ ]
1809
+ )
1810
+ nullable_fields = set(
1811
+ [
1812
+ "email",
1813
+ "avatar",
1814
+ "stripeCustomerId",
1815
+ "country",
1816
+ "sales",
1817
+ "saleAmount",
1818
+ "firstSaleAt",
1819
+ "subscriptionCanceledAt",
1820
+ ]
1821
+ )
1776
1822
  serialized = handler(self)
1777
-
1778
1823
  m = {}
1779
1824
 
1780
1825
  for n, f in type(self).model_fields.items():
1781
1826
  k = f.alias or n
1782
1827
  val = serialized.get(k)
1783
- serialized.pop(k, None)
1784
-
1785
- optional_nullable = k in optional_fields and k in nullable_fields
1786
- is_set = (
1787
- self.__pydantic_fields_set__.intersection({n})
1788
- or k in null_default_fields
1789
- ) # pylint: disable=no-member
1790
-
1791
- if val is not None and val != UNSET_SENTINEL:
1792
- m[k] = val
1793
- elif val != UNSET_SENTINEL and (
1794
- not k in optional_fields or (optional_nullable and is_set)
1795
- ):
1796
- m[k] = val
1828
+ is_nullable_and_explicitly_set = (
1829
+ k in nullable_fields
1830
+ and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member
1831
+ )
1832
+
1833
+ if val != UNSET_SENTINEL:
1834
+ if (
1835
+ val is not None
1836
+ or k not in optional_fields
1837
+ or is_nullable_and_explicitly_set
1838
+ ):
1839
+ m[k] = val
1797
1840
 
1798
1841
  return m
1799
1842
 
@@ -1958,31 +2001,26 @@ class LeadEvent(BaseModel):
1958
2001
 
1959
2002
  @model_serializer(mode="wrap")
1960
2003
  def serialize_model(self, handler):
1961
- optional_fields = ["metadata"]
1962
- nullable_fields = ["metadata"]
1963
- null_default_fields = []
1964
-
2004
+ optional_fields = set(["metadata"])
2005
+ nullable_fields = set(["metadata"])
1965
2006
  serialized = handler(self)
1966
-
1967
2007
  m = {}
1968
2008
 
1969
2009
  for n, f in type(self).model_fields.items():
1970
2010
  k = f.alias or n
1971
2011
  val = serialized.get(k)
1972
- serialized.pop(k, None)
1973
-
1974
- optional_nullable = k in optional_fields and k in nullable_fields
1975
- is_set = (
1976
- self.__pydantic_fields_set__.intersection({n})
1977
- or k in null_default_fields
1978
- ) # pylint: disable=no-member
1979
-
1980
- if val is not None and val != UNSET_SENTINEL:
1981
- m[k] = val
1982
- elif val != UNSET_SENTINEL and (
1983
- not k in optional_fields or (optional_nullable and is_set)
1984
- ):
1985
- m[k] = val
2012
+ is_nullable_and_explicitly_set = (
2013
+ k in nullable_fields
2014
+ and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member
2015
+ )
2016
+
2017
+ if val != UNSET_SENTINEL:
2018
+ if (
2019
+ val is not None
2020
+ or k not in optional_fields
2021
+ or is_nullable_and_explicitly_set
2022
+ ):
2023
+ m[k] = val
1986
2024
 
1987
2025
  return m
1988
2026
 
@@ -2042,31 +2080,26 @@ class ListEventsResponseBodyEventsClick(BaseModel):
2042
2080
 
2043
2081
  @model_serializer(mode="wrap")
2044
2082
  def serialize_model(self, handler):
2045
- optional_fields = ["trigger"]
2046
- nullable_fields = ["trigger"]
2047
- null_default_fields = []
2048
-
2083
+ optional_fields = set(["trigger"])
2084
+ nullable_fields = set(["trigger"])
2049
2085
  serialized = handler(self)
2050
-
2051
2086
  m = {}
2052
2087
 
2053
2088
  for n, f in type(self).model_fields.items():
2054
2089
  k = f.alias or n
2055
2090
  val = serialized.get(k)
2056
- serialized.pop(k, None)
2057
-
2058
- optional_nullable = k in optional_fields and k in nullable_fields
2059
- is_set = (
2060
- self.__pydantic_fields_set__.intersection({n})
2061
- or k in null_default_fields
2062
- ) # pylint: disable=no-member
2063
-
2064
- if val is not None and val != UNSET_SENTINEL:
2065
- m[k] = val
2066
- elif val != UNSET_SENTINEL and (
2067
- not k in optional_fields or (optional_nullable and is_set)
2068
- ):
2069
- m[k] = val
2091
+ is_nullable_and_explicitly_set = (
2092
+ k in nullable_fields
2093
+ and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member
2094
+ )
2095
+
2096
+ if val != UNSET_SENTINEL:
2097
+ if (
2098
+ val is not None
2099
+ or k not in optional_fields
2100
+ or is_nullable_and_explicitly_set
2101
+ ):
2102
+ m[k] = val
2070
2103
 
2071
2104
  return m
2072
2105
 
@@ -2326,63 +2359,55 @@ class ListEventsResponseBodyEventsLink(BaseModel):
2326
2359
 
2327
2360
  @model_serializer(mode="wrap")
2328
2361
  def serialize_model(self, handler):
2329
- optional_fields = [
2330
- "testVariants",
2331
- "clicks",
2332
- "leads",
2333
- "conversions",
2334
- "sales",
2335
- "saleAmount",
2336
- ]
2337
- nullable_fields = [
2338
- "externalId",
2339
- "tenantId",
2340
- "programId",
2341
- "partnerId",
2342
- "expiredUrl",
2343
- "password",
2344
- "title",
2345
- "description",
2346
- "image",
2347
- "video",
2348
- "ios",
2349
- "android",
2350
- "geo",
2351
- "tags",
2352
- "folderId",
2353
- "comments",
2354
- "utm_source",
2355
- "utm_medium",
2356
- "utm_campaign",
2357
- "utm_term",
2358
- "utm_content",
2359
- "testVariants",
2360
- "userId",
2361
- "tagId",
2362
- ]
2363
- null_default_fields = []
2364
-
2362
+ optional_fields = set(
2363
+ ["testVariants", "clicks", "leads", "conversions", "sales", "saleAmount"]
2364
+ )
2365
+ nullable_fields = set(
2366
+ [
2367
+ "externalId",
2368
+ "tenantId",
2369
+ "programId",
2370
+ "partnerId",
2371
+ "expiredUrl",
2372
+ "password",
2373
+ "title",
2374
+ "description",
2375
+ "image",
2376
+ "video",
2377
+ "ios",
2378
+ "android",
2379
+ "geo",
2380
+ "tags",
2381
+ "folderId",
2382
+ "comments",
2383
+ "utm_source",
2384
+ "utm_medium",
2385
+ "utm_campaign",
2386
+ "utm_term",
2387
+ "utm_content",
2388
+ "testVariants",
2389
+ "userId",
2390
+ "tagId",
2391
+ ]
2392
+ )
2365
2393
  serialized = handler(self)
2366
-
2367
2394
  m = {}
2368
2395
 
2369
2396
  for n, f in type(self).model_fields.items():
2370
2397
  k = f.alias or n
2371
2398
  val = serialized.get(k)
2372
- serialized.pop(k, None)
2373
-
2374
- optional_nullable = k in optional_fields and k in nullable_fields
2375
- is_set = (
2376
- self.__pydantic_fields_set__.intersection({n})
2377
- or k in null_default_fields
2378
- ) # pylint: disable=no-member
2379
-
2380
- if val is not None and val != UNSET_SENTINEL:
2381
- m[k] = val
2382
- elif val != UNSET_SENTINEL and (
2383
- not k in optional_fields or (optional_nullable and is_set)
2384
- ):
2385
- m[k] = val
2399
+ is_nullable_and_explicitly_set = (
2400
+ k in nullable_fields
2401
+ and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member
2402
+ )
2403
+
2404
+ if val != UNSET_SENTINEL:
2405
+ if (
2406
+ val is not None
2407
+ or k not in optional_fields
2408
+ or is_nullable_and_explicitly_set
2409
+ ):
2410
+ m[k] = val
2386
2411
 
2387
2412
  return m
2388
2413