@oneuptime/common 12.0.17 → 12.0.19

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 (220) hide show
  1. package/Models/DatabaseModels/EnterpriseLicense.ts +23 -0
  2. package/Models/DatabaseModels/Index.ts +4 -2
  3. package/Models/DatabaseModels/Monitor.ts +39 -9
  4. package/Models/DatabaseModels/UserMicrosoftTeams.ts +344 -0
  5. package/Models/DatabaseModels/UserNotificationRule.ts +144 -8
  6. package/Models/DatabaseModels/UserNotificationSetting.ts +34 -0
  7. package/Models/DatabaseModels/UserOnCallLogTimeline.ts +95 -0
  8. package/Models/DatabaseModels/UserSlack.ts +341 -0
  9. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +9 -0
  10. package/Server/API/UserMicrosoftTeamsAPI.ts +127 -0
  11. package/Server/API/UserSlackAPI.ts +125 -0
  12. package/Server/EnvironmentConfig.ts +12 -107
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1788700000000-RemoveMarketingConversionUploadState.ts +33 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail.ts +51 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.ts +117 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.ts +197 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
  18. package/Server/Infrastructure/Queue.ts +7 -0
  19. package/Server/Services/BillingService.ts +8 -0
  20. package/Server/Services/EnterpriseLicenseService.ts +60 -0
  21. package/Server/Services/Index.ts +6 -2
  22. package/Server/Services/LlmLogService.ts +25 -9
  23. package/Server/Services/OnCallReadinessService.ts +86 -2
  24. package/Server/Services/ProjectService.ts +67 -0
  25. package/Server/Services/UserMicrosoftTeamsService.ts +208 -0
  26. package/Server/Services/UserNotificationMethodAdminService.ts +167 -5
  27. package/Server/Services/UserNotificationRuleAdminService.ts +40 -6
  28. package/Server/Services/UserNotificationRuleService.ts +589 -11
  29. package/Server/Services/UserNotificationSettingService.ts +138 -0
  30. package/Server/Services/UserService.ts +28 -0
  31. package/Server/Services/UserSlackService.ts +218 -0
  32. package/Server/Services/WorkspaceProjectAuthTokenService.ts +67 -1
  33. package/Server/Services/WorkspaceUserAuthTokenService.ts +73 -0
  34. package/Server/Services/WorkspaceUserNotificationService.ts +190 -0
  35. package/Server/Utils/Marketing/MarketingEventUtil.ts +145 -0
  36. package/Server/Utils/Marketing/MarketingEventWebhook.ts +114 -0
  37. package/Server/Utils/Monitor/MonitorLogUtil.ts +14 -1
  38. package/Server/Utils/Monitor/MonitorPayloadRedaction.ts +321 -0
  39. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +191 -4
  40. package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +22 -14
  41. package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +22 -11
  42. package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +37 -1
  43. package/Tests/App/Dashboard/OnCallRulesTable.test.tsx +18 -10
  44. package/Tests/App/Dashboard/PayAsYouGoNotices.test.tsx +17 -0
  45. package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +27 -1
  46. package/Tests/Models/DatabaseModels/MonitorSecretKeyColumnAccessControl.test.ts +214 -0
  47. package/Tests/Server/API/OnCallReadinessAPI.test.ts +145 -26
  48. package/Tests/Server/API/UserNotificationMethodAdminAPI.test.ts +2 -2
  49. package/Tests/Server/Services/AIServiceDailyBudget.test.ts +11 -1
  50. package/Tests/Server/Services/AdminRuleEditGuards.test.ts +22 -8
  51. package/Tests/Server/Services/BillingService.test.ts +20 -0
  52. package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +18 -2
  53. package/Tests/Server/Services/EpisodeRuleSeverityRepair.test.ts +5 -1
  54. package/Tests/Server/Services/LlmLogServiceTokenAggregateParams.test.ts +640 -0
  55. package/Tests/Server/Services/NotificationChannelEventCoverage.test.ts +246 -22
  56. package/Tests/Server/Services/NotificationDeletionImpact.test.ts +136 -8
  57. package/Tests/Server/Services/OnCallNotificationFallback.test.ts +191 -8
  58. package/Tests/Server/Services/OnCallReadinessService.test.ts +343 -18
  59. package/Tests/Server/Services/SeverityRuleBackfill.test.ts +5 -1
  60. package/Tests/Server/Services/UserMicrosoftTeamsService.test.ts +343 -0
  61. package/Tests/Server/Services/UserNotificationMethodAdminService.test.ts +209 -8
  62. package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +22 -6
  63. package/Tests/Server/Services/UserNotificationRuleDefaultCreation.test.ts +12 -6
  64. package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +37 -3
  65. package/Tests/Server/Services/UserNotificationRuleWorkspaceDelivery.test.ts +669 -0
  66. package/Tests/Server/Services/UserNotificationSettingWorkspaceChannels.test.ts +410 -0
  67. package/Tests/Server/Services/UserSlackService.test.ts +407 -0
  68. package/Tests/Server/Services/WorkspaceProjectAuthTokenDisconnectCascade.test.ts +224 -0
  69. package/Tests/Server/Services/WorkspaceUserAuthTokenNotificationMethodCascade.test.ts +189 -0
  70. package/Tests/Server/Services/WorkspaceUserNotificationService.test.ts +496 -0
  71. package/Tests/Server/Types/Database/Permissions/AdminNotificationRuleAccess.test.ts +30 -17
  72. package/Tests/Server/Types/Database/Permissions/CreateOwnershipScoping.test.ts +8 -3
  73. package/Tests/Server/Types/Database/Permissions/OwnerOnlyColumns.test.ts +72 -3
  74. package/Tests/Server/Types/Database/Permissions/UserNotificationRuleScoping.test.ts +22 -18
  75. package/Tests/Server/Types/Database/Permissions/WorkspaceMethodStampedColumnCreate.test.ts +178 -0
  76. package/Tests/Server/Utils/Marketing/MarketingEventUtil.test.ts +259 -0
  77. package/Tests/Server/Utils/Marketing/MarketingEventWebhook.test.ts +257 -0
  78. package/Tests/Server/Utils/Monitor/MonitorLogUtilRedaction.test.ts +300 -0
  79. package/Tests/Server/Utils/Monitor/MonitorPayloadRedaction.test.ts +381 -0
  80. package/Tests/Server/Utils/Runbook/RunbookExecutePermission.test.ts +228 -0
  81. package/Tests/Server/Utils/SessionReplay/SessionReplayErasureTombstone.test.ts +189 -0
  82. package/Tests/Server/Utils/SessionReplay/SessionReplayUsage.test.ts +205 -0
  83. package/Tests/Server/Utils/Telemetry/AppMetrics.test.ts +275 -0
  84. package/Tests/Server/Utils/Telemetry/TelemetryContext.test.ts +279 -0
  85. package/Tests/Server/Utils/Workspace/MicrosoftTeamsDirectMessage.test.ts +454 -0
  86. package/Tests/Types/Kubernetes/KubernetesObjectParsers.test.ts +794 -0
  87. package/Tests/Types/Kubernetes/KubernetesRightSizing.test.ts +53 -0
  88. package/Tests/Types/Measurement/MeasurementAggregationType.test.ts +100 -0
  89. package/Tests/Types/Monitor/MonitorStepConfigHelpers.test.ts +114 -0
  90. package/Tests/Types/Workspace/WorkspaceType.test.ts +53 -0
  91. package/Tests/UI/Utils/NotificationMethodUtil.test.ts +137 -9
  92. package/Tests/UI/Utils/PermissionGate.test.ts +105 -0
  93. package/Tests/Utils/Dashboard/Components/DashboardComponentDefaults.test.ts +257 -0
  94. package/Types/Billing/PayAsYouGoPricing.ts +1 -1
  95. package/Types/Marketing/MarketingEvent.ts +97 -0
  96. package/UI/Utils/NotificationMethodUtil.ts +41 -1
  97. package/UI/Utils/PermissionGate.ts +56 -0
  98. package/build/dist/Models/DatabaseModels/EnterpriseLicense.js +24 -0
  99. package/build/dist/Models/DatabaseModels/EnterpriseLicense.js.map +1 -1
  100. package/build/dist/Models/DatabaseModels/Index.js +4 -2
  101. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  102. package/build/dist/Models/DatabaseModels/Monitor.js +39 -9
  103. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  104. package/build/dist/Models/DatabaseModels/UserMicrosoftTeams.js +363 -0
  105. package/build/dist/Models/DatabaseModels/UserMicrosoftTeams.js.map +1 -0
  106. package/build/dist/Models/DatabaseModels/UserNotificationRule.js +145 -8
  107. package/build/dist/Models/DatabaseModels/UserNotificationRule.js.map +1 -1
  108. package/build/dist/Models/DatabaseModels/UserNotificationSetting.js +38 -0
  109. package/build/dist/Models/DatabaseModels/UserNotificationSetting.js.map +1 -1
  110. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +96 -0
  111. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
  112. package/build/dist/Models/DatabaseModels/UserSlack.js +361 -0
  113. package/build/dist/Models/DatabaseModels/UserSlack.js.map +1 -0
  114. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  115. package/build/dist/Server/API/UserMicrosoftTeamsAPI.js +82 -0
  116. package/build/dist/Server/API/UserMicrosoftTeamsAPI.js.map +1 -0
  117. package/build/dist/Server/API/UserSlackAPI.js +81 -0
  118. package/build/dist/Server/API/UserSlackAPI.js.map +1 -0
  119. package/build/dist/Server/EnvironmentConfig.js +10 -70
  120. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  121. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788700000000-RemoveMarketingConversionUploadState.js +26 -0
  122. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788700000000-RemoveMarketingConversionUploadState.js.map +1 -0
  123. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail.js +37 -0
  124. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail.js.map +1 -0
  125. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.js +106 -0
  126. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.js.map +1 -0
  127. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.js +78 -0
  128. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.js.map +1 -0
  129. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
  130. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  131. package/build/dist/Server/Infrastructure/Queue.js +7 -0
  132. package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
  133. package/build/dist/Server/Services/BillingService.js +6 -0
  134. package/build/dist/Server/Services/BillingService.js.map +1 -1
  135. package/build/dist/Server/Services/EnterpriseLicenseService.js +65 -0
  136. package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
  137. package/build/dist/Server/Services/Index.js +6 -2
  138. package/build/dist/Server/Services/Index.js.map +1 -1
  139. package/build/dist/Server/Services/LlmLogService.js +25 -9
  140. package/build/dist/Server/Services/LlmLogService.js.map +1 -1
  141. package/build/dist/Server/Services/OnCallReadinessService.js +74 -2
  142. package/build/dist/Server/Services/OnCallReadinessService.js.map +1 -1
  143. package/build/dist/Server/Services/ProjectService.js +56 -0
  144. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  145. package/build/dist/Server/Services/UserMicrosoftTeamsService.js +183 -0
  146. package/build/dist/Server/Services/UserMicrosoftTeamsService.js.map +1 -0
  147. package/build/dist/Server/Services/UserNotificationMethodAdminService.js +138 -2
  148. package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -1
  149. package/build/dist/Server/Services/UserNotificationRuleAdminService.js +32 -4
  150. package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -1
  151. package/build/dist/Server/Services/UserNotificationRuleService.js +499 -67
  152. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  153. package/build/dist/Server/Services/UserNotificationSettingService.js +118 -0
  154. package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
  155. package/build/dist/Server/Services/UserService.js +26 -1
  156. package/build/dist/Server/Services/UserService.js.map +1 -1
  157. package/build/dist/Server/Services/UserSlackService.js +195 -0
  158. package/build/dist/Server/Services/UserSlackService.js.map +1 -0
  159. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +64 -1
  160. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -1
  161. package/build/dist/Server/Services/WorkspaceUserAuthTokenService.js +70 -0
  162. package/build/dist/Server/Services/WorkspaceUserAuthTokenService.js.map +1 -1
  163. package/build/dist/Server/Services/WorkspaceUserNotificationService.js +129 -0
  164. package/build/dist/Server/Services/WorkspaceUserNotificationService.js.map +1 -0
  165. package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js +94 -0
  166. package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js.map +1 -0
  167. package/build/dist/Server/Utils/Marketing/MarketingEventWebhook.js +93 -0
  168. package/build/dist/Server/Utils/Marketing/MarketingEventWebhook.js.map +1 -0
  169. package/build/dist/Server/Utils/Monitor/MonitorLogUtil.js +12 -1
  170. package/build/dist/Server/Utils/Monitor/MonitorLogUtil.js.map +1 -1
  171. package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js +233 -0
  172. package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js.map +1 -0
  173. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +141 -4
  174. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  175. package/build/dist/Types/Billing/PayAsYouGoPricing.js +1 -1
  176. package/build/dist/Types/Billing/PayAsYouGoPricing.js.map +1 -1
  177. package/build/dist/Types/Marketing/MarketingEvent.js +43 -0
  178. package/build/dist/Types/Marketing/MarketingEvent.js.map +1 -0
  179. package/build/dist/UI/Utils/NotificationMethodUtil.js +33 -1
  180. package/build/dist/UI/Utils/NotificationMethodUtil.js.map +1 -1
  181. package/build/dist/UI/Utils/PermissionGate.js +32 -0
  182. package/build/dist/UI/Utils/PermissionGate.js.map +1 -1
  183. package/package.json +1 -1
  184. package/Models/DatabaseModels/MarketingConversion.ts +0 -410
  185. package/Server/Services/MarketingConversionService.ts +0 -10
  186. package/Server/Utils/Marketing/ConversionUploadProvider.ts +0 -215
  187. package/Server/Utils/Marketing/ConversionUploadProviders.ts +0 -22
  188. package/Server/Utils/Marketing/Providers/GoogleAds.ts +0 -333
  189. package/Server/Utils/Marketing/Providers/LinkedIn.ts +0 -185
  190. package/Server/Utils/Marketing/Providers/Meta.ts +0 -182
  191. package/Server/Utils/Marketing/Providers/MicrosoftAds.ts +0 -223
  192. package/Server/Utils/Marketing/Providers/Reddit.ts +0 -208
  193. package/Tests/Server/Utils/Marketing/AdUploadableConversionTypes.test.ts +0 -315
  194. package/Tests/Server/Utils/Marketing/ConversionUploadProvider.test.ts +0 -300
  195. package/Tests/Server/Utils/Marketing/GoogleAds.test.ts +0 -592
  196. package/Tests/Server/Utils/Marketing/LinkedIn.test.ts +0 -282
  197. package/Tests/Server/Utils/Marketing/Meta.test.ts +0 -304
  198. package/Tests/Server/Utils/Marketing/MicrosoftAds.test.ts +0 -263
  199. package/Tests/Server/Utils/Marketing/Reddit.test.ts +0 -259
  200. package/Types/Marketing/MarketingConversion.ts +0 -53
  201. package/build/dist/Models/DatabaseModels/MarketingConversion.js +0 -445
  202. package/build/dist/Models/DatabaseModels/MarketingConversion.js.map +0 -1
  203. package/build/dist/Server/Services/MarketingConversionService.js +0 -9
  204. package/build/dist/Server/Services/MarketingConversionService.js.map +0 -1
  205. package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js +0 -93
  206. package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js.map +0 -1
  207. package/build/dist/Server/Utils/Marketing/ConversionUploadProviders.js +0 -20
  208. package/build/dist/Server/Utils/Marketing/ConversionUploadProviders.js.map +0 -1
  209. package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js +0 -221
  210. package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js.map +0 -1
  211. package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js +0 -137
  212. package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js.map +0 -1
  213. package/build/dist/Server/Utils/Marketing/Providers/Meta.js +0 -133
  214. package/build/dist/Server/Utils/Marketing/Providers/Meta.js.map +0 -1
  215. package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js +0 -143
  216. package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js.map +0 -1
  217. package/build/dist/Server/Utils/Marketing/Providers/Reddit.js +0 -145
  218. package/build/dist/Server/Utils/Marketing/Providers/Reddit.js.map +0 -1
  219. package/build/dist/Types/Marketing/MarketingConversion.js +0 -52
  220. package/build/dist/Types/Marketing/MarketingConversion.js.map +0 -1
@@ -1,221 +0,0 @@
1
- import axios from "axios";
2
- import { GoogleAdsApiVersion, GoogleAdsCustomerId, GoogleAdsDeveloperToken, GoogleAdsEnhancedConversionsForLeadsEnabled, GoogleAdsLoginCustomerId, GoogleAdsMeetingBookedConversionActionId, GoogleAdsOAuthClientId, GoogleAdsOAuthClientSecret, GoogleAdsOAuthRefreshToken, GoogleAdsPaidSubscriptionConversionActionId, GoogleAdsSignUpConversionActionId, } from "../../../EnvironmentConfig";
3
- import ConversionUploadProvider from "../ConversionUploadProvider";
4
- import { MarketingConversionType } from "../../../../Types/Marketing/MarketingConversion";
5
- import logger from "../../Logger";
6
- const REQUEST_TIMEOUT_MS = 30000;
7
- // Google Ads accepts click conversions up to 90 days after the click.
8
- const GOOGLE_MAX_CONVERSION_AGE_IN_DAYS = 90;
9
- /*
10
- * Google Ads offline click conversions
11
- * (customers/{id}:uploadClickConversions) with OAuth2 refresh-token auth.
12
- * Supports gclid, wbraid and gbraid click ids, plus enhanced conversions — a
13
- * SHA-256 email in userIdentifiers. Uses partialFailure so valid conversions
14
- * succeed even when others in the batch fail.
15
- *
16
- * A hashed email is attached to EVERY upload that has one, which only improves
17
- * matching. Uploading a conversion that has ONLY a hashed email — enhanced
18
- * conversions for leads, and the only way a sales-led deal months after the
19
- * click is ever attributed — additionally requires the account to be set up
20
- * for it, so it is gated on
21
- * GOOGLE_ADS_ENHANCED_CONVERSIONS_FOR_LEADS_ENABLED.
22
- */
23
- export default class GoogleAdsProvider extends ConversionUploadProvider {
24
- constructor() {
25
- super(...arguments);
26
- this.key = "google";
27
- this.displayName = "Google Ads";
28
- this.cachedAccessToken = null;
29
- this.cachedAccessTokenExpiresAt = 0;
30
- }
31
- isConfigured() {
32
- return Boolean(GoogleAdsDeveloperToken &&
33
- GoogleAdsOAuthClientId &&
34
- GoogleAdsOAuthClientSecret &&
35
- GoogleAdsOAuthRefreshToken &&
36
- GoogleAdsCustomerId);
37
- }
38
- getProviderSkipReason(conversion) {
39
- if (!this.getGoogleClickId(conversion) &&
40
- !this.canUploadAsLead(conversion)) {
41
- return {
42
- reason: GoogleAdsEnhancedConversionsForLeadsEnabled
43
- ? "No Google click id (gclid/wbraid/gbraid) and no email to match on"
44
- : "No Google click id (gclid/wbraid/gbraid)",
45
- isPermanent: true,
46
- };
47
- }
48
- if (!this.getConversionActionId(conversion)) {
49
- // Config gap — leave pending so it uploads once the id is configured.
50
- return {
51
- reason: "No Google Ads conversion action id configured for this conversion type",
52
- isPermanent: false,
53
- };
54
- }
55
- if (this.isOlderThanDays(conversion, GOOGLE_MAX_CONVERSION_AGE_IN_DAYS)) {
56
- return {
57
- reason: "Conversion older than Google's 90-day click window",
58
- isPermanent: true,
59
- };
60
- }
61
- return null;
62
- }
63
- getGoogleClickId(conversion) {
64
- const gclid = this.getClickId(conversion, "gclid");
65
- if (gclid) {
66
- return { gclid: gclid };
67
- }
68
- const wbraid = this.getClickId(conversion, "wbraid");
69
- if (wbraid) {
70
- return { wbraid: wbraid };
71
- }
72
- const gbraid = this.getClickId(conversion, "gbraid");
73
- if (gbraid) {
74
- return { gbraid: gbraid };
75
- }
76
- return null;
77
- }
78
- /*
79
- * Enhanced conversions for leads: this conversion has no click id, but it
80
- * has an email and the account is configured to match on one.
81
- */
82
- canUploadAsLead(conversion) {
83
- return Boolean(GoogleAdsEnhancedConversionsForLeadsEnabled &&
84
- this.getHashedEmail(conversion));
85
- }
86
- /*
87
- * userIdentifiers per the enhanced-conversions spec. FIRST_PARTY is the
88
- * correct source: the address was given to OneUptime directly, not bought or
89
- * inferred.
90
- */
91
- getUserIdentifiers(conversion) {
92
- const hashedEmail = this.getHashedEmail(conversion);
93
- if (!hashedEmail) {
94
- return [];
95
- }
96
- return [
97
- {
98
- hashedEmail: hashedEmail,
99
- userIdentifierSource: "FIRST_PARTY",
100
- },
101
- ];
102
- }
103
- getConversionActionId(conversion) {
104
- const mapping = {
105
- [MarketingConversionType.SignUp]: GoogleAdsSignUpConversionActionId,
106
- [MarketingConversionType.MeetingBooked]: GoogleAdsMeetingBookedConversionActionId,
107
- [MarketingConversionType.PaidSubscription]: GoogleAdsPaidSubscriptionConversionActionId,
108
- };
109
- return this.resolveByConversionType(conversion, mapping) || "";
110
- }
111
- async getAccessToken() {
112
- var _a, _b;
113
- if (this.cachedAccessToken &&
114
- Date.now() < this.cachedAccessTokenExpiresAt) {
115
- return this.cachedAccessToken;
116
- }
117
- const params = new URLSearchParams();
118
- params.append("client_id", GoogleAdsOAuthClientId);
119
- params.append("client_secret", GoogleAdsOAuthClientSecret);
120
- params.append("refresh_token", GoogleAdsOAuthRefreshToken);
121
- params.append("grant_type", "refresh_token");
122
- const response = await axios.post("https://oauth2.googleapis.com/token", params.toString(), {
123
- headers: {
124
- "content-type": "application/x-www-form-urlencoded",
125
- },
126
- timeout: REQUEST_TIMEOUT_MS,
127
- });
128
- const accessToken = (_a = response.data) === null || _a === void 0 ? void 0 : _a.access_token;
129
- if (!accessToken) {
130
- throw new Error("Google OAuth token response had no access_token");
131
- }
132
- this.cachedAccessToken = accessToken;
133
- // Refresh one minute before actual expiry.
134
- this.cachedAccessTokenExpiresAt =
135
- Date.now() + ((((_b = response.data) === null || _b === void 0 ? void 0 : _b.expires_in) || 3600) - 60) * 1000;
136
- return accessToken;
137
- }
138
- // Format Google Ads expects: "yyyy-mm-dd hh:mm:ss+00:00".
139
- formatConversionDateTime(date) {
140
- const iso = date.toISOString(); // 2026-07-18T12:34:56.789Z
141
- return `${iso.slice(0, 10)} ${iso.slice(11, 19)}+00:00`;
142
- }
143
- async upload(conversions) {
144
- const accessToken = await this.getAccessToken();
145
- const headers = {
146
- Authorization: `Bearer ${accessToken}`,
147
- "developer-token": GoogleAdsDeveloperToken,
148
- };
149
- if (GoogleAdsLoginCustomerId) {
150
- headers["login-customer-id"] = GoogleAdsLoginCustomerId;
151
- }
152
- const body = {
153
- conversions: conversions.map((conversion) => {
154
- const payload = Object.assign(Object.assign({}, (this.getGoogleClickId(conversion) || {})), { conversionAction: `customers/${GoogleAdsCustomerId}/conversionActions/${this.getConversionActionId(conversion)}`, conversionDateTime: this.formatConversionDateTime(conversion.conversionAt || new Date()) });
155
- const userIdentifiers = this.getUserIdentifiers(conversion);
156
- if (userIdentifiers.length > 0) {
157
- payload["userIdentifiers"] = userIdentifiers;
158
- }
159
- const valueInUSD = this.getValueInUSD(conversion);
160
- if (valueInUSD !== undefined) {
161
- payload["conversionValue"] = valueInUSD;
162
- payload["currencyCode"] = "USD";
163
- }
164
- return payload;
165
- }),
166
- partialFailure: true,
167
- };
168
- const url = `https://googleads.googleapis.com/${GoogleAdsApiVersion}/customers/${GoogleAdsCustomerId}:uploadClickConversions`;
169
- const response = await axios.post(url, body, {
170
- headers: headers,
171
- timeout: REQUEST_TIMEOUT_MS,
172
- });
173
- return this.parsePartialFailure(response.data);
174
- }
175
- /*
176
- * partialFailureError.details[].errors[] entries carry the index of the
177
- * failing operation in location.fieldPathElements ({fieldName:
178
- * "conversions", index: N}).
179
- */
180
- parsePartialFailure(responseData) {
181
- var _a;
182
- const permanentFailures = new Map();
183
- const partialFailureError = responseData === null || responseData === void 0 ? void 0 : responseData["partialFailureError"];
184
- if (!partialFailureError) {
185
- return { permanentFailures };
186
- }
187
- const details = partialFailureError["details"] || [];
188
- let alreadyExistsCount = 0;
189
- for (const detail of details) {
190
- const errors = detail["errors"] || [];
191
- for (const error of errors) {
192
- const message = error["message"] || "Unknown Google Ads error";
193
- /*
194
- * A duplicate rejection means the conversion WAS counted by a prior
195
- * run (e.g. crash between upload and status write) — that is a
196
- * success, not a failure.
197
- */
198
- if (JSON.stringify(error).includes("ALREADY_EXISTS")) {
199
- alreadyExistsCount++;
200
- continue;
201
- }
202
- const fieldPathElements = ((_a = error["location"]) === null || _a === void 0 ? void 0 : _a["fieldPathElements"]) || [];
203
- for (const element of fieldPathElements) {
204
- if (element["fieldName"] === "conversions" &&
205
- typeof element["index"] === "number") {
206
- permanentFailures.set(element["index"], message);
207
- }
208
- }
209
- }
210
- }
211
- /*
212
- * A partial failure with no parseable indexes should not be silently
213
- * treated as full success.
214
- */
215
- if (permanentFailures.size === 0 && alreadyExistsCount === 0) {
216
- logger.error(`Google Ads partial failure with unparseable indexes: ${JSON.stringify(partialFailureError).slice(0, 1000)}`);
217
- }
218
- return { permanentFailures };
219
- }
220
- }
221
- //# sourceMappingURL=GoogleAds.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"GoogleAds.js","sourceRoot":"","sources":["../../../../../../Server/Utils/Marketing/Providers/GoogleAds.ts"],"names":[],"mappings":"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"}
@@ -1,137 +0,0 @@
1
- import axios from "axios";
2
- import { LinkedInApiVersion, LinkedInConversionsAccessToken, LinkedInMeetingBookedConversionId, LinkedInPaidSubscriptionConversionId, LinkedInSignUpConversionId, } from "../../../EnvironmentConfig";
3
- import ConversionUploadProvider from "../ConversionUploadProvider";
4
- import { MarketingConversionType } from "../../../../Types/Marketing/MarketingConversion";
5
- const REQUEST_TIMEOUT_MS = 30000;
6
- // LinkedIn accepts conversions up to 90 days after the interaction.
7
- const LINKEDIN_MAX_CONVERSION_AGE_IN_DAYS = 90;
8
- /*
9
- * LinkedIn Conversions API (api.linkedin.com/rest/conversionEvents) matching
10
- * on the li_fat_id first-party click id, a SHA-256 email, or both — LinkedIn
11
- * accepts several userIds and resolves whichever it can. Conversion rules are
12
- * referenced by urn:lla:llaPartnerConversion:{id}. Events are posted one at a
13
- * time (small volumes); auth-level failures abort the batch for retry, other
14
- * per-event rejections are recorded as permanent.
15
- *
16
- * Note: LinkedIn access tokens expire (typically 60 days) — the token in
17
- * LINKEDIN_CONVERSIONS_ACCESS_TOKEN must be rotated externally.
18
- */
19
- export default class LinkedInProvider extends ConversionUploadProvider {
20
- constructor() {
21
- super(...arguments);
22
- this.key = "linkedin";
23
- this.displayName = "LinkedIn";
24
- /*
25
- * LinkedIn has no batch endpoint, so upload() issues one request per
26
- * conversion — keep runs short enough to finish inside the job timeout.
27
- */
28
- this.maxBatchSize = 50;
29
- }
30
- isConfigured() {
31
- return Boolean(LinkedInConversionsAccessToken);
32
- }
33
- getProviderSkipReason(conversion) {
34
- if (this.getUserIds(conversion).length === 0) {
35
- return {
36
- reason: "No LinkedIn click id (li_fat_id) and no email to match on",
37
- isPermanent: true,
38
- };
39
- }
40
- if (!this.getConversionRuleId(conversion)) {
41
- // Config gap — leave pending so it uploads once the id is configured.
42
- return {
43
- reason: "No LinkedIn conversion rule id configured for this conversion type",
44
- isPermanent: false,
45
- };
46
- }
47
- if (this.isOlderThanDays(conversion, LINKEDIN_MAX_CONVERSION_AGE_IN_DAYS)) {
48
- return {
49
- reason: "Conversion older than LinkedIn's 90-day window",
50
- isPermanent: true,
51
- };
52
- }
53
- return null;
54
- }
55
- getConversionRuleId(conversion) {
56
- const mapping = {
57
- [MarketingConversionType.SignUp]: LinkedInSignUpConversionId,
58
- [MarketingConversionType.MeetingBooked]: LinkedInMeetingBookedConversionId,
59
- [MarketingConversionType.PaidSubscription]: LinkedInPaidSubscriptionConversionId,
60
- };
61
- return this.resolveByConversionType(conversion, mapping) || "";
62
- }
63
- /*
64
- * Every identifier LinkedIn can resolve this person by. Sending both when
65
- * both exist raises the match rate; sending only the email is how a demo
66
- * booked on one device and a licence signed on another are still joined to
67
- * the click that started it.
68
- */
69
- getUserIds(conversion) {
70
- const userIds = [];
71
- const clickId = this.getClickId(conversion, "li_fat_id");
72
- if (clickId) {
73
- userIds.push({
74
- idType: "LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID",
75
- idValue: clickId,
76
- });
77
- }
78
- const hashedEmail = this.getHashedEmail(conversion);
79
- if (hashedEmail) {
80
- userIds.push({
81
- idType: "SHA256_EMAIL",
82
- idValue: hashedEmail,
83
- });
84
- }
85
- return userIds;
86
- }
87
- async upload(conversions) {
88
- var _a;
89
- const permanentFailures = new Map();
90
- for (let i = 0; i < conversions.length; i++) {
91
- const conversion = conversions[i];
92
- const body = {
93
- conversion: `urn:lla:llaPartnerConversion:${this.getConversionRuleId(conversion)}`,
94
- conversionHappenedAt: (conversion.conversionAt || new Date()).getTime(),
95
- // Dedup key: a retried batch must not double-count conversions.
96
- eventId: conversion.id.toString(),
97
- user: {
98
- userIds: this.getUserIds(conversion),
99
- },
100
- };
101
- const valueInUSD = this.getValueInUSD(conversion);
102
- if (valueInUSD !== undefined) {
103
- body["conversionValue"] = {
104
- currencyCode: "USD",
105
- amount: valueInUSD.toFixed(2),
106
- };
107
- }
108
- try {
109
- await axios.post("https://api.linkedin.com/rest/conversionEvents", body, {
110
- headers: {
111
- Authorization: `Bearer ${LinkedInConversionsAccessToken}`,
112
- "LinkedIn-Version": LinkedInApiVersion,
113
- "X-Restli-Protocol-Version": "2.0.0",
114
- "Content-Type": "application/json",
115
- },
116
- timeout: REQUEST_TIMEOUT_MS,
117
- });
118
- }
119
- catch (err) {
120
- const status = (_a = err.response) === null || _a === void 0 ? void 0 : _a.status;
121
- /*
122
- * Only definitive per-event validation rejections are permanent.
123
- * Everything else — auth/token problems, rate limits, 5xx, and
124
- * network errors (no response, so no status) — is transport-level:
125
- * throw so the whole batch stays pending and is retried. eventId
126
- * makes redelivery idempotent on LinkedIn's side.
127
- */
128
- if (status !== 400 && status !== 422) {
129
- throw err;
130
- }
131
- permanentFailures.set(i, ConversionUploadProvider.getErrorMessage(err));
132
- }
133
- }
134
- return { permanentFailures };
135
- }
136
- }
137
- //# sourceMappingURL=LinkedIn.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"LinkedIn.js","sourceRoot":"","sources":["../../../../../../Server/Utils/Marketing/Providers/LinkedIn.ts"],"names":[],"mappings":"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"}
@@ -1,133 +0,0 @@
1
- import axios from "axios";
2
- import { MetaConversionsAccessToken, MetaConversionsPixelId, MetaGraphApiVersion, } from "../../../EnvironmentConfig";
3
- import ConversionUploadProvider from "../ConversionUploadProvider";
4
- import { MarketingConversionType } from "../../../../Types/Marketing/MarketingConversion";
5
- const REQUEST_TIMEOUT_MS = 30000;
6
- // Meta rejects website events older than 7 days at upload time.
7
- const META_MAX_EVENT_AGE_IN_DAYS = 7;
8
- /*
9
- * Meta (Facebook) Conversions API
10
- * (graph.facebook.com/{version}/{pixelId}/events). Emails are SHA-256 hashed
11
- * per Meta's customer-information matching spec; the raw email never leaves
12
- * OneUptime. event_id is the conversion row id, so Meta deduplicates
13
- * retried uploads (and browser-pixel duplicates) server-side.
14
- *
15
- * An event is uploadable with a click id, a hashed email, or both — Meta
16
- * matches on either. Requiring fbclid, as this provider used to, discarded
17
- * every conversion whose click id had not survived to the moment of
18
- * conversion, which is most of a sales-led funnel.
19
- */
20
- export default class MetaProvider extends ConversionUploadProvider {
21
- constructor() {
22
- super(...arguments);
23
- this.key = "meta";
24
- this.displayName = "Meta";
25
- }
26
- isConfigured() {
27
- return Boolean(MetaConversionsPixelId && MetaConversionsAccessToken);
28
- }
29
- getProviderSkipReason(conversion) {
30
- if (!this.getClickId(conversion, "fbclid") &&
31
- !this.getHashedEmail(conversion)) {
32
- return {
33
- reason: "No Meta click id (fbclid) and no email to match on",
34
- isPermanent: true,
35
- };
36
- }
37
- if (!this.getEventName(conversion)) {
38
- return {
39
- reason: "No Meta event name mapped for this conversion type",
40
- isPermanent: true,
41
- };
42
- }
43
- if (this.isOlderThanDays(conversion, META_MAX_EVENT_AGE_IN_DAYS)) {
44
- return {
45
- reason: "Conversion older than Meta's 7-day upload window",
46
- isPermanent: true,
47
- };
48
- }
49
- return null;
50
- }
51
- /*
52
- * Standard Meta event names. `Schedule` is Meta's own name for a booked
53
- * meeting, and it carries no revenue — which is what keeps a demo out of the
54
- * Purchase optimisation pool.
55
- */
56
- getEventName(conversion) {
57
- const mapping = {
58
- [MarketingConversionType.SignUp]: "CompleteRegistration",
59
- [MarketingConversionType.MeetingBooked]: "Schedule",
60
- [MarketingConversionType.PaidSubscription]: "Purchase",
61
- };
62
- return this.resolveByConversionType(conversion, mapping);
63
- }
64
- /*
65
- * fbc format per Meta spec: fb.1.{creationTimeMs}.{fbclid}. We use the
66
- * conversion time as creation time since the original click timestamp is
67
- * not stored.
68
- */
69
- buildFbc(fbclid, eventAt) {
70
- return `fb.1.${eventAt.getTime()}.${fbclid}`;
71
- }
72
- async upload(conversions) {
73
- var _a;
74
- const data = conversions.map((conversion) => {
75
- const eventAt = conversion.conversionAt || new Date();
76
- const fbclid = this.getClickId(conversion, "fbclid");
77
- const userData = {};
78
- // Only send fbc when there is a click id to build it from.
79
- if (fbclid) {
80
- userData["fbc"] = this.buildFbc(fbclid, eventAt);
81
- }
82
- const hashedEmail = this.getHashedEmail(conversion);
83
- if (hashedEmail) {
84
- userData["em"] = [hashedEmail];
85
- }
86
- const eventName = this.getEventName(conversion) || "Lead";
87
- const payload = {
88
- event_name: eventName,
89
- event_time: Math.floor(eventAt.getTime() / 1000),
90
- event_id: conversion.id.toString(),
91
- /*
92
- * Not "website": website events REQUIRE client_user_agent and
93
- * event_source_url, which a server-side backfill does not have —
94
- * Meta rejects the whole batch without them.
95
- */
96
- action_source: "other",
97
- user_data: userData,
98
- };
99
- const valueInUSD = this.getValueInUSD(conversion);
100
- /*
101
- * Purchase events REQUIRE custom_data.value and currency — one
102
- * valueless Purchase (custom-pricing plan) would 400 the entire
103
- * batch. Send 0 when the value is unknown. Lead-shaped events
104
- * (Schedule, Lead) never carry a value: getValueInUSD returns
105
- * undefined for them, so no custom_data is attached at all.
106
- */
107
- if (eventName === "Purchase" || valueInUSD !== undefined) {
108
- payload["custom_data"] = {
109
- value: valueInUSD !== null && valueInUSD !== void 0 ? valueInUSD : 0,
110
- currency: "USD",
111
- };
112
- }
113
- return payload;
114
- });
115
- const url = `https://graph.facebook.com/${MetaGraphApiVersion}/${MetaConversionsPixelId}/events`;
116
- const response = await axios.post(url, {
117
- data: data,
118
- access_token: MetaConversionsAccessToken,
119
- }, {
120
- timeout: REQUEST_TIMEOUT_MS,
121
- });
122
- const eventsReceived = ((_a = response.data) === null || _a === void 0 ? void 0 : _a["events_received"]) || 0;
123
- if (eventsReceived !== conversions.length) {
124
- /*
125
- * Retrying the whole batch is safe: event_id makes redelivery
126
- * idempotent on Meta's side.
127
- */
128
- throw new Error(`Meta accepted ${eventsReceived} of ${conversions.length} events`);
129
- }
130
- return { permanentFailures: new Map() };
131
- }
132
- }
133
- //# sourceMappingURL=Meta.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Meta.js","sourceRoot":"","sources":["../../../../../../Server/Utils/Marketing/Providers/Meta.ts"],"names":[],"mappings":"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"}
@@ -1,143 +0,0 @@
1
- import axios from "axios";
2
- import { MicrosoftAdsAccountId, MicrosoftAdsCustomerId, MicrosoftAdsDeveloperToken, MicrosoftAdsMeetingBookedConversionName, MicrosoftAdsOAuthClientId, MicrosoftAdsOAuthClientSecret, MicrosoftAdsOAuthRefreshToken, MicrosoftAdsPaidSubscriptionConversionName, MicrosoftAdsSignUpConversionName, } from "../../../EnvironmentConfig";
3
- import ConversionUploadProvider from "../ConversionUploadProvider";
4
- import { MarketingConversionType } from "../../../../Types/Marketing/MarketingConversion";
5
- const REQUEST_TIMEOUT_MS = 30000;
6
- // Microsoft accepts offline conversions up to 90 days after the click.
7
- const MICROSOFT_MAX_CONVERSION_AGE_IN_DAYS = 90;
8
- /*
9
- * Microsoft Advertising (Bing Ads) offline conversions via the Campaign
10
- * Management v13 REST surface (OfflineConversions/Apply), matching on msclkid
11
- * or on a SHA-256 email (Microsoft's enhanced conversions). Offline conversion
12
- * goals are matched by conversion NAME configured in the Microsoft Advertising
13
- * UI. OAuth2 refresh-token flow against Microsoft identity platform with the
14
- * msads.manage scope.
15
- */
16
- export default class MicrosoftAdsProvider extends ConversionUploadProvider {
17
- constructor() {
18
- super(...arguments);
19
- this.key = "microsoft";
20
- this.displayName = "Microsoft Advertising";
21
- this.cachedAccessToken = null;
22
- this.cachedAccessTokenExpiresAt = 0;
23
- }
24
- isConfigured() {
25
- return Boolean(MicrosoftAdsDeveloperToken &&
26
- MicrosoftAdsOAuthClientId &&
27
- MicrosoftAdsOAuthRefreshToken &&
28
- MicrosoftAdsCustomerId &&
29
- MicrosoftAdsAccountId);
30
- }
31
- getProviderSkipReason(conversion) {
32
- if (!this.getClickId(conversion, "msclkid") &&
33
- !this.getHashedEmail(conversion)) {
34
- return {
35
- reason: "No Microsoft click id (msclkid) and no email to match on",
36
- isPermanent: true,
37
- };
38
- }
39
- if (!this.getConversionName(conversion)) {
40
- // Config gap — leave pending so it uploads once the name is configured.
41
- return {
42
- reason: "No Microsoft Advertising conversion name configured for this conversion type",
43
- isPermanent: false,
44
- };
45
- }
46
- if (this.isOlderThanDays(conversion, MICROSOFT_MAX_CONVERSION_AGE_IN_DAYS)) {
47
- return {
48
- reason: "Conversion older than Microsoft's 90-day click window",
49
- isPermanent: true,
50
- };
51
- }
52
- return null;
53
- }
54
- getConversionName(conversion) {
55
- const mapping = {
56
- [MarketingConversionType.SignUp]: MicrosoftAdsSignUpConversionName,
57
- [MarketingConversionType.MeetingBooked]: MicrosoftAdsMeetingBookedConversionName,
58
- [MarketingConversionType.PaidSubscription]: MicrosoftAdsPaidSubscriptionConversionName,
59
- };
60
- return this.resolveByConversionType(conversion, mapping) || "";
61
- }
62
- async getAccessToken() {
63
- var _a, _b;
64
- if (this.cachedAccessToken &&
65
- Date.now() < this.cachedAccessTokenExpiresAt) {
66
- return this.cachedAccessToken;
67
- }
68
- const params = new URLSearchParams();
69
- params.append("client_id", MicrosoftAdsOAuthClientId);
70
- if (MicrosoftAdsOAuthClientSecret) {
71
- params.append("client_secret", MicrosoftAdsOAuthClientSecret);
72
- }
73
- params.append("refresh_token", MicrosoftAdsOAuthRefreshToken);
74
- params.append("grant_type", "refresh_token");
75
- params.append("scope", "https://ads.microsoft.com/msads.manage offline_access");
76
- const response = await axios.post("https://login.microsoftonline.com/common/oauth2/v2.0/token", params.toString(), {
77
- headers: {
78
- "content-type": "application/x-www-form-urlencoded",
79
- },
80
- timeout: REQUEST_TIMEOUT_MS,
81
- });
82
- const accessToken = (_a = response.data) === null || _a === void 0 ? void 0 : _a.access_token;
83
- if (!accessToken) {
84
- throw new Error("Microsoft OAuth token response had no access_token");
85
- }
86
- this.cachedAccessToken = accessToken;
87
- this.cachedAccessTokenExpiresAt =
88
- Date.now() + ((((_b = response.data) === null || _b === void 0 ? void 0 : _b.expires_in) || 3600) - 60) * 1000;
89
- return accessToken;
90
- }
91
- async upload(conversions) {
92
- var _a;
93
- const accessToken = await this.getAccessToken();
94
- const body = {
95
- OfflineConversions: conversions.map((conversion) => {
96
- const payload = {
97
- ConversionName: this.getConversionName(conversion),
98
- // ISO 8601 UTC, e.g. 2026-07-18T12:34:56Z.
99
- ConversionTime: (conversion.conversionAt || new Date())
100
- .toISOString()
101
- .replace(/\.\d{3}Z$/, "Z"),
102
- };
103
- const clickId = this.getClickId(conversion, "msclkid");
104
- if (clickId) {
105
- payload["MicrosoftClickId"] = clickId;
106
- }
107
- const hashedEmail = this.getHashedEmail(conversion);
108
- if (hashedEmail) {
109
- payload["HashedEmailAddress"] = hashedEmail;
110
- }
111
- const valueInUSD = this.getValueInUSD(conversion);
112
- if (valueInUSD !== undefined) {
113
- payload["ConversionValue"] = valueInUSD;
114
- payload["ConversionCurrencyCode"] = "USD";
115
- }
116
- return payload;
117
- }),
118
- };
119
- const response = await axios.post("https://campaign.api.bingads.microsoft.com/CampaignManagement/v13/OfflineConversions/Apply", body, {
120
- headers: {
121
- Authorization: `Bearer ${accessToken}`,
122
- DeveloperToken: MicrosoftAdsDeveloperToken,
123
- CustomerId: MicrosoftAdsCustomerId,
124
- CustomerAccountId: MicrosoftAdsAccountId,
125
- "Content-Type": "application/json",
126
- },
127
- timeout: REQUEST_TIMEOUT_MS,
128
- });
129
- /*
130
- * The Apply response reports per-item failures in PartialErrors, each
131
- * carrying the Index of the failing item in the submitted array.
132
- */
133
- const permanentFailures = new Map();
134
- const partialErrors = ((_a = response.data) === null || _a === void 0 ? void 0 : _a["PartialErrors"]) || [];
135
- for (const partialError of partialErrors) {
136
- if (typeof (partialError === null || partialError === void 0 ? void 0 : partialError["Index"]) === "number") {
137
- permanentFailures.set(partialError["Index"], `${partialError["ErrorCode"] || ""} ${partialError["Message"] || "Unknown Microsoft Advertising error"}`.trim());
138
- }
139
- }
140
- return { permanentFailures };
141
- }
142
- }
143
- //# sourceMappingURL=MicrosoftAds.js.map