@prismatic-io/components-google-ads 0.0.0-stage → 0.0.1
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.
- package/README.md +64 -2
- package/dist/icon.png +0 -0
- package/dist/index.js +325 -0
- package/dist/oauth-button.png +0 -0
- package/npm-dist/icon.png +0 -0
- package/npm-dist/index.cjs +2928 -0
- package/npm-dist/index.d.cts +1147 -0
- package/npm-dist/oauth-button.png +0 -0
- package/package.json +52 -4
|
@@ -0,0 +1,2928 @@
|
|
|
1
|
+
let _prismatic_io_spectral = require("@prismatic-io/spectral");
|
|
2
|
+
let _prismatic_io_spectral_http = require("@prismatic-io/spectral/http");
|
|
3
|
+
let lodash = require("lodash");
|
|
4
|
+
//#region src/constants.ts
|
|
5
|
+
const GET_ACCOUNT_HIERARCHY_QUERY = `SELECT
|
|
6
|
+
customer_client.client_customer,
|
|
7
|
+
customer_client.level,
|
|
8
|
+
customer_client.manager,
|
|
9
|
+
customer_client.descriptive_name,
|
|
10
|
+
customer_client.id
|
|
11
|
+
FROM customer_client
|
|
12
|
+
WHERE customer_client.level <= `;
|
|
13
|
+
const MICROS_TO_DOLLARS_CONVERSION_FACTOR = 1e6;
|
|
14
|
+
const BUDGET_SEVERITY = {
|
|
15
|
+
INFO: "info",
|
|
16
|
+
WARNING: "warning",
|
|
17
|
+
CRITICAL: "critical"
|
|
18
|
+
};
|
|
19
|
+
const NO_DESCRIPTIVE_NAME = "(No descriptive name)";
|
|
20
|
+
const DESCRIPTIVE_NAME_UNAVAILABLE = "No Description";
|
|
21
|
+
const GOOGLE_ADS_OAUTH_SCOPE = "https://www.googleapis.com/auth/adwords";
|
|
22
|
+
const GOOGLE_DATA_MANAGER_OAUTH_SCOPE = "https://www.googleapis.com/auth/datamanager";
|
|
23
|
+
const GOOGLE_ADS_BASE_URL = "https://googleads.googleapis.com";
|
|
24
|
+
const GOOGLE_DATA_MANAGER_BASE_URL = "https://datamanager.googleapis.com";
|
|
25
|
+
const GOOGLE_LOCAL_SERVICES_BASE_URL = "https://localservices.googleapis.com";
|
|
26
|
+
const googleAdsSearchPath = (customerId) => `/customers/${customerId}/googleAds:search`;
|
|
27
|
+
const CHANGE_TYPE = {
|
|
28
|
+
STATUS: "status",
|
|
29
|
+
BUDGET: "budget",
|
|
30
|
+
BIDDING: "bidding",
|
|
31
|
+
SETTINGS: "settings",
|
|
32
|
+
ALL: "all"
|
|
33
|
+
};
|
|
34
|
+
const CHANGE_EVENT_ROW_LIMIT = 1e4;
|
|
35
|
+
const BATCHED_CHANGE_EVENT_ROW_LIMIT = 1e3;
|
|
36
|
+
const LOOK_BACK_DATE_PATTERN = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
37
|
+
const LOOK_BACK_DATE_START_TIME = "00:00:00";
|
|
38
|
+
const OPERATION_TO_CHANGE_TYPE = {
|
|
39
|
+
CREATE: "created",
|
|
40
|
+
UPDATE: "updated",
|
|
41
|
+
REMOVE: "removed"
|
|
42
|
+
};
|
|
43
|
+
const CAMPAIGN_CHANGE_RESOURCE_TYPE = {
|
|
44
|
+
CAMPAIGN: "CAMPAIGN",
|
|
45
|
+
CAMPAIGN_BUDGET: "CAMPAIGN_BUDGET"
|
|
46
|
+
};
|
|
47
|
+
const CHANGE_HISTORY_RESOURCE_TYPE_QUERY_VALUE = { KEYWORD: "AD_GROUP_CRITERION" };
|
|
48
|
+
const CHANGE_TYPES_MODEL = [
|
|
49
|
+
{
|
|
50
|
+
label: "Status Changes",
|
|
51
|
+
value: CHANGE_TYPE.STATUS
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
label: "Budget Changes",
|
|
55
|
+
value: CHANGE_TYPE.BUDGET
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
label: "Bid Strategy Changes",
|
|
59
|
+
value: CHANGE_TYPE.BIDDING
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
label: "Campaign Settings",
|
|
63
|
+
value: CHANGE_TYPE.SETTINGS
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
label: "All Changes",
|
|
67
|
+
value: CHANGE_TYPE.ALL
|
|
68
|
+
}
|
|
69
|
+
];
|
|
70
|
+
//#endregion
|
|
71
|
+
//#region src/connections/dataManagerOAuth.ts
|
|
72
|
+
const dataManagerOAuth = (0, _prismatic_io_spectral.oauth2Connection)({
|
|
73
|
+
key: "dataManagerOAuth",
|
|
74
|
+
display: {
|
|
75
|
+
label: "Data Manager OAuth 2.0",
|
|
76
|
+
description: "Authenticate to the Google Data Manager API using OAuth 2.0. Use this connection for the Ingest Offline Conversions action. A Developer Token is not required for this API.",
|
|
77
|
+
icons: { oauth2ConnectionIconPath: "oauth-button.png" }
|
|
78
|
+
},
|
|
79
|
+
oauth2Type: _prismatic_io_spectral.OAuth2Type.AuthorizationCode,
|
|
80
|
+
inputs: {
|
|
81
|
+
authorizeUrl: {
|
|
82
|
+
label: "Authorize URL",
|
|
83
|
+
placeholder: "Enter authorize URL",
|
|
84
|
+
type: "string",
|
|
85
|
+
required: true,
|
|
86
|
+
shown: false,
|
|
87
|
+
comments: "The OAuth 2.0 Authorization URL for the Google Data Manager API. See [OAuth 2.0 documentation](https://developers.google.com/identity/protocols/oauth2).",
|
|
88
|
+
default: "https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&prompt=consent"
|
|
89
|
+
},
|
|
90
|
+
tokenUrl: {
|
|
91
|
+
label: "Token URL",
|
|
92
|
+
placeholder: "Enter token URL",
|
|
93
|
+
type: "string",
|
|
94
|
+
required: true,
|
|
95
|
+
shown: false,
|
|
96
|
+
comments: "The OAuth 2.0 Token URL for the Google Data Manager API. See [OAuth 2.0 documentation](https://developers.google.com/identity/protocols/oauth2).",
|
|
97
|
+
default: "https://oauth2.googleapis.com/token"
|
|
98
|
+
},
|
|
99
|
+
scopes: {
|
|
100
|
+
label: "Scopes",
|
|
101
|
+
placeholder: "Enter OAuth scopes",
|
|
102
|
+
type: "string",
|
|
103
|
+
required: true,
|
|
104
|
+
shown: false,
|
|
105
|
+
comments: "Space-separated OAuth 2.0 permission scopes for the Google Data Manager API. See [OAuth scopes documentation](https://developers.google.com/identity/protocols/oauth2/scopes).",
|
|
106
|
+
default: GOOGLE_DATA_MANAGER_OAUTH_SCOPE
|
|
107
|
+
},
|
|
108
|
+
clientId: {
|
|
109
|
+
label: "Client ID",
|
|
110
|
+
placeholder: "Enter client ID",
|
|
111
|
+
type: "string",
|
|
112
|
+
required: true,
|
|
113
|
+
shown: true,
|
|
114
|
+
comments: "The Client ID for the Google Data Manager API application. Obtain from the [Google Cloud Console](https://console.cloud.google.com/apis/credentials).",
|
|
115
|
+
example: "123456789012-abcdefghijklmnopqrstuvwxyz123456.apps.googleusercontent.com"
|
|
116
|
+
},
|
|
117
|
+
clientSecret: {
|
|
118
|
+
label: "Client Secret",
|
|
119
|
+
placeholder: "Enter client secret",
|
|
120
|
+
type: "password",
|
|
121
|
+
required: true,
|
|
122
|
+
shown: true,
|
|
123
|
+
comments: "The Client Secret for the Google Data Manager API application. Obtain from the [Google Cloud Console](https://console.cloud.google.com/apis/credentials).",
|
|
124
|
+
example: "GOCSPX-abcdefghijklmnopqrstuvwxyz"
|
|
125
|
+
},
|
|
126
|
+
apiVersion: {
|
|
127
|
+
label: "API Version",
|
|
128
|
+
placeholder: "Enter API version",
|
|
129
|
+
type: "string",
|
|
130
|
+
required: false,
|
|
131
|
+
shown: true,
|
|
132
|
+
comments: `The version of the Google Data Manager API to use. Defaults to v1. See [API reference](https://developers.google.com/data-manager/api/reference/rest).`,
|
|
133
|
+
example: "v1",
|
|
134
|
+
default: "v1"
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
//#endregion
|
|
139
|
+
//#region src/connections/oauth.ts
|
|
140
|
+
const oauth = (0, _prismatic_io_spectral.oauth2Connection)({
|
|
141
|
+
key: "oauth",
|
|
142
|
+
display: {
|
|
143
|
+
label: "OAuth 2.0",
|
|
144
|
+
description: "Authenticate using OAuth 2.0.",
|
|
145
|
+
icons: { oauth2ConnectionIconPath: "oauth-button.png" }
|
|
146
|
+
},
|
|
147
|
+
oauth2Type: _prismatic_io_spectral.OAuth2Type.AuthorizationCode,
|
|
148
|
+
inputs: {
|
|
149
|
+
authorizeUrl: {
|
|
150
|
+
label: "Authorize URL",
|
|
151
|
+
placeholder: "Enter authorize URL",
|
|
152
|
+
type: "string",
|
|
153
|
+
required: true,
|
|
154
|
+
shown: false,
|
|
155
|
+
comments: "The OAuth 2.0 Authorization URL for the Google Ads API. See [OAuth 2.0 documentation](https://developers.google.com/identity/protocols/oauth2).",
|
|
156
|
+
default: "https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&prompt=consent"
|
|
157
|
+
},
|
|
158
|
+
tokenUrl: {
|
|
159
|
+
label: "Token URL",
|
|
160
|
+
placeholder: "Enter token URL",
|
|
161
|
+
type: "string",
|
|
162
|
+
required: true,
|
|
163
|
+
shown: false,
|
|
164
|
+
comments: "The OAuth 2.0 Token URL for the Google Ads API. See [OAuth 2.0 documentation](https://developers.google.com/identity/protocols/oauth2).",
|
|
165
|
+
default: "https://oauth2.googleapis.com/token"
|
|
166
|
+
},
|
|
167
|
+
scopes: {
|
|
168
|
+
label: "Scopes",
|
|
169
|
+
placeholder: "Enter OAuth scopes",
|
|
170
|
+
type: "string",
|
|
171
|
+
required: true,
|
|
172
|
+
shown: true,
|
|
173
|
+
comments: "Space-separated OAuth 2.0 permission scopes for the Google Ads and Data Manager APIs. See [OAuth scopes documentation](https://developers.google.com/identity/protocols/oauth2/scopes).",
|
|
174
|
+
example: `${GOOGLE_ADS_OAUTH_SCOPE} ${GOOGLE_DATA_MANAGER_OAUTH_SCOPE}`,
|
|
175
|
+
default: GOOGLE_ADS_OAUTH_SCOPE
|
|
176
|
+
},
|
|
177
|
+
clientId: {
|
|
178
|
+
label: "Client ID",
|
|
179
|
+
placeholder: "Enter client ID",
|
|
180
|
+
type: "string",
|
|
181
|
+
required: true,
|
|
182
|
+
shown: true,
|
|
183
|
+
comments: "The Client ID for the Google Ads API application. Obtain from the [Google Cloud Console](https://console.cloud.google.com/apis/credentials).",
|
|
184
|
+
example: "123456789012-abcdefghijklmnopqrstuvwxyz123456.apps.googleusercontent.com"
|
|
185
|
+
},
|
|
186
|
+
clientSecret: {
|
|
187
|
+
label: "Client Secret",
|
|
188
|
+
placeholder: "Enter client secret",
|
|
189
|
+
type: "password",
|
|
190
|
+
required: true,
|
|
191
|
+
shown: true,
|
|
192
|
+
comments: "The Client Secret for the Google Ads API application. Obtain from the [Google Cloud Console](https://console.cloud.google.com/apis/credentials).",
|
|
193
|
+
example: "GOCSPX-abcdefghijklmnopqrstuvwxyz"
|
|
194
|
+
},
|
|
195
|
+
developerToken: {
|
|
196
|
+
label: "Developer Token",
|
|
197
|
+
placeholder: "Leave blank",
|
|
198
|
+
type: "password",
|
|
199
|
+
required: false,
|
|
200
|
+
shown: true,
|
|
201
|
+
comments: "Leave blank. Google [sunset developer tokens](https://developers.google.com/google-ads/api/docs/api-policy/developer-token) on September 9, 2026, and API access is now granted to the Google Cloud project that owns the OAuth credentials. A saved token is not sent to the Google Ads API.",
|
|
202
|
+
example: "ABcdeFGH93KL-NOPQ_RsTuv"
|
|
203
|
+
},
|
|
204
|
+
apiVersion: {
|
|
205
|
+
label: "API Version",
|
|
206
|
+
placeholder: "Enter API version",
|
|
207
|
+
type: "string",
|
|
208
|
+
required: false,
|
|
209
|
+
shown: true,
|
|
210
|
+
comments: `The version of the Google Ads API to use. Defaults to v25. Any version from v22 through v25 can be set explicitly; anything below v22 has already been sunset by Google and is automatically upgraded to v22. Note: v22 sunsets in October 2026. See [API versions documentation](https://developers.google.com/google-ads/api/docs/release-notes).`,
|
|
211
|
+
example: "v25",
|
|
212
|
+
default: "v25"
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
//#endregion
|
|
217
|
+
//#region src/connections/index.ts
|
|
218
|
+
var connections_default = [oauth, dataManagerOAuth];
|
|
219
|
+
//#endregion
|
|
220
|
+
//#region src/util/apiVersion.ts
|
|
221
|
+
const validateVersionAgainstFloor = (apiVersion, logger, options) => {
|
|
222
|
+
if (!apiVersion) return options.defaultVersion;
|
|
223
|
+
const match = apiVersion.match(/^v(\d+)$/);
|
|
224
|
+
if (!match) throw new Error(options.invalidFormatMessage);
|
|
225
|
+
if (_prismatic_io_spectral.util.types.toInt(match[1]) < _prismatic_io_spectral.util.types.toInt(options.minimumVersion.replace("v", ""))) {
|
|
226
|
+
logger.warn(options.belowMinimumMessage);
|
|
227
|
+
return options.minimumVersion;
|
|
228
|
+
}
|
|
229
|
+
return apiVersion;
|
|
230
|
+
};
|
|
231
|
+
const validateApiVersion = (apiVersion, logger) => validateVersionAgainstFloor(apiVersion, logger, {
|
|
232
|
+
defaultVersion: "v25",
|
|
233
|
+
minimumVersion: "v22",
|
|
234
|
+
invalidFormatMessage: `Invalid API version format: ${apiVersion}. Expected format: v{number} (e.g., v24, v25)`,
|
|
235
|
+
belowMinimumMessage: `The connection API version ${apiVersion} is configured below the minimum supported version. Version v22 will be used to prevent errors.`
|
|
236
|
+
});
|
|
237
|
+
const validateDataManagerApiVersion = (apiVersion, logger) => validateVersionAgainstFloor(apiVersion, logger, {
|
|
238
|
+
defaultVersion: "v1",
|
|
239
|
+
minimumVersion: "v1",
|
|
240
|
+
invalidFormatMessage: `Invalid Data Manager API version format: ${apiVersion}. Expected format: v{number} (e.g., v1)`,
|
|
241
|
+
belowMinimumMessage: `The Data Manager API version ${apiVersion} is configured below the minimum supported version. Version v1 will be used to prevent errors.`
|
|
242
|
+
});
|
|
243
|
+
//#endregion
|
|
244
|
+
//#region src/util/clean.ts
|
|
245
|
+
const cleanString = (value) => {
|
|
246
|
+
const str = _prismatic_io_spectral.util.types.toString(value);
|
|
247
|
+
return str ? str : void 0;
|
|
248
|
+
};
|
|
249
|
+
const cleanCustomerId = (value) => {
|
|
250
|
+
return _prismatic_io_spectral.util.types.toString(value).replace("customers/", "").replace(/-/g, "");
|
|
251
|
+
};
|
|
252
|
+
const toOptionalCustomerId = (value) => {
|
|
253
|
+
const id = _prismatic_io_spectral.util.types.toString(value);
|
|
254
|
+
return id ? cleanCustomerId(id) : void 0;
|
|
255
|
+
};
|
|
256
|
+
const valueListInputClean = (value) => {
|
|
257
|
+
if (Array.isArray(value) && value.length >= 1 && value[0] !== "000xxx") return value.map((v) => `customer_id:${cleanCustomerId(v)}`).toString().replaceAll(",", ";");
|
|
258
|
+
};
|
|
259
|
+
const toOptionalInt = (value) => value ? _prismatic_io_spectral.util.types.toInt(value) : void 0;
|
|
260
|
+
const toStringList = (value) => {
|
|
261
|
+
if (value && Array.isArray(value)) return value;
|
|
262
|
+
return [];
|
|
263
|
+
};
|
|
264
|
+
const lookBackDateClean = (value) => {
|
|
265
|
+
const raw = _prismatic_io_spectral.util.types.toString(value).trim();
|
|
266
|
+
if (raw === "") return "";
|
|
267
|
+
const match = raw.match(LOOK_BACK_DATE_PATTERN);
|
|
268
|
+
if (!match) throw new Error(`Look-back Date must be a date in YYYY-MM-DD format. Received: ${raw}`);
|
|
269
|
+
const [, yearStr, monthStr, dayStr] = match;
|
|
270
|
+
const year = _prismatic_io_spectral.util.types.toNumber(yearStr);
|
|
271
|
+
const month = _prismatic_io_spectral.util.types.toNumber(monthStr);
|
|
272
|
+
const day = _prismatic_io_spectral.util.types.toNumber(dayStr);
|
|
273
|
+
const parsed = new Date(Date.UTC(year, month - 1, day));
|
|
274
|
+
if (parsed.getUTCFullYear() !== year || parsed.getUTCMonth() !== month - 1 || parsed.getUTCDate() !== day) throw new Error(`Look-back Date must be a date in YYYY-MM-DD format. Received: ${raw}`);
|
|
275
|
+
if (parsed.getTime() > Date.now()) throw new Error(`Look-back Date cannot be a future date. Received: ${raw}`);
|
|
276
|
+
return raw;
|
|
277
|
+
};
|
|
278
|
+
//#endregion
|
|
279
|
+
//#region src/client.ts
|
|
280
|
+
const extractAccessToken = (connection) => {
|
|
281
|
+
const accessToken = _prismatic_io_spectral.util.types.toString(connection.token?.access_token);
|
|
282
|
+
if (!accessToken) throw new _prismatic_io_spectral.ConnectionError(connection, "Did not receive an access token. Confirm your connection settings.");
|
|
283
|
+
return accessToken;
|
|
284
|
+
};
|
|
285
|
+
const validateConnection = (connection) => {
|
|
286
|
+
if (connection.key !== oauth.key) throw new _prismatic_io_spectral.ConnectionError(connection, `Unexpected connection type received: ${connection.key}`);
|
|
287
|
+
return { accessToken: extractAccessToken(connection) };
|
|
288
|
+
};
|
|
289
|
+
const createClient = ({ connection, debugEnabled, logger, loginCustomerId }) => {
|
|
290
|
+
const { accessToken } = validateConnection(connection);
|
|
291
|
+
const apiVersion = validateApiVersion(_prismatic_io_spectral.util.types.toString(connection.fields?.apiVersion), logger);
|
|
292
|
+
const headers = {
|
|
293
|
+
Accept: "application/json",
|
|
294
|
+
Authorization: `Bearer ${accessToken}`
|
|
295
|
+
};
|
|
296
|
+
if (loginCustomerId) headers["login-customer-id"] = cleanCustomerId(loginCustomerId);
|
|
297
|
+
return (0, _prismatic_io_spectral_http.createClient)({
|
|
298
|
+
baseUrl: `${GOOGLE_ADS_BASE_URL}/${apiVersion}`,
|
|
299
|
+
headers,
|
|
300
|
+
responseType: "json",
|
|
301
|
+
debug: debugEnabled
|
|
302
|
+
});
|
|
303
|
+
};
|
|
304
|
+
const validateDataManagerConnection = (connection) => {
|
|
305
|
+
if (connection.key !== dataManagerOAuth.key && connection.key !== oauth.key) throw new _prismatic_io_spectral.ConnectionError(connection, `Unexpected connection type received: ${connection.key}.`);
|
|
306
|
+
return { accessToken: extractAccessToken(connection) };
|
|
307
|
+
};
|
|
308
|
+
const createDataManagerClient = ({ connection, debugEnabled, logger }) => {
|
|
309
|
+
const { accessToken } = validateDataManagerConnection(connection);
|
|
310
|
+
return (0, _prismatic_io_spectral_http.createClient)({
|
|
311
|
+
baseUrl: `${GOOGLE_DATA_MANAGER_BASE_URL}/${connection.key === oauth.key ? "v1" : validateDataManagerApiVersion(_prismatic_io_spectral.util.types.toString(connection.fields?.apiVersion), logger)}`,
|
|
312
|
+
headers: {
|
|
313
|
+
Accept: "application/json",
|
|
314
|
+
Authorization: `Bearer ${accessToken}`
|
|
315
|
+
},
|
|
316
|
+
responseType: "json",
|
|
317
|
+
debug: debugEnabled
|
|
318
|
+
});
|
|
319
|
+
};
|
|
320
|
+
const createLocalServicesClient = ({ connection, debugEnabled, loginCustomerId }) => {
|
|
321
|
+
const { accessToken } = validateConnection(connection);
|
|
322
|
+
const headers = {
|
|
323
|
+
Accept: "application/json",
|
|
324
|
+
Authorization: `Bearer ${accessToken}`
|
|
325
|
+
};
|
|
326
|
+
if (loginCustomerId) headers["login-customer-id"] = cleanCustomerId(loginCustomerId);
|
|
327
|
+
return (0, _prismatic_io_spectral_http.createClient)({
|
|
328
|
+
baseUrl: `${GOOGLE_LOCAL_SERVICES_BASE_URL}/v1`,
|
|
329
|
+
headers,
|
|
330
|
+
responseType: "json",
|
|
331
|
+
debug: debugEnabled
|
|
332
|
+
});
|
|
333
|
+
};
|
|
334
|
+
const clickConversionsExampleInput = [{
|
|
335
|
+
gbraid: "CjwKCAjwABCDEF1234567890ghijklMNOPQRstuvwxyz",
|
|
336
|
+
wbraid: "CjwKCAjwZYXWVU0987654321LKJIHGfedcbaSTUVWXqr",
|
|
337
|
+
externalAttributionData: {
|
|
338
|
+
externalAttributionCredit: .75,
|
|
339
|
+
externalAttributionModel: "LINEAR"
|
|
340
|
+
},
|
|
341
|
+
customVariables: [{
|
|
342
|
+
conversionCustomVariable: "customers/1234567890/conversionCustomVariables/11111",
|
|
343
|
+
value: "premium_tier"
|
|
344
|
+
}],
|
|
345
|
+
cartData: {
|
|
346
|
+
merchantId: "1234567",
|
|
347
|
+
feedCountryCode: "US",
|
|
348
|
+
feedLanguageCode: "EN",
|
|
349
|
+
localTransactionCost: 5.99,
|
|
350
|
+
items: [{
|
|
351
|
+
productId: "SKU-12345",
|
|
352
|
+
quantity: 2,
|
|
353
|
+
unitPrice: 49.99
|
|
354
|
+
}]
|
|
355
|
+
},
|
|
356
|
+
userIdentifiers: [{
|
|
357
|
+
userIdentifierSource: "FIRST_PARTY",
|
|
358
|
+
hashedEmail: "a1159e9df3670d549d04524532629f5477ceb7deec9b45e47e8c009506ecb2c8",
|
|
359
|
+
hashedPhoneNumber: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
|
360
|
+
mobileId: "cdda802e-fb9c-47ad-0794d394c913",
|
|
361
|
+
thirdPartyUserId: "ext-user-98765",
|
|
362
|
+
addressInfo: {
|
|
363
|
+
hashedFirstName: "a8cfcd74832004951b4408cdb0a5dbcd8c7e52d43f7fe244bf720582e05241da",
|
|
364
|
+
hashedLastName: "2c624232cdd221771294dfbb310aca000a0df6ac8b166e8b32c4bdc2a5a5cc0a",
|
|
365
|
+
city: "San Francisco",
|
|
366
|
+
state: "CA",
|
|
367
|
+
countryCode: "US",
|
|
368
|
+
postalCode: "94105",
|
|
369
|
+
hashedStreetAddress: "d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35"
|
|
370
|
+
}
|
|
371
|
+
}],
|
|
372
|
+
conversionEnvironment: "WEB",
|
|
373
|
+
consent: {
|
|
374
|
+
adUserData: "GRANTED",
|
|
375
|
+
adPersonalization: "GRANTED"
|
|
376
|
+
},
|
|
377
|
+
gclid: "CjwKCAjw1234567890abcdefGHIJKLmnoPQRStuvwxyz",
|
|
378
|
+
conversionAction: "customers/1234567890/conversionActions/987654321",
|
|
379
|
+
conversionDateTime: "2026-01-15 10:30:00-05:00",
|
|
380
|
+
conversionValue: 149.99,
|
|
381
|
+
currencyCode: "USD",
|
|
382
|
+
orderId: "ORDER-2026-00123"
|
|
383
|
+
}];
|
|
384
|
+
const ingestOfflineConversionsExamplePayload = { data: {
|
|
385
|
+
requestId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
|
386
|
+
fieldWarnings: [{
|
|
387
|
+
reason: "WARNING_REASON_CUSTOM_VARIABLE_NOT_ENABLED",
|
|
388
|
+
description: "The custom variable is not enabled in the destination account.",
|
|
389
|
+
field: "events[0].custom_variables[0]"
|
|
390
|
+
}]
|
|
391
|
+
} };
|
|
392
|
+
const campaignOperationsExampleInput = [{
|
|
393
|
+
updateMask: "status,name",
|
|
394
|
+
create: {
|
|
395
|
+
name: "Example Campaign",
|
|
396
|
+
advertisingChannelType: "SEARCH",
|
|
397
|
+
status: "PAUSED",
|
|
398
|
+
campaignBudget: "customers/1234567890/campaignBudgets/9876543210"
|
|
399
|
+
},
|
|
400
|
+
update: {
|
|
401
|
+
resourceName: "customers/1234567890/campaigns/1122334455",
|
|
402
|
+
status: "ENABLED"
|
|
403
|
+
},
|
|
404
|
+
remove: "customers/1234567890/campaigns/1122334455"
|
|
405
|
+
}];
|
|
406
|
+
const campaignCriteriaOperationsExampleInput = [{
|
|
407
|
+
updateMask: "bidModifier",
|
|
408
|
+
create: {
|
|
409
|
+
campaign: "customers/1234567890/campaigns/1122334455",
|
|
410
|
+
location: { geoTargetConstant: "geoTargetConstants/1014044" }
|
|
411
|
+
},
|
|
412
|
+
update: {
|
|
413
|
+
resourceName: "customers/1234567890/campaignCriteria/1122334455~987654321",
|
|
414
|
+
bidModifier: 1.5
|
|
415
|
+
},
|
|
416
|
+
remove: "customers/1234567890/campaignCriteria/1122334455~987654321"
|
|
417
|
+
}];
|
|
418
|
+
const uploadConversionsPartialFailureError = {
|
|
419
|
+
code: 3,
|
|
420
|
+
message: "Multiple errors in 'conversions' field. Details are provided in the 'details' field.",
|
|
421
|
+
details: [{
|
|
422
|
+
"@type": "type.googleapis.com/google.ads.googleads.v25.errors.GoogleAdsFailure",
|
|
423
|
+
errors: [{
|
|
424
|
+
errorCode: { conversionUploadError: "INVALID_CONVERSION_ACTION" },
|
|
425
|
+
message: "The conversion action specified in the request is invalid.",
|
|
426
|
+
location: { fieldPathElements: [{
|
|
427
|
+
fieldName: "conversions",
|
|
428
|
+
index: 0
|
|
429
|
+
}, { fieldName: "conversionAction" }] }
|
|
430
|
+
}]
|
|
431
|
+
}]
|
|
432
|
+
};
|
|
433
|
+
const uploadCallConversionsExamplePayload = { data: {
|
|
434
|
+
partialFailureError: uploadConversionsPartialFailureError,
|
|
435
|
+
results: [{
|
|
436
|
+
callerId: "+18005551234",
|
|
437
|
+
callStartDateTime: "2026-01-15 09:30:00-05:00",
|
|
438
|
+
conversionAction: "customers/1234567890/conversionActions/987654321",
|
|
439
|
+
conversionDateTime: "2026-01-15 10:30:00-05:00"
|
|
440
|
+
}]
|
|
441
|
+
} };
|
|
442
|
+
const uploadClickConversionsExamplePayload = { data: {
|
|
443
|
+
partialFailureError: uploadConversionsPartialFailureError,
|
|
444
|
+
results: [{
|
|
445
|
+
gclid: "TeSter123.gClIdString_xYz",
|
|
446
|
+
gbraid: "gbraid_identifier_string",
|
|
447
|
+
wbraid: "wbraid_identifier_string",
|
|
448
|
+
conversionAction: "customers/1234567890/conversionActions/987654321",
|
|
449
|
+
conversionDateTime: "2026-01-15 10:30:00-05:00",
|
|
450
|
+
userIdentifiers: [{
|
|
451
|
+
userIdentifierSource: "FIRST_PARTY",
|
|
452
|
+
hashedEmail: "a1159e9df3670d549d04524532629f5477ceb7deec9b45e47e8c009506ecb2c8"
|
|
453
|
+
}]
|
|
454
|
+
}],
|
|
455
|
+
jobId: "1234567890123456789"
|
|
456
|
+
} };
|
|
457
|
+
const confirmClientLinkExamplePayload = { data: { results: [{ resourceName: "customers/2222222222/customerManagerLinks/1111111111~3333333333" }] } };
|
|
458
|
+
const getCustomerExamplePayload = { data: {
|
|
459
|
+
results: [{ customer: {
|
|
460
|
+
resourceName: "customers/1234567890",
|
|
461
|
+
id: "1234567890",
|
|
462
|
+
descriptiveName: "Example Customer Account",
|
|
463
|
+
status: "ENABLED",
|
|
464
|
+
testAccount: false,
|
|
465
|
+
manager: false
|
|
466
|
+
} }],
|
|
467
|
+
fieldMask: "customer.id,customer.descriptiveName,customer.status,customer.testAccount,customer.manager",
|
|
468
|
+
queryResourceConsumption: "80"
|
|
469
|
+
} };
|
|
470
|
+
const getConversionActionExamplePayload = { data: {
|
|
471
|
+
results: [{ conversionAction: {
|
|
472
|
+
resourceName: "customers/1234567890/conversionActions/987654321",
|
|
473
|
+
id: "987654321",
|
|
474
|
+
name: "Purchase Conversion"
|
|
475
|
+
} }],
|
|
476
|
+
fieldMask: "conversionAction.id,conversionAction.name,conversionAction.resourceName",
|
|
477
|
+
queryResourceConsumption: "702"
|
|
478
|
+
} };
|
|
479
|
+
const mutateCampaignExamplePayload = { data: { results: [{ resourceName: "customers/1234567890/campaigns/9876543210" }] } };
|
|
480
|
+
const mutateCampaignCriteriaExamplePayload = { data: { results: [{ resourceName: "customers/1234567890/campaignCriteria/9876543210~123456" }] } };
|
|
481
|
+
const searchAdsLocalServicesExamplePayload = { data: {
|
|
482
|
+
results: [{ campaign: {
|
|
483
|
+
resourceName: "customers/1234567890/campaigns/9876543210",
|
|
484
|
+
id: "9876543210",
|
|
485
|
+
name: "Local Services Campaign",
|
|
486
|
+
status: "ENABLED"
|
|
487
|
+
} }],
|
|
488
|
+
nextPageToken: "CJL5XLT2PWDmIpGNGciABRnu",
|
|
489
|
+
fieldMask: "campaign.resourceName,campaign.id,campaign.name,campaign.status",
|
|
490
|
+
queryResourceConsumption: "74"
|
|
491
|
+
} };
|
|
492
|
+
const accountReportsExamplePayload = { data: {
|
|
493
|
+
accountReports: [{
|
|
494
|
+
accountId: "1234567890",
|
|
495
|
+
aggregatorInfo: { aggregatorProviderId: "1234567890" },
|
|
496
|
+
averageFiveStarRating: 4.8,
|
|
497
|
+
averageWeeklyBudget: 500,
|
|
498
|
+
businessName: "Example Plumbing Services",
|
|
499
|
+
currentPeriodChargedLeads: "15",
|
|
500
|
+
currentPeriodConnectedPhoneCalls: "25",
|
|
501
|
+
currentPeriodPhoneCalls: "30",
|
|
502
|
+
currentPeriodTotalCost: 450.5,
|
|
503
|
+
impressionsLastTwoDays: "1250",
|
|
504
|
+
phoneLeadResponsiveness: .95,
|
|
505
|
+
previousPeriodChargedLeads: "12",
|
|
506
|
+
previousPeriodConnectedPhoneCalls: "20",
|
|
507
|
+
previousPeriodPhoneCalls: "28",
|
|
508
|
+
previousPeriodTotalCost: 380.25,
|
|
509
|
+
totalReview: 125
|
|
510
|
+
}],
|
|
511
|
+
nextPageToken: "CJL5XLT2PWDmIpGNGciABRnu"
|
|
512
|
+
} };
|
|
513
|
+
const detailedLeadReportsExamplePayload = { data: {
|
|
514
|
+
detailedLeadReports: [{
|
|
515
|
+
accountId: "1234567890",
|
|
516
|
+
aggregatorInfo: { aggregatorProviderId: "1234567890" },
|
|
517
|
+
businessName: "Example Plumbing Services",
|
|
518
|
+
leadType: "MESSAGE",
|
|
519
|
+
chargeStatus: "CHARGED",
|
|
520
|
+
currencyCode: "USD",
|
|
521
|
+
disputeStatus: "DISPUTE_INELIGIBLE",
|
|
522
|
+
geo: "San Francisco, CA",
|
|
523
|
+
googleAdsLeadId: "1122334455",
|
|
524
|
+
leadCategory: "PLUMBER",
|
|
525
|
+
leadCreationTimestamp: "2026-01-15T10:30:00Z",
|
|
526
|
+
leadId: "987654321",
|
|
527
|
+
leadPrice: 25.5,
|
|
528
|
+
messageLead: {
|
|
529
|
+
customerName: "Jane Smith",
|
|
530
|
+
jobType: "Pipe Repair",
|
|
531
|
+
postalCode: "94105"
|
|
532
|
+
},
|
|
533
|
+
timezone: {
|
|
534
|
+
id: "America/Los_Angeles",
|
|
535
|
+
version: ""
|
|
536
|
+
}
|
|
537
|
+
}],
|
|
538
|
+
nextPageToken: "CJL5XLT2PWDmIpGNGciABRnu"
|
|
539
|
+
} };
|
|
540
|
+
const inviteUserExamplePayload = { data: { result: {
|
|
541
|
+
resourceName: "customers/1234567890/customerUserAccessInvitations/9876543210",
|
|
542
|
+
multiPartyAuthReview: ""
|
|
543
|
+
} } };
|
|
544
|
+
const listAccessibleCustomersExamplePayload = { data: { resourceNames: ["customers/1234567890", "customers/5555555555"] } };
|
|
545
|
+
const listCustomersExamplePayload = { data: {
|
|
546
|
+
results: [{ customerClient: {
|
|
547
|
+
resourceName: "customers/1234567890/customerClients/1234567890",
|
|
548
|
+
clientCustomer: "customers/1234567890",
|
|
549
|
+
id: "1234567890",
|
|
550
|
+
hidden: false,
|
|
551
|
+
level: "1"
|
|
552
|
+
} }, { customerClient: {
|
|
553
|
+
resourceName: "customers/1234567890/customerClients/5555555555",
|
|
554
|
+
clientCustomer: "customers/5555555555",
|
|
555
|
+
id: "5555555555",
|
|
556
|
+
hidden: false,
|
|
557
|
+
level: "2"
|
|
558
|
+
} }],
|
|
559
|
+
nextPageToken: "CJL5XLT2PWDmIpGNGciABRnu",
|
|
560
|
+
fieldMask: "customerClient.resourceName,customerClient.clientCustomer,customerClient.id,customerClient.level,customerClient.hidden,customerClient.level"
|
|
561
|
+
} };
|
|
562
|
+
const createClientLinkExamplePayload = { data: {
|
|
563
|
+
resourceName: "customers/1111111111/customerClientLinks/2222222222~3333333333",
|
|
564
|
+
managerCustomerId: "1111111111",
|
|
565
|
+
clientCustomerId: "2222222222",
|
|
566
|
+
managerLinkId: "3333333333"
|
|
567
|
+
} };
|
|
568
|
+
const rawRequestExamplePayload = { data: { resourceNames: ["customers/1234567890", "customers/5555555555"] } };
|
|
569
|
+
const listAccessibleCustomersDataSourceExamplePayload = { result: [{
|
|
570
|
+
label: "Example Account - 123-456-7890",
|
|
571
|
+
key: "1234567890"
|
|
572
|
+
}] };
|
|
573
|
+
const listAccessibleSubAccountsExamplePayload = { result: [{
|
|
574
|
+
label: "Example Account - 123-456-7890",
|
|
575
|
+
key: "1234567890"
|
|
576
|
+
}] };
|
|
577
|
+
const campaignChangesTriggerExamplePayload = { payload: {
|
|
578
|
+
headers: {},
|
|
579
|
+
queryParameters: {},
|
|
580
|
+
rawBody: { data: null },
|
|
581
|
+
body: { data: {
|
|
582
|
+
changes: [{
|
|
583
|
+
changeType: "created",
|
|
584
|
+
campaignId: "12345678901",
|
|
585
|
+
campaignName: "Example-Campaign-1",
|
|
586
|
+
field: "campaign",
|
|
587
|
+
oldValue: null,
|
|
588
|
+
newValue: {
|
|
589
|
+
resourceName: "customers/1234567890/campaigns/12345678901",
|
|
590
|
+
status: "ENABLED",
|
|
591
|
+
name: "Example-Campaign-1",
|
|
592
|
+
id: "12345678901"
|
|
593
|
+
},
|
|
594
|
+
changedAt: "2026-01-01 12:00:00"
|
|
595
|
+
}, {
|
|
596
|
+
changeType: "budget",
|
|
597
|
+
campaignId: "12345678901",
|
|
598
|
+
campaignName: "Example-Campaign-1",
|
|
599
|
+
field: "budget_amount_micros",
|
|
600
|
+
oldValue: "50000000",
|
|
601
|
+
newValue: "75000000",
|
|
602
|
+
changedAt: "2026-01-01 12:05:00"
|
|
603
|
+
}],
|
|
604
|
+
changesDetected: 2,
|
|
605
|
+
timeRange: {
|
|
606
|
+
start: "2026-01-01 11:00:00",
|
|
607
|
+
end: "2026-01-01 12:10:00"
|
|
608
|
+
},
|
|
609
|
+
syncedAt: "2026-01-01 12:10:00"
|
|
610
|
+
} },
|
|
611
|
+
pathFragment: "",
|
|
612
|
+
webhookUrls: {},
|
|
613
|
+
webhookApiKeys: {},
|
|
614
|
+
invokeUrl: "",
|
|
615
|
+
executionId: "RXhhbXBsZUV4ZWN1dGlvblJlc3VsdElk",
|
|
616
|
+
customer: {
|
|
617
|
+
id: "testCustomerId",
|
|
618
|
+
name: "Test Customer",
|
|
619
|
+
externalId: "testExternalId"
|
|
620
|
+
},
|
|
621
|
+
instance: {
|
|
622
|
+
id: "testInstanceId",
|
|
623
|
+
name: "Test Instance"
|
|
624
|
+
},
|
|
625
|
+
user: {
|
|
626
|
+
id: "testUserId",
|
|
627
|
+
email: "user@example.com",
|
|
628
|
+
name: "Test User",
|
|
629
|
+
externalId: "testUserExternalId"
|
|
630
|
+
},
|
|
631
|
+
integration: {
|
|
632
|
+
id: "testIntegrationId",
|
|
633
|
+
name: "Test Integration",
|
|
634
|
+
versionSequenceId: "1",
|
|
635
|
+
externalVersion: ""
|
|
636
|
+
},
|
|
637
|
+
flow: {
|
|
638
|
+
id: "testFlowId",
|
|
639
|
+
name: "Test Flow",
|
|
640
|
+
stableId: ""
|
|
641
|
+
},
|
|
642
|
+
startedAt: "2024-01-15T00:00:00.000Z",
|
|
643
|
+
globalDebug: false
|
|
644
|
+
} };
|
|
645
|
+
const budgetAlertTriggerExamplePayload = { payload: {
|
|
646
|
+
headers: {},
|
|
647
|
+
queryParameters: {},
|
|
648
|
+
rawBody: { data: null },
|
|
649
|
+
body: { data: {
|
|
650
|
+
alerts: [{
|
|
651
|
+
campaignId: "23302011123",
|
|
652
|
+
campaignName: "Example-Campaign-1",
|
|
653
|
+
budgetAmount: 100,
|
|
654
|
+
spent: 1e3,
|
|
655
|
+
percentSpent: 1e3,
|
|
656
|
+
remaining: -900,
|
|
657
|
+
period: "DAILY",
|
|
658
|
+
shouldAlert: true,
|
|
659
|
+
severity: BUDGET_SEVERITY.CRITICAL,
|
|
660
|
+
message: "Campaign has exceeded daily"
|
|
661
|
+
}],
|
|
662
|
+
totalCampaignsMonitored: 1,
|
|
663
|
+
alertThreshold: 80
|
|
664
|
+
} },
|
|
665
|
+
pathFragment: "",
|
|
666
|
+
webhookUrls: {},
|
|
667
|
+
webhookApiKeys: {},
|
|
668
|
+
invokeUrl: "",
|
|
669
|
+
executionId: "RXhhbXBsZUV4ZWN1dGlvblJlc3VsdElk",
|
|
670
|
+
customer: {
|
|
671
|
+
id: "testCustomerId",
|
|
672
|
+
name: "Test Customer",
|
|
673
|
+
externalId: "testExternalId"
|
|
674
|
+
},
|
|
675
|
+
instance: {
|
|
676
|
+
id: "testInstanceId",
|
|
677
|
+
name: "Test Instance"
|
|
678
|
+
},
|
|
679
|
+
user: {
|
|
680
|
+
id: "testUserId",
|
|
681
|
+
email: "user@example.com",
|
|
682
|
+
name: "Test User",
|
|
683
|
+
externalId: "testUserExternalId"
|
|
684
|
+
},
|
|
685
|
+
integration: {
|
|
686
|
+
id: "testIntegrationId",
|
|
687
|
+
name: "Test Integration",
|
|
688
|
+
versionSequenceId: "1",
|
|
689
|
+
externalVersion: ""
|
|
690
|
+
},
|
|
691
|
+
flow: {
|
|
692
|
+
id: "testFlowId",
|
|
693
|
+
name: "Test Flow",
|
|
694
|
+
stableId: ""
|
|
695
|
+
},
|
|
696
|
+
startedAt: "2024-01-15T00:00:00.000Z",
|
|
697
|
+
globalDebug: false
|
|
698
|
+
} };
|
|
699
|
+
const changeHistoryTriggerExamplePayload = { payload: {
|
|
700
|
+
headers: {},
|
|
701
|
+
queryParameters: {},
|
|
702
|
+
rawBody: { data: null },
|
|
703
|
+
body: { data: {
|
|
704
|
+
changes: [{ changeEvent: {
|
|
705
|
+
resourceName: "customers/6577008345/changeEvents/1764141874897602~0~1",
|
|
706
|
+
changeDateTime: "2025-11-26 01:24:34.897602",
|
|
707
|
+
changeResourceType: "CAMPAIGN_BUDGET",
|
|
708
|
+
changeResourceName: "customers/6577008345/campaignBudgets/15170398017",
|
|
709
|
+
clientType: "GOOGLE_ADS_WEB_CLIENT",
|
|
710
|
+
userEmail: "exampleUser@email.com",
|
|
711
|
+
oldResource: { campaignBudget: {} },
|
|
712
|
+
newResource: { campaignBudget: {
|
|
713
|
+
resourceName: "customers/6577008345/campaignBudgets/15170398017",
|
|
714
|
+
deliveryMethod: "STANDARD",
|
|
715
|
+
period: "DAILY",
|
|
716
|
+
type: "STANDARD",
|
|
717
|
+
id: "15170398017",
|
|
718
|
+
amountMicros: "481350000",
|
|
719
|
+
explicitlyShared: false
|
|
720
|
+
} },
|
|
721
|
+
resourceChangeOperation: "CREATE"
|
|
722
|
+
} }],
|
|
723
|
+
changeCount: 1,
|
|
724
|
+
timeRange: {
|
|
725
|
+
start: "2025-11-26 01:11:50",
|
|
726
|
+
end: "2025-11-26 01:30:17"
|
|
727
|
+
}
|
|
728
|
+
} },
|
|
729
|
+
pathFragment: "",
|
|
730
|
+
webhookUrls: {},
|
|
731
|
+
webhookApiKeys: {},
|
|
732
|
+
invokeUrl: "",
|
|
733
|
+
executionId: "RXhhbXBsZUV4ZWN1dGlvblJlc3VsdElk",
|
|
734
|
+
customer: {
|
|
735
|
+
id: "testCustomerId",
|
|
736
|
+
name: "Test Customer",
|
|
737
|
+
externalId: "testExternalId"
|
|
738
|
+
},
|
|
739
|
+
instance: {
|
|
740
|
+
id: "testInstanceId",
|
|
741
|
+
name: "Test Instance"
|
|
742
|
+
},
|
|
743
|
+
user: {
|
|
744
|
+
id: "testUserId",
|
|
745
|
+
email: "user@example.com",
|
|
746
|
+
name: "Test User",
|
|
747
|
+
externalId: "testUserExternalId"
|
|
748
|
+
},
|
|
749
|
+
integration: {
|
|
750
|
+
id: "testIntegrationId",
|
|
751
|
+
name: "Test Integration",
|
|
752
|
+
versionSequenceId: "1",
|
|
753
|
+
externalVersion: ""
|
|
754
|
+
},
|
|
755
|
+
flow: {
|
|
756
|
+
id: "testFlowId",
|
|
757
|
+
name: "Test Flow",
|
|
758
|
+
stableId: ""
|
|
759
|
+
},
|
|
760
|
+
startedAt: "2024-01-15T00:00:00.000Z",
|
|
761
|
+
globalDebug: false
|
|
762
|
+
} };
|
|
763
|
+
//#endregion
|
|
764
|
+
//#region src/util/format.ts
|
|
765
|
+
const formatAccountNumber = (accountNumber) => {
|
|
766
|
+
try {
|
|
767
|
+
const match = /^(\d{3})(\d{3})(\d{4})$/.exec(accountNumber);
|
|
768
|
+
if (match) return `${match[1]}-${match[2]}-${match[3]}`;
|
|
769
|
+
} catch {
|
|
770
|
+
return accountNumber;
|
|
771
|
+
}
|
|
772
|
+
return accountNumber;
|
|
773
|
+
};
|
|
774
|
+
//#endregion
|
|
775
|
+
//#region src/util/pagination/searchGoogleAds.ts
|
|
776
|
+
async function searchGoogleAds(client, options) {
|
|
777
|
+
const { customerId, params, fetchAll = false } = options;
|
|
778
|
+
const url = googleAdsSearchPath(customerId);
|
|
779
|
+
if (fetchAll) options.params.pageToken = void 0;
|
|
780
|
+
const firstData = (await client.post(url, params)).data;
|
|
781
|
+
if (!fetchAll || !firstData.nextPageToken) return firstData;
|
|
782
|
+
const accumulated = [...firstData.results ?? []];
|
|
783
|
+
let nextPageToken = firstData.nextPageToken;
|
|
784
|
+
while (nextPageToken) {
|
|
785
|
+
const { data } = await client.post(url, {
|
|
786
|
+
...options.params,
|
|
787
|
+
pageToken: nextPageToken
|
|
788
|
+
});
|
|
789
|
+
if (data.results) accumulated.push(...data.results);
|
|
790
|
+
nextPageToken = data.nextPageToken || "";
|
|
791
|
+
}
|
|
792
|
+
return {
|
|
793
|
+
...firstData,
|
|
794
|
+
results: accumulated,
|
|
795
|
+
nextPageToken: void 0
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
//#endregion
|
|
799
|
+
//#region src/util/records.ts
|
|
800
|
+
const getCustomerDescriptiveName = async (client, customerId) => {
|
|
801
|
+
try {
|
|
802
|
+
const { data } = await client.post(googleAdsSearchPath(customerId), { query: "SELECT customer.descriptive_name FROM customer" });
|
|
803
|
+
if (data?.results[0]?.customer) return data.results[0].customer?.descriptiveName || "(No descriptive name)";
|
|
804
|
+
return NO_DESCRIPTIVE_NAME;
|
|
805
|
+
} catch (_error) {
|
|
806
|
+
return DESCRIPTIVE_NAME_UNAVAILABLE;
|
|
807
|
+
}
|
|
808
|
+
};
|
|
809
|
+
//#endregion
|
|
810
|
+
//#region src/util/triggers/budgetCalculation.ts
|
|
811
|
+
const calculateBudgetStatus = (campaign, alertThreshold) => {
|
|
812
|
+
const budgetMicros = _prismatic_io_spectral.util.types.toNumber(campaign?.campaignBudget?.amountMicros);
|
|
813
|
+
const costMicros = _prismatic_io_spectral.util.types.toNumber(campaign?.metrics?.costMicros);
|
|
814
|
+
const budgetAmount = budgetMicros / MICROS_TO_DOLLARS_CONVERSION_FACTOR;
|
|
815
|
+
const spent = costMicros / MICROS_TO_DOLLARS_CONVERSION_FACTOR;
|
|
816
|
+
const percentSpent = budgetAmount > 0 ? spent / budgetAmount * 100 : 0;
|
|
817
|
+
const remaining = budgetAmount - spent;
|
|
818
|
+
let severity = BUDGET_SEVERITY.INFO;
|
|
819
|
+
let shouldAlert = false;
|
|
820
|
+
let message = "";
|
|
821
|
+
const period = campaign.campaignBudget?.period?.toLowerCase() || "budget";
|
|
822
|
+
if (percentSpent >= 100) {
|
|
823
|
+
severity = BUDGET_SEVERITY.CRITICAL;
|
|
824
|
+
shouldAlert = true;
|
|
825
|
+
message = `Campaign has exceeded ${period}`;
|
|
826
|
+
} else if (percentSpent >= 95) {
|
|
827
|
+
severity = BUDGET_SEVERITY.CRITICAL;
|
|
828
|
+
shouldAlert = true;
|
|
829
|
+
message = `Campaign has spent ${percentSpent.toFixed(1)}% of ${period}`;
|
|
830
|
+
} else if (percentSpent >= alertThreshold) {
|
|
831
|
+
severity = BUDGET_SEVERITY.WARNING;
|
|
832
|
+
shouldAlert = true;
|
|
833
|
+
message = `Campaign has spent ${percentSpent.toFixed(1)}% of ${period}`;
|
|
834
|
+
}
|
|
835
|
+
return {
|
|
836
|
+
campaignId: campaign.campaign.id,
|
|
837
|
+
campaignName: campaign.campaign.name,
|
|
838
|
+
budgetAmount,
|
|
839
|
+
spent,
|
|
840
|
+
percentSpent: Math.round(percentSpent),
|
|
841
|
+
remaining,
|
|
842
|
+
period: campaign.campaignBudget?.period || "UNKNOWN",
|
|
843
|
+
shouldAlert,
|
|
844
|
+
severity,
|
|
845
|
+
message
|
|
846
|
+
};
|
|
847
|
+
};
|
|
848
|
+
//#endregion
|
|
849
|
+
//#region src/util/triggers/changeDetection.ts
|
|
850
|
+
const isAllChangeTypesSelected = (changeTypes) => changeTypes.length === 0 || changeTypes.includes(CHANGE_TYPE.ALL);
|
|
851
|
+
const isSelected = (changeTypes, changeType) => changeTypes.includes(changeType) || isAllChangeTypesSelected(changeTypes);
|
|
852
|
+
const mapChangeEventsToCampaignChanges = (rows, changeTypes) => {
|
|
853
|
+
const changes = [];
|
|
854
|
+
for (const row of [...rows].reverse()) {
|
|
855
|
+
const event = row.changeEvent;
|
|
856
|
+
if (!event) continue;
|
|
857
|
+
const campaignId = row.campaign?.id ?? "";
|
|
858
|
+
const campaignName = row.campaign?.name ?? "";
|
|
859
|
+
const changedAt = event.changeDateTime;
|
|
860
|
+
const operation = event.resourceChangeOperation;
|
|
861
|
+
const oldCampaign = event.oldResource?.campaign;
|
|
862
|
+
const newCampaign = event.newResource?.campaign;
|
|
863
|
+
if (event.changeResourceType === CAMPAIGN_CHANGE_RESOURCE_TYPE.CAMPAIGN) {
|
|
864
|
+
if (operation === "CREATE") {
|
|
865
|
+
changes.push({
|
|
866
|
+
changeType: "created",
|
|
867
|
+
campaignId,
|
|
868
|
+
campaignName,
|
|
869
|
+
field: "campaign",
|
|
870
|
+
oldValue: null,
|
|
871
|
+
newValue: newCampaign ?? null,
|
|
872
|
+
changedAt
|
|
873
|
+
});
|
|
874
|
+
continue;
|
|
875
|
+
}
|
|
876
|
+
if (operation === "REMOVE") {
|
|
877
|
+
changes.push({
|
|
878
|
+
changeType: "deleted",
|
|
879
|
+
campaignId,
|
|
880
|
+
campaignName,
|
|
881
|
+
field: "campaign",
|
|
882
|
+
oldValue: oldCampaign ?? null,
|
|
883
|
+
newValue: null,
|
|
884
|
+
changedAt
|
|
885
|
+
});
|
|
886
|
+
continue;
|
|
887
|
+
}
|
|
888
|
+
if (isSelected(changeTypes, CHANGE_TYPE.STATUS) && oldCampaign?.status !== newCampaign?.status) changes.push({
|
|
889
|
+
changeType: "status",
|
|
890
|
+
campaignId,
|
|
891
|
+
campaignName,
|
|
892
|
+
field: "status",
|
|
893
|
+
oldValue: oldCampaign?.status ?? null,
|
|
894
|
+
newValue: newCampaign?.status ?? null,
|
|
895
|
+
changedAt
|
|
896
|
+
});
|
|
897
|
+
if (isSelected(changeTypes, CHANGE_TYPE.BIDDING) && oldCampaign?.biddingStrategyType !== newCampaign?.biddingStrategyType) changes.push({
|
|
898
|
+
changeType: "bidding",
|
|
899
|
+
campaignId,
|
|
900
|
+
campaignName,
|
|
901
|
+
field: "bidding_strategy_type",
|
|
902
|
+
oldValue: oldCampaign?.biddingStrategyType ?? null,
|
|
903
|
+
newValue: newCampaign?.biddingStrategyType ?? null,
|
|
904
|
+
changedAt
|
|
905
|
+
});
|
|
906
|
+
continue;
|
|
907
|
+
}
|
|
908
|
+
const oldAmount = event.oldResource?.campaignBudget?.amountMicros;
|
|
909
|
+
const newAmount = event.newResource?.campaignBudget?.amountMicros;
|
|
910
|
+
if (isSelected(changeTypes, CHANGE_TYPE.BUDGET) && oldAmount !== newAmount) changes.push({
|
|
911
|
+
changeType: "budget",
|
|
912
|
+
campaignId,
|
|
913
|
+
campaignName,
|
|
914
|
+
field: "budget_amount_micros",
|
|
915
|
+
oldValue: oldAmount ?? null,
|
|
916
|
+
newValue: newAmount ?? null,
|
|
917
|
+
changedAt
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
return changes.reverse();
|
|
921
|
+
};
|
|
922
|
+
//#endregion
|
|
923
|
+
//#region src/util/triggers/dateUtils.ts
|
|
924
|
+
const getCustomerTimezone = async (client, customerId) => {
|
|
925
|
+
const timeZone = (await client.post(googleAdsSearchPath(customerId), { query: "SELECT customer.time_zone FROM customer LIMIT 1" })).data?.results?.[0]?.customer?.timeZone;
|
|
926
|
+
if (!timeZone) throw new Error(`Unable to retrieve timezone for customer ${customerId}`);
|
|
927
|
+
return timeZone;
|
|
928
|
+
};
|
|
929
|
+
const getCurrentDate = (timeZone) => {
|
|
930
|
+
return new Intl.DateTimeFormat("en-CA", {
|
|
931
|
+
timeZone,
|
|
932
|
+
year: "numeric",
|
|
933
|
+
month: "2-digit",
|
|
934
|
+
day: "2-digit"
|
|
935
|
+
}).format(/* @__PURE__ */ new Date());
|
|
936
|
+
};
|
|
937
|
+
const getPreviousDate = (timeZone) => {
|
|
938
|
+
const formatter = new Intl.DateTimeFormat("en-CA", {
|
|
939
|
+
timeZone,
|
|
940
|
+
year: "numeric",
|
|
941
|
+
month: "2-digit",
|
|
942
|
+
day: "2-digit"
|
|
943
|
+
});
|
|
944
|
+
const date = /* @__PURE__ */ new Date();
|
|
945
|
+
date.setDate(date.getDate() - 1);
|
|
946
|
+
return formatter.format(date);
|
|
947
|
+
};
|
|
948
|
+
const getGAQLDateTime = (timeZone, hoursAgo) => {
|
|
949
|
+
const date = /* @__PURE__ */ new Date();
|
|
950
|
+
if (typeof hoursAgo === "number" && hoursAgo > 0) date.setHours(date.getHours() - hoursAgo);
|
|
951
|
+
const dateFormatter = new Intl.DateTimeFormat("en-CA", {
|
|
952
|
+
timeZone,
|
|
953
|
+
year: "numeric",
|
|
954
|
+
month: "2-digit",
|
|
955
|
+
day: "2-digit"
|
|
956
|
+
});
|
|
957
|
+
const timeFormatter = new Intl.DateTimeFormat("en-GB", {
|
|
958
|
+
timeZone,
|
|
959
|
+
hour: "2-digit",
|
|
960
|
+
minute: "2-digit",
|
|
961
|
+
second: "2-digit",
|
|
962
|
+
hour12: false
|
|
963
|
+
});
|
|
964
|
+
return `${dateFormatter.format(date)} ${timeFormatter.format(date)}`;
|
|
965
|
+
};
|
|
966
|
+
const clampToChangeEventWindow = (sinceTime, timeZone) => {
|
|
967
|
+
const floor = getGAQLDateTime(timeZone, 696);
|
|
968
|
+
return sinceTime < floor ? floor : sinceTime;
|
|
969
|
+
};
|
|
970
|
+
//#endregion
|
|
971
|
+
//#region src/util/triggers/changeEventPagination.ts
|
|
972
|
+
const toGAQLSecond = (changeDateTime) => changeDateTime.slice(0, 19);
|
|
973
|
+
const nextGAQLSecond = (dateTime) => new Date(Date.parse(`${dateTime.replace(" ", "T")}Z`) + 1e3).toISOString().slice(0, 19).replace("T", " ");
|
|
974
|
+
const isChangeEventCursor = (value) => {
|
|
975
|
+
const cursor = value;
|
|
976
|
+
return typeof cursor?.sinceTime === "string" && typeof cursor.toTime === "string" && Array.isArray(cursor.boundaryResourceNames);
|
|
977
|
+
};
|
|
978
|
+
const resolveChangeEventPageSize = (batchEnabled) => batchEnabled === true ? BATCHED_CHANGE_EVENT_ROW_LIMIT : CHANGE_EVENT_ROW_LIMIT;
|
|
979
|
+
const resolveChangeEventCursor = (options) => {
|
|
980
|
+
const { incoming, state, nowTime, timeZone } = options;
|
|
981
|
+
const cursor = [incoming, state.inFlightCursor].find(isChangeEventCursor) ?? {
|
|
982
|
+
sinceTime: state.lastChangeTime,
|
|
983
|
+
toTime: nowTime,
|
|
984
|
+
boundaryResourceNames: []
|
|
985
|
+
};
|
|
986
|
+
return {
|
|
987
|
+
...cursor,
|
|
988
|
+
sinceTime: clampToChangeEventWindow(cursor.sinceTime, timeZone)
|
|
989
|
+
};
|
|
990
|
+
};
|
|
991
|
+
const advanceChangeEventCursor = (rows, cursor, pageSize, logger) => {
|
|
992
|
+
const alreadyEmitted = new Set(cursor.boundaryResourceNames);
|
|
993
|
+
const emit = rows.filter((row) => !alreadyEmitted.has(row.changeEvent?.resourceName ?? ""));
|
|
994
|
+
if (rows.length < pageSize) return {
|
|
995
|
+
emit,
|
|
996
|
+
nextCursor: null
|
|
997
|
+
};
|
|
998
|
+
const seconds = rows.map((row) => toGAQLSecond(row.changeEvent?.changeDateTime ?? cursor.sinceTime));
|
|
999
|
+
const newestSecond = seconds.reduce((max, s) => s > max ? s : max);
|
|
1000
|
+
if (emit.length === 0) {
|
|
1001
|
+
logger?.warn(`More than ${pageSize} change events share ${newestSecond}; skipping to the next second.`);
|
|
1002
|
+
const sinceTime = nextGAQLSecond(newestSecond);
|
|
1003
|
+
return {
|
|
1004
|
+
emit,
|
|
1005
|
+
nextCursor: sinceTime < cursor.toTime ? {
|
|
1006
|
+
...cursor,
|
|
1007
|
+
sinceTime,
|
|
1008
|
+
boundaryResourceNames: []
|
|
1009
|
+
} : null
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
const atNewestSecond = rows.filter((_row, index) => seconds[index] === newestSecond).map((row) => row.changeEvent?.resourceName ?? "").filter((name) => name !== "");
|
|
1013
|
+
const carried = newestSecond === cursor.sinceTime ? cursor.boundaryResourceNames : [];
|
|
1014
|
+
return {
|
|
1015
|
+
emit,
|
|
1016
|
+
nextCursor: {
|
|
1017
|
+
...cursor,
|
|
1018
|
+
sinceTime: newestSecond,
|
|
1019
|
+
boundaryResourceNames: [.../* @__PURE__ */ new Set([...carried, ...atNewestSecond])]
|
|
1020
|
+
}
|
|
1021
|
+
};
|
|
1022
|
+
};
|
|
1023
|
+
//#endregion
|
|
1024
|
+
//#region src/util/triggers/handlePollingError.ts
|
|
1025
|
+
const handlePollingError = (error, pollState, context, serviceName) => {
|
|
1026
|
+
const consecutiveErrors = (pollState.consecutiveErrors || 0) + 1;
|
|
1027
|
+
const errorCount = (pollState.errorCount || 0) + 1;
|
|
1028
|
+
context.polling.setState({
|
|
1029
|
+
...pollState,
|
|
1030
|
+
errorCount,
|
|
1031
|
+
consecutiveErrors
|
|
1032
|
+
});
|
|
1033
|
+
if (consecutiveErrors >= 3) context.logger.error(`${serviceName} polling failed ${consecutiveErrors} times consecutively. Error: ${error.message}`);
|
|
1034
|
+
throw error;
|
|
1035
|
+
};
|
|
1036
|
+
//#endregion
|
|
1037
|
+
//#region src/util/triggers/queryBuilders.ts
|
|
1038
|
+
const resolveCampaignChangeResourceTypes = (changeTypes) => {
|
|
1039
|
+
const all = isAllChangeTypesSelected(changeTypes);
|
|
1040
|
+
const types = [CAMPAIGN_CHANGE_RESOURCE_TYPE.CAMPAIGN];
|
|
1041
|
+
if (all || changeTypes.includes(CHANGE_TYPE.BUDGET)) types.push(CAMPAIGN_CHANGE_RESOURCE_TYPE.CAMPAIGN_BUDGET);
|
|
1042
|
+
return types;
|
|
1043
|
+
};
|
|
1044
|
+
const buildCampaignChangeEventQuery = (options) => {
|
|
1045
|
+
const { sinceTime, toTime, changeTypes, limit } = options;
|
|
1046
|
+
return `
|
|
1047
|
+
SELECT
|
|
1048
|
+
change_event.resource_name,
|
|
1049
|
+
change_event.change_date_time,
|
|
1050
|
+
change_event.change_resource_type,
|
|
1051
|
+
change_event.change_resource_name,
|
|
1052
|
+
change_event.resource_change_operation,
|
|
1053
|
+
change_event.changed_fields,
|
|
1054
|
+
change_event.old_resource,
|
|
1055
|
+
change_event.new_resource,
|
|
1056
|
+
campaign.id,
|
|
1057
|
+
campaign.name
|
|
1058
|
+
FROM change_event
|
|
1059
|
+
WHERE change_event.change_date_time >= '${sinceTime}'
|
|
1060
|
+
AND change_event.change_date_time < '${toTime}'
|
|
1061
|
+
AND change_event.change_resource_type IN (${resolveCampaignChangeResourceTypes(changeTypes).map((type) => `'${type}'`).join(",")})
|
|
1062
|
+
ORDER BY change_event.change_date_time ASC
|
|
1063
|
+
LIMIT ${limit}
|
|
1064
|
+
`.trim();
|
|
1065
|
+
};
|
|
1066
|
+
const buildBudgetAlertQuery = (options) => {
|
|
1067
|
+
const { includeSharedBudgets } = options;
|
|
1068
|
+
return `
|
|
1069
|
+
SELECT
|
|
1070
|
+
campaign.id,
|
|
1071
|
+
campaign.name,
|
|
1072
|
+
campaign_budget.amount_micros,
|
|
1073
|
+
campaign_budget.total_amount_micros,
|
|
1074
|
+
campaign_budget.period,
|
|
1075
|
+
metrics.cost_micros
|
|
1076
|
+
FROM campaign
|
|
1077
|
+
WHERE segments.date DURING TODAY
|
|
1078
|
+
AND campaign.status = 'ENABLED'${includeSharedBudgets ? "" : "\n AND campaign_budget.explicitly_shared = FALSE"}
|
|
1079
|
+
`.trim();
|
|
1080
|
+
};
|
|
1081
|
+
const buildChangeHistoryQuery = (options) => {
|
|
1082
|
+
const { sinceTime, toTime, resourceTypes, includeUserInfo, limit } = options;
|
|
1083
|
+
const resourceFilter = resourceTypes.length > 0 ? `AND change_event.change_resource_type IN (${resourceTypes.map((type) => `'${CHANGE_HISTORY_RESOURCE_TYPE_QUERY_VALUE[type] ?? type}'`).join(",")})` : "";
|
|
1084
|
+
return `
|
|
1085
|
+
SELECT
|
|
1086
|
+
change_event.resource_name,
|
|
1087
|
+
change_event.change_date_time,
|
|
1088
|
+
change_event.change_resource_type,
|
|
1089
|
+
change_event.change_resource_name,
|
|
1090
|
+
${includeUserInfo ? "change_event.user_email,\n change_event.client_type," : ""}
|
|
1091
|
+
change_event.resource_change_operation,
|
|
1092
|
+
change_event.old_resource,
|
|
1093
|
+
change_event.new_resource
|
|
1094
|
+
FROM change_event
|
|
1095
|
+
WHERE change_event.change_date_time >= '${sinceTime}'
|
|
1096
|
+
AND change_event.change_date_time < '${toTime}'
|
|
1097
|
+
${resourceFilter}
|
|
1098
|
+
ORDER BY change_event.change_date_time ASC
|
|
1099
|
+
LIMIT ${limit}
|
|
1100
|
+
`.trim();
|
|
1101
|
+
};
|
|
1102
|
+
//#endregion
|
|
1103
|
+
//#region src/util/triggers/resolvers.ts
|
|
1104
|
+
const resolveCampaignChanges = (data) => {
|
|
1105
|
+
return ((data ?? {
|
|
1106
|
+
changes: [],
|
|
1107
|
+
changesDetected: 0,
|
|
1108
|
+
timeRange: {
|
|
1109
|
+
start: "",
|
|
1110
|
+
end: ""
|
|
1111
|
+
},
|
|
1112
|
+
syncedAt: ""
|
|
1113
|
+
}).changes ?? []).map((record) => ({
|
|
1114
|
+
changeType: record.changeType,
|
|
1115
|
+
record
|
|
1116
|
+
}));
|
|
1117
|
+
};
|
|
1118
|
+
const resolveChangeHistoryItems = (data) => {
|
|
1119
|
+
return ((data ?? {
|
|
1120
|
+
changes: [],
|
|
1121
|
+
changeCount: 0,
|
|
1122
|
+
timeRange: {
|
|
1123
|
+
start: "",
|
|
1124
|
+
end: ""
|
|
1125
|
+
}
|
|
1126
|
+
}).changes ?? []).map((record) => ({
|
|
1127
|
+
changeType: OPERATION_TO_CHANGE_TYPE[record.changeEvent.resourceChangeOperation] ?? "updated",
|
|
1128
|
+
record
|
|
1129
|
+
}));
|
|
1130
|
+
};
|
|
1131
|
+
const resolveBudgetAlerts = (data) => {
|
|
1132
|
+
return ((data ?? {
|
|
1133
|
+
alerts: [],
|
|
1134
|
+
totalCampaignsMonitored: 0,
|
|
1135
|
+
alertThreshold: 0
|
|
1136
|
+
}).alerts ?? []).map((record) => ({
|
|
1137
|
+
changeType: record.severity,
|
|
1138
|
+
record
|
|
1139
|
+
}));
|
|
1140
|
+
};
|
|
1141
|
+
//#endregion
|
|
1142
|
+
//#region src/util/triggers/triggerHelpers.ts
|
|
1143
|
+
const createTriggerClient = async (context, params) => {
|
|
1144
|
+
const client = createClient({
|
|
1145
|
+
connection: params.connection,
|
|
1146
|
+
debugEnabled: context.debug.enabled,
|
|
1147
|
+
logger: context.logger,
|
|
1148
|
+
loginCustomerId: params.managerCustomerId
|
|
1149
|
+
});
|
|
1150
|
+
return {
|
|
1151
|
+
client,
|
|
1152
|
+
timezone: await getCustomerTimezone(client, params.customerId)
|
|
1153
|
+
};
|
|
1154
|
+
};
|
|
1155
|
+
const getPollingState = (context, initialState) => {
|
|
1156
|
+
const state = context.polling.getState();
|
|
1157
|
+
return state && Object.keys(state).length > 0 ? state : initialState;
|
|
1158
|
+
};
|
|
1159
|
+
const buildTriggerPayload = (payload, data) => ({
|
|
1160
|
+
...payload,
|
|
1161
|
+
body: { data }
|
|
1162
|
+
});
|
|
1163
|
+
//#endregion
|
|
1164
|
+
//#region src/inputs/common.ts
|
|
1165
|
+
const connectionInput = (0, _prismatic_io_spectral.input)({
|
|
1166
|
+
label: "Connection",
|
|
1167
|
+
type: "connection",
|
|
1168
|
+
required: true,
|
|
1169
|
+
comments: "The Google Ads connection to use."
|
|
1170
|
+
});
|
|
1171
|
+
const customerIdInput = (0, _prismatic_io_spectral.input)({
|
|
1172
|
+
label: "Customer ID",
|
|
1173
|
+
placeholder: "Enter customer ID",
|
|
1174
|
+
type: "string",
|
|
1175
|
+
required: true,
|
|
1176
|
+
comments: "The unique identifier for the Google Ads client account. Accepts hyphenated or number forms. See [Customer ID documentation](https://developers.google.com/google-ads/api/docs/concepts/call-structure#cid).",
|
|
1177
|
+
example: "111-222-4444",
|
|
1178
|
+
dataSource: "listAccessibleCustomers",
|
|
1179
|
+
clean: cleanCustomerId
|
|
1180
|
+
});
|
|
1181
|
+
const managerCustomerIdInput = (0, _prismatic_io_spectral.input)({
|
|
1182
|
+
label: "Manager Customer ID",
|
|
1183
|
+
placeholder: "Enter manager customer ID",
|
|
1184
|
+
type: "string",
|
|
1185
|
+
required: true,
|
|
1186
|
+
comments: "The unique identifier for the Google Ads Manager account. Accepts hyphenated or number forms. When used in conjunction with a sub account as the customer ID, this value is used as the 'login-customer-id' header for the HTTP request. See [Customer ID documentation](https://developers.google.com/google-ads/api/docs/concepts/call-structure#cid).",
|
|
1187
|
+
example: "123-456-7890",
|
|
1188
|
+
dataSource: "listAccessibleCustomers",
|
|
1189
|
+
clean: cleanCustomerId
|
|
1190
|
+
});
|
|
1191
|
+
const pageSizeInput = (0, _prismatic_io_spectral.input)({
|
|
1192
|
+
label: "Page Size",
|
|
1193
|
+
placeholder: "Enter page size",
|
|
1194
|
+
type: "string",
|
|
1195
|
+
required: false,
|
|
1196
|
+
example: "100",
|
|
1197
|
+
clean: toOptionalInt,
|
|
1198
|
+
comments: "The maximum number of results to return per page."
|
|
1199
|
+
});
|
|
1200
|
+
const pageTokenInput = (0, _prismatic_io_spectral.input)({
|
|
1201
|
+
label: "Page Token",
|
|
1202
|
+
placeholder: "Enter page token",
|
|
1203
|
+
type: "string",
|
|
1204
|
+
required: false,
|
|
1205
|
+
example: "CJL5XLT2PWDmIpGNGciABRnu",
|
|
1206
|
+
clean: cleanString,
|
|
1207
|
+
comments: "The pagination cursor from a previous request. Returned in previous page responses."
|
|
1208
|
+
});
|
|
1209
|
+
const validateOnly = (0, _prismatic_io_spectral.input)({
|
|
1210
|
+
label: "Validate Only",
|
|
1211
|
+
type: "boolean",
|
|
1212
|
+
required: false,
|
|
1213
|
+
clean: _prismatic_io_spectral.util.types.toBool,
|
|
1214
|
+
default: "false",
|
|
1215
|
+
comments: "When true, the request is validated but not executed. Only errors are returned, not results."
|
|
1216
|
+
});
|
|
1217
|
+
const fetchAll = (0, _prismatic_io_spectral.input)({
|
|
1218
|
+
label: "Fetch All",
|
|
1219
|
+
type: "boolean",
|
|
1220
|
+
required: false,
|
|
1221
|
+
default: "false",
|
|
1222
|
+
clean: _prismatic_io_spectral.util.types.toBool,
|
|
1223
|
+
comments: "When true, automatically fetches all pages of results. When false, only the first page of results will be returned."
|
|
1224
|
+
});
|
|
1225
|
+
const startDateInput = (0, _prismatic_io_spectral.input)({
|
|
1226
|
+
label: "Start Date",
|
|
1227
|
+
placeholder: "Enter start date (MM-DD-YYYY)",
|
|
1228
|
+
type: "string",
|
|
1229
|
+
required: false,
|
|
1230
|
+
example: "01-01-2025",
|
|
1231
|
+
comments: "The start date of the date range, inclusive. Format: MM-DD-YYYY.",
|
|
1232
|
+
clean: cleanString
|
|
1233
|
+
});
|
|
1234
|
+
const endDateInput = (0, _prismatic_io_spectral.input)({
|
|
1235
|
+
label: "End Date",
|
|
1236
|
+
placeholder: "Enter end date (MM-DD-YYYY)",
|
|
1237
|
+
type: "string",
|
|
1238
|
+
required: false,
|
|
1239
|
+
example: "12-31-2025",
|
|
1240
|
+
comments: "The end date of the date range, inclusive. Format: MM-DD-YYYY.",
|
|
1241
|
+
clean: cleanString
|
|
1242
|
+
});
|
|
1243
|
+
const customerClientLevel = (0, _prismatic_io_spectral.input)({
|
|
1244
|
+
label: "Customer Client Level",
|
|
1245
|
+
type: "string",
|
|
1246
|
+
required: false,
|
|
1247
|
+
comments: "The hierarchy depth level of the customer client to retrieve. Defaults to 1 (direct children only).",
|
|
1248
|
+
example: "1",
|
|
1249
|
+
placeholder: "Enter level",
|
|
1250
|
+
default: "1",
|
|
1251
|
+
clean: cleanString
|
|
1252
|
+
});
|
|
1253
|
+
//#endregion
|
|
1254
|
+
//#region src/inputs/campaign.ts
|
|
1255
|
+
const partialFailure = (0, _prismatic_io_spectral.input)({
|
|
1256
|
+
label: "Partial Failure",
|
|
1257
|
+
type: "boolean",
|
|
1258
|
+
required: true,
|
|
1259
|
+
clean: _prismatic_io_spectral.util.types.toBool,
|
|
1260
|
+
default: "false",
|
|
1261
|
+
comments: "When true, successful operations will be carried out and invalid operations will return errors. When false, all operations will be carried out in one transaction if and only if they are all valid. This should always be set to true. See [Partial failure documentation](https://developers.google.com/google-ads/api/docs/best-practices/partial-failures)."
|
|
1262
|
+
});
|
|
1263
|
+
const operations = (0, _prismatic_io_spectral.input)({
|
|
1264
|
+
label: "Operations",
|
|
1265
|
+
placeholder: "Enter campaign operations as a JSON array",
|
|
1266
|
+
type: "code",
|
|
1267
|
+
language: "json",
|
|
1268
|
+
required: true,
|
|
1269
|
+
default: JSON.stringify(campaignOperationsExampleInput, null, 2),
|
|
1270
|
+
comments: "The list of operations to perform on individual campaigns. See [Campaign operations documentation](https://developers.google.com/google-ads/api/reference/rpc/v25/CampaignOperation).",
|
|
1271
|
+
clean: _prismatic_io_spectral.util.types.toObject
|
|
1272
|
+
});
|
|
1273
|
+
const mutateCampaignInputs = {
|
|
1274
|
+
connection: connectionInput,
|
|
1275
|
+
customerId: customerIdInput,
|
|
1276
|
+
operations,
|
|
1277
|
+
partialFailure,
|
|
1278
|
+
managerCustomerId: {
|
|
1279
|
+
...managerCustomerIdInput,
|
|
1280
|
+
required: false,
|
|
1281
|
+
clean: toOptionalCustomerId
|
|
1282
|
+
},
|
|
1283
|
+
validateOnly
|
|
1284
|
+
};
|
|
1285
|
+
const mutateCampaignCriteriaInputs = {
|
|
1286
|
+
connection: connectionInput,
|
|
1287
|
+
customerId: customerIdInput,
|
|
1288
|
+
operations: {
|
|
1289
|
+
...operations,
|
|
1290
|
+
default: JSON.stringify(campaignCriteriaOperationsExampleInput, null, 2),
|
|
1291
|
+
placeholder: "Enter campaign criterion operations as a JSON array",
|
|
1292
|
+
comments: "The list of operations to create, update, or remove individual campaign criteria. See [Campaign criterion operations documentation](https://developers.google.com/google-ads/api/reference/rpc/v25/CampaignCriterionOperation)."
|
|
1293
|
+
},
|
|
1294
|
+
partialFailure,
|
|
1295
|
+
managerCustomerId: {
|
|
1296
|
+
...managerCustomerIdInput,
|
|
1297
|
+
required: false,
|
|
1298
|
+
clean: toOptionalCustomerId
|
|
1299
|
+
},
|
|
1300
|
+
validateOnly
|
|
1301
|
+
};
|
|
1302
|
+
//#endregion
|
|
1303
|
+
//#region src/inputs/conversions.ts
|
|
1304
|
+
const conversions = (0, _prismatic_io_spectral.input)({
|
|
1305
|
+
label: "Conversions",
|
|
1306
|
+
placeholder: "Enter conversions as a JSON array",
|
|
1307
|
+
type: "code",
|
|
1308
|
+
language: "json",
|
|
1309
|
+
required: true,
|
|
1310
|
+
example: JSON.stringify(clickConversionsExampleInput, null, 2),
|
|
1311
|
+
comments: "The click conversions to upload, each identifying the click (for example, by GCLID) and the conversion action, time, and value. See [Offline conversions documentation](https://developers.google.com/google-ads/api/docs/conversions/upload-offline).",
|
|
1312
|
+
clean: _prismatic_io_spectral.util.types.toObject
|
|
1313
|
+
});
|
|
1314
|
+
const ingestOfflineConversionsInputs = {
|
|
1315
|
+
connection: connectionInput,
|
|
1316
|
+
events: (0, _prismatic_io_spectral.input)({
|
|
1317
|
+
label: "Events",
|
|
1318
|
+
placeholder: "Enter conversion events as a JSON array",
|
|
1319
|
+
type: "code",
|
|
1320
|
+
language: "json",
|
|
1321
|
+
required: true,
|
|
1322
|
+
example: JSON.stringify([{
|
|
1323
|
+
eventTimestamp: "2026-05-15T12:30:00Z",
|
|
1324
|
+
transactionId: "ORDER-2026-00123",
|
|
1325
|
+
adIdentifiers: { gclid: "CjwKCAjw1234567890abcdefGHIJKLmnoPQRStuvwxyz" },
|
|
1326
|
+
conversionValue: 149.99,
|
|
1327
|
+
currency: "USD",
|
|
1328
|
+
consent: {
|
|
1329
|
+
adUserData: "CONSENT_GRANTED",
|
|
1330
|
+
adPersonalization: "CONSENT_GRANTED"
|
|
1331
|
+
}
|
|
1332
|
+
}], null, 2),
|
|
1333
|
+
comments: "The array of conversion events to ingest (max 2000 per request). See [Event resource](https://developers.google.com/data-manager/api/reference/rest/v1/events/ingest).",
|
|
1334
|
+
clean: _prismatic_io_spectral.util.types.toObject
|
|
1335
|
+
}),
|
|
1336
|
+
destinations: (0, _prismatic_io_spectral.input)({
|
|
1337
|
+
label: "Destinations",
|
|
1338
|
+
placeholder: "Enter destinations as a JSON array",
|
|
1339
|
+
type: "code",
|
|
1340
|
+
language: "json",
|
|
1341
|
+
required: true,
|
|
1342
|
+
example: JSON.stringify([{
|
|
1343
|
+
operatingAccount: {
|
|
1344
|
+
accountType: "GOOGLE_ADS",
|
|
1345
|
+
accountId: "1234567890"
|
|
1346
|
+
},
|
|
1347
|
+
productDestinationId: "987654321"
|
|
1348
|
+
}], null, 2),
|
|
1349
|
+
comments: "The array of destinations that describe where each event should be ingested. See [Destination reference](https://developers.google.com/data-manager/api/reference/rest/v1/Destination).",
|
|
1350
|
+
clean: _prismatic_io_spectral.util.types.toObject
|
|
1351
|
+
}),
|
|
1352
|
+
encoding: (0, _prismatic_io_spectral.input)({
|
|
1353
|
+
label: "Hash Encoding",
|
|
1354
|
+
type: "string",
|
|
1355
|
+
required: false,
|
|
1356
|
+
example: "HEX",
|
|
1357
|
+
model: [{
|
|
1358
|
+
label: "HEX",
|
|
1359
|
+
value: "HEX"
|
|
1360
|
+
}, {
|
|
1361
|
+
label: "BASE64",
|
|
1362
|
+
value: "BASE64"
|
|
1363
|
+
}],
|
|
1364
|
+
comments: "The encoding format to select for hashed user data fields (such as email or phone). Required when `userData` fields are included in events.",
|
|
1365
|
+
clean: cleanString
|
|
1366
|
+
}),
|
|
1367
|
+
validateOnly
|
|
1368
|
+
};
|
|
1369
|
+
const uploadCallConversionsInputs = {
|
|
1370
|
+
connection: connectionInput,
|
|
1371
|
+
customerId: customerIdInput,
|
|
1372
|
+
conversions: {
|
|
1373
|
+
...conversions,
|
|
1374
|
+
example: JSON.stringify([{
|
|
1375
|
+
callerId: "+16505550100",
|
|
1376
|
+
callStartDateTime: "2026-01-15 10:00:00-05:00",
|
|
1377
|
+
conversionAction: "customers/1234567890/conversionActions/987654321",
|
|
1378
|
+
conversionDateTime: "2026-01-15 10:30:00-05:00",
|
|
1379
|
+
conversionValue: 149.99,
|
|
1380
|
+
currencyCode: "USD",
|
|
1381
|
+
consent: {
|
|
1382
|
+
adUserData: "GRANTED",
|
|
1383
|
+
adPersonalization: "GRANTED"
|
|
1384
|
+
},
|
|
1385
|
+
customVariables: [{
|
|
1386
|
+
conversionCustomVariable: "customers/1234567890/conversionCustomVariables/111222333",
|
|
1387
|
+
value: "premium"
|
|
1388
|
+
}]
|
|
1389
|
+
}], null, 2),
|
|
1390
|
+
comments: "The call conversions to upload, each identifying the call by caller ID and call start time. See [Call conversions documentation](https://developers.google.com/google-ads/api/docs/conversions/upload-calls)."
|
|
1391
|
+
},
|
|
1392
|
+
managerCustomerId: {
|
|
1393
|
+
...managerCustomerIdInput,
|
|
1394
|
+
required: false,
|
|
1395
|
+
clean: toOptionalCustomerId
|
|
1396
|
+
},
|
|
1397
|
+
validateOnly
|
|
1398
|
+
};
|
|
1399
|
+
const uploadClickConversionsInputs = {
|
|
1400
|
+
connection: connectionInput,
|
|
1401
|
+
customerId: customerIdInput,
|
|
1402
|
+
conversions,
|
|
1403
|
+
managerCustomerId: {
|
|
1404
|
+
...managerCustomerIdInput,
|
|
1405
|
+
required: false,
|
|
1406
|
+
clean: toOptionalCustomerId
|
|
1407
|
+
},
|
|
1408
|
+
validateOnly
|
|
1409
|
+
};
|
|
1410
|
+
const confirmClientLinkInputs = {
|
|
1411
|
+
connection: connectionInput,
|
|
1412
|
+
managerCustomerId: managerCustomerIdInput,
|
|
1413
|
+
customerId: customerIdInput,
|
|
1414
|
+
managerLinkId: (0, _prismatic_io_spectral.input)({
|
|
1415
|
+
label: "Manager Link ID",
|
|
1416
|
+
placeholder: "Enter manager link ID",
|
|
1417
|
+
type: "string",
|
|
1418
|
+
required: true,
|
|
1419
|
+
example: "1234567890",
|
|
1420
|
+
comments: "The unique identifier of the manager link. See [Customer manager link documentation](https://developers.google.com/google-ads/api/reference/rpc/v25/CustomerManagerLink).",
|
|
1421
|
+
clean: _prismatic_io_spectral.util.types.toString
|
|
1422
|
+
})
|
|
1423
|
+
};
|
|
1424
|
+
const createClientLinkInputs = {
|
|
1425
|
+
connection: connectionInput,
|
|
1426
|
+
managerCustomerId: managerCustomerIdInput,
|
|
1427
|
+
customerId: customerIdInput
|
|
1428
|
+
};
|
|
1429
|
+
const customerEntityInputs = {
|
|
1430
|
+
connection: connectionInput,
|
|
1431
|
+
customerId: customerIdInput,
|
|
1432
|
+
pageToken: pageTokenInput,
|
|
1433
|
+
managerCustomerId: {
|
|
1434
|
+
...managerCustomerIdInput,
|
|
1435
|
+
required: false,
|
|
1436
|
+
clean: toOptionalCustomerId
|
|
1437
|
+
}
|
|
1438
|
+
};
|
|
1439
|
+
const listCustomersInputs = {
|
|
1440
|
+
connection: connectionInput,
|
|
1441
|
+
managerCustomerId: managerCustomerIdInput,
|
|
1442
|
+
fetchAll,
|
|
1443
|
+
pageToken: pageTokenInput
|
|
1444
|
+
};
|
|
1445
|
+
//#endregion
|
|
1446
|
+
//#region src/inputs/dataSources.ts
|
|
1447
|
+
const listAccessibleCustomersInputs = { connection: connectionInput };
|
|
1448
|
+
const listAccessibleSubAccountsInputs = {
|
|
1449
|
+
connection: connectionInput,
|
|
1450
|
+
customerId: {
|
|
1451
|
+
...customerIdInput,
|
|
1452
|
+
dataSource: void 0
|
|
1453
|
+
},
|
|
1454
|
+
customerClientLevel
|
|
1455
|
+
};
|
|
1456
|
+
//#endregion
|
|
1457
|
+
//#region src/inputs/localServices.ts
|
|
1458
|
+
const customerIds = (0, _prismatic_io_spectral.input)({
|
|
1459
|
+
label: "Customer IDs",
|
|
1460
|
+
placeholder: "Enter customer ID",
|
|
1461
|
+
type: "string",
|
|
1462
|
+
collection: "valuelist",
|
|
1463
|
+
required: false,
|
|
1464
|
+
clean: valueListInputClean,
|
|
1465
|
+
comments: "The Google Ads customer IDs to filter the Local Services report. Leave empty to include all accessible customers."
|
|
1466
|
+
});
|
|
1467
|
+
const pagination = (0, _prismatic_io_spectral.structuredObjectInput)({
|
|
1468
|
+
label: "Pagination",
|
|
1469
|
+
required: false,
|
|
1470
|
+
comments: "Page size and cursor controls for the result set.",
|
|
1471
|
+
inputs: {
|
|
1472
|
+
pageSizeInput: {
|
|
1473
|
+
...pageSizeInput,
|
|
1474
|
+
required: false,
|
|
1475
|
+
default: "1000"
|
|
1476
|
+
},
|
|
1477
|
+
pageTokenInput
|
|
1478
|
+
}
|
|
1479
|
+
});
|
|
1480
|
+
const accountReportsInputs = {
|
|
1481
|
+
connection: connectionInput,
|
|
1482
|
+
managerCustomerIdInput,
|
|
1483
|
+
pagination,
|
|
1484
|
+
customerIds,
|
|
1485
|
+
startDateInput,
|
|
1486
|
+
endDateInput
|
|
1487
|
+
};
|
|
1488
|
+
const detailedLeadReportsInputs = {
|
|
1489
|
+
connection: connectionInput,
|
|
1490
|
+
managerCustomerIdInput,
|
|
1491
|
+
pagination,
|
|
1492
|
+
customerIds,
|
|
1493
|
+
startDateInput,
|
|
1494
|
+
endDateInput
|
|
1495
|
+
};
|
|
1496
|
+
//#endregion
|
|
1497
|
+
//#region src/inputs/misc.ts
|
|
1498
|
+
const { debugRequest: _, ...baseRawRequestInputs } = _prismatic_io_spectral_http.inputs;
|
|
1499
|
+
const rawRequestInputs = {
|
|
1500
|
+
connection: connectionInput,
|
|
1501
|
+
...baseRawRequestInputs,
|
|
1502
|
+
url: {
|
|
1503
|
+
...baseRawRequestInputs.url,
|
|
1504
|
+
example: `/v25/customers:listAccessibleCustomers`,
|
|
1505
|
+
comments: `Input the path only (/v25/customers:listAccessibleCustomers), The base URL is already included (${GOOGLE_ADS_BASE_URL}). For example, to connect to ${GOOGLE_ADS_BASE_URL}/v25/customers:listAccessibleCustomers, only /v25/customers:listAccessibleCustomers is entered in this field. Note: When using the Raw Request action, the API version must be specified in the path (e.g., /v25/) to override the connection default.`,
|
|
1506
|
+
default: `/v25/`
|
|
1507
|
+
}
|
|
1508
|
+
};
|
|
1509
|
+
//#endregion
|
|
1510
|
+
//#region src/inputs/search.ts
|
|
1511
|
+
const query = (0, _prismatic_io_spectral.input)({
|
|
1512
|
+
label: "Query",
|
|
1513
|
+
placeholder: "Enter GAQL query",
|
|
1514
|
+
type: "string",
|
|
1515
|
+
required: true,
|
|
1516
|
+
clean: _prismatic_io_spectral.util.types.toString,
|
|
1517
|
+
comments: "Google Ads Query Language (GAQL) query string. See [GAQL documentation](https://developers.google.com/google-ads/api/docs/query/overview).",
|
|
1518
|
+
example: `SELECT
|
|
1519
|
+
campaign.id,
|
|
1520
|
+
campaign.status,
|
|
1521
|
+
campaign_budget.id,
|
|
1522
|
+
campaign_budget.period,
|
|
1523
|
+
campaign_budget.amount_micros,
|
|
1524
|
+
campaign_budget.type
|
|
1525
|
+
FROM campaign
|
|
1526
|
+
WHERE campaign.advertising_channel_type = 'LOCAL_SERVICES'`
|
|
1527
|
+
});
|
|
1528
|
+
const returnTotalResultsCount = (0, _prismatic_io_spectral.input)({
|
|
1529
|
+
label: "Return Total Results Count",
|
|
1530
|
+
type: "boolean",
|
|
1531
|
+
required: false,
|
|
1532
|
+
default: "false",
|
|
1533
|
+
clean: _prismatic_io_spectral.util.types.toBool,
|
|
1534
|
+
comments: "When true, the total number of results that match the query ignoring the LIMIT clause will be included in the response. Default is false."
|
|
1535
|
+
});
|
|
1536
|
+
const searchAdsLocalServicesInputs = {
|
|
1537
|
+
connection: connectionInput,
|
|
1538
|
+
customerId: customerIdInput,
|
|
1539
|
+
query,
|
|
1540
|
+
fetchAll,
|
|
1541
|
+
pageTokenInput,
|
|
1542
|
+
managerCustomerId: {
|
|
1543
|
+
...managerCustomerIdInput,
|
|
1544
|
+
required: false,
|
|
1545
|
+
clean: toOptionalCustomerId
|
|
1546
|
+
},
|
|
1547
|
+
returnTotalResultsCount
|
|
1548
|
+
};
|
|
1549
|
+
//#endregion
|
|
1550
|
+
//#region src/inputs/triggers.ts
|
|
1551
|
+
const lookBackDate = (0, _prismatic_io_spectral.input)({
|
|
1552
|
+
label: "Look-back Date",
|
|
1553
|
+
placeholder: "Enter look-back date (YYYY-MM-DD)",
|
|
1554
|
+
type: "string",
|
|
1555
|
+
required: false,
|
|
1556
|
+
comments: "The date the initial sync starts from, in YYYY-MM-DD format. Cannot be a future date. Leave empty to start from the first recurrence with no backfill. When set, the initial sync's change_event query starts from this date (clamped to Google's 30-day change_event window) instead of one hour before the first run.",
|
|
1557
|
+
example: "2026-01-01",
|
|
1558
|
+
clean: lookBackDateClean
|
|
1559
|
+
});
|
|
1560
|
+
const changeTypes = (0, _prismatic_io_spectral.input)({
|
|
1561
|
+
label: "Change Types to Monitor",
|
|
1562
|
+
type: "string",
|
|
1563
|
+
collection: "valuelist",
|
|
1564
|
+
required: false,
|
|
1565
|
+
model: CHANGE_TYPES_MODEL,
|
|
1566
|
+
comments: "Types of campaign changes to detect. Leave empty to detect all change types. The Campaign Settings option is not yet implemented and currently has no effect; no settings fields are evaluated.",
|
|
1567
|
+
placeholder: "Enter change types",
|
|
1568
|
+
clean: toStringList
|
|
1569
|
+
});
|
|
1570
|
+
const campaignChangesTriggerInputs = {
|
|
1571
|
+
connection: connectionInput,
|
|
1572
|
+
customerId: customerIdInput,
|
|
1573
|
+
lookBackDate,
|
|
1574
|
+
managerCustomerId: {
|
|
1575
|
+
...managerCustomerIdInput,
|
|
1576
|
+
required: false,
|
|
1577
|
+
clean: toOptionalCustomerId
|
|
1578
|
+
},
|
|
1579
|
+
changeTypes
|
|
1580
|
+
};
|
|
1581
|
+
const alertThreshold = (0, _prismatic_io_spectral.input)({
|
|
1582
|
+
label: "Alert Threshold (%)",
|
|
1583
|
+
type: "string",
|
|
1584
|
+
required: false,
|
|
1585
|
+
default: 80 .toString(),
|
|
1586
|
+
comments: "Budget spend percentage at which to trigger an alert.",
|
|
1587
|
+
example: 80 .toString(),
|
|
1588
|
+
placeholder: "Enter alert threshold percentage",
|
|
1589
|
+
clean: toOptionalInt
|
|
1590
|
+
});
|
|
1591
|
+
const includeSharedBudgets = (0, _prismatic_io_spectral.input)({
|
|
1592
|
+
label: "Include Shared Budgets",
|
|
1593
|
+
type: "boolean",
|
|
1594
|
+
required: false,
|
|
1595
|
+
default: "true",
|
|
1596
|
+
comments: "When true, shared budgets across multiple campaigns will be monitored.",
|
|
1597
|
+
clean: _prismatic_io_spectral.util.types.toBool
|
|
1598
|
+
});
|
|
1599
|
+
const budgetAlertTriggerInputs = {
|
|
1600
|
+
connection: connectionInput,
|
|
1601
|
+
customerId: {
|
|
1602
|
+
...customerIdInput,
|
|
1603
|
+
comments: "The unique identifier for the Google Ads client account. Accepts hyphenated or number forms. Use a client account; manager accounts return no metrics. See [Customer ID documentation](https://developers.google.com/google-ads/api/docs/concepts/call-structure#cid)."
|
|
1604
|
+
},
|
|
1605
|
+
managerCustomerId: {
|
|
1606
|
+
...managerCustomerIdInput,
|
|
1607
|
+
required: false,
|
|
1608
|
+
clean: toOptionalCustomerId
|
|
1609
|
+
},
|
|
1610
|
+
alertThreshold,
|
|
1611
|
+
includeSharedBudgets
|
|
1612
|
+
};
|
|
1613
|
+
const resourceTypes = (0, _prismatic_io_spectral.input)({
|
|
1614
|
+
label: "Resource Types",
|
|
1615
|
+
type: "string",
|
|
1616
|
+
collection: "valuelist",
|
|
1617
|
+
required: false,
|
|
1618
|
+
model: [
|
|
1619
|
+
{
|
|
1620
|
+
label: "Campaigns",
|
|
1621
|
+
value: "CAMPAIGN"
|
|
1622
|
+
},
|
|
1623
|
+
{
|
|
1624
|
+
label: "Ad Groups",
|
|
1625
|
+
value: "AD_GROUP"
|
|
1626
|
+
},
|
|
1627
|
+
{
|
|
1628
|
+
label: "Ads",
|
|
1629
|
+
value: "AD"
|
|
1630
|
+
},
|
|
1631
|
+
{
|
|
1632
|
+
label: "Keywords",
|
|
1633
|
+
value: "KEYWORD"
|
|
1634
|
+
}
|
|
1635
|
+
],
|
|
1636
|
+
comments: "Types of resources to track changes for. Leave empty to track all resource types.",
|
|
1637
|
+
placeholder: "Enter resource types",
|
|
1638
|
+
clean: toStringList
|
|
1639
|
+
});
|
|
1640
|
+
const includeUserInfo = (0, _prismatic_io_spectral.input)({
|
|
1641
|
+
label: "Include User Info",
|
|
1642
|
+
type: "boolean",
|
|
1643
|
+
required: false,
|
|
1644
|
+
default: "true",
|
|
1645
|
+
comments: "When true, user email and client type will be included in change events.",
|
|
1646
|
+
clean: _prismatic_io_spectral.util.types.toBool
|
|
1647
|
+
});
|
|
1648
|
+
const changeHistoryTriggerInputs = {
|
|
1649
|
+
connection: connectionInput,
|
|
1650
|
+
customerId: customerIdInput,
|
|
1651
|
+
lookBackDate,
|
|
1652
|
+
managerCustomerId: {
|
|
1653
|
+
...managerCustomerIdInput,
|
|
1654
|
+
required: false,
|
|
1655
|
+
clean: toOptionalCustomerId
|
|
1656
|
+
},
|
|
1657
|
+
resourceTypes,
|
|
1658
|
+
includeUserInfo
|
|
1659
|
+
};
|
|
1660
|
+
const inviteUserInputs = {
|
|
1661
|
+
connection: connectionInput,
|
|
1662
|
+
customerId: customerIdInput,
|
|
1663
|
+
emailAddress: (0, _prismatic_io_spectral.input)({
|
|
1664
|
+
label: "Email Address",
|
|
1665
|
+
placeholder: "Enter email address",
|
|
1666
|
+
type: "string",
|
|
1667
|
+
required: true,
|
|
1668
|
+
example: "john.doe@example.com",
|
|
1669
|
+
comments: "The email address of the user to invite to the customer account.",
|
|
1670
|
+
clean: _prismatic_io_spectral.util.types.toString
|
|
1671
|
+
}),
|
|
1672
|
+
accessRole: (0, _prismatic_io_spectral.input)({
|
|
1673
|
+
label: "Access Role",
|
|
1674
|
+
placeholder: "Enter access role",
|
|
1675
|
+
type: "string",
|
|
1676
|
+
required: true,
|
|
1677
|
+
comments: "The access role to grant to the user. See [Access roles documentation](https://developers.google.com/google-ads/api/reference/rpc/v25/AccessRoleEnum.AccessRole).",
|
|
1678
|
+
model: [
|
|
1679
|
+
"ADMIN",
|
|
1680
|
+
"STANDARD",
|
|
1681
|
+
"READ_ONLY",
|
|
1682
|
+
"EMAIL_ONLY"
|
|
1683
|
+
].map((role) => ({
|
|
1684
|
+
label: role,
|
|
1685
|
+
value: role
|
|
1686
|
+
})),
|
|
1687
|
+
clean: _prismatic_io_spectral.util.types.toString
|
|
1688
|
+
})
|
|
1689
|
+
};
|
|
1690
|
+
//#endregion
|
|
1691
|
+
//#region src/outputSchemas/common.ts
|
|
1692
|
+
const googleRpcStatusSchema = {
|
|
1693
|
+
type: "object",
|
|
1694
|
+
properties: {
|
|
1695
|
+
code: { type: "integer" },
|
|
1696
|
+
message: { type: "string" },
|
|
1697
|
+
details: {
|
|
1698
|
+
type: "array",
|
|
1699
|
+
items: { type: "object" }
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
};
|
|
1703
|
+
const mutateResourceNameResultSchema = {
|
|
1704
|
+
type: "object",
|
|
1705
|
+
properties: { resourceName: { type: "string" } }
|
|
1706
|
+
};
|
|
1707
|
+
const localServicesAggregatorInfoSchema = {
|
|
1708
|
+
type: "object",
|
|
1709
|
+
properties: { aggregatorProviderId: { type: "string" } }
|
|
1710
|
+
};
|
|
1711
|
+
const userIdentifierSchema = {
|
|
1712
|
+
type: "object",
|
|
1713
|
+
properties: {
|
|
1714
|
+
userIdentifierSource: {
|
|
1715
|
+
type: "string",
|
|
1716
|
+
enum: [
|
|
1717
|
+
"UNSPECIFIED",
|
|
1718
|
+
"UNKNOWN",
|
|
1719
|
+
"FIRST_PARTY",
|
|
1720
|
+
"THIRD_PARTY"
|
|
1721
|
+
]
|
|
1722
|
+
},
|
|
1723
|
+
hashedEmail: { type: "string" },
|
|
1724
|
+
hashedPhoneNumber: { type: "string" },
|
|
1725
|
+
mobileId: { type: "string" },
|
|
1726
|
+
thirdPartyUserId: { type: "string" },
|
|
1727
|
+
addressInfo: {
|
|
1728
|
+
type: "object",
|
|
1729
|
+
properties: {
|
|
1730
|
+
hashedFirstName: { type: "string" },
|
|
1731
|
+
hashedLastName: { type: "string" },
|
|
1732
|
+
city: { type: "string" },
|
|
1733
|
+
state: { type: "string" },
|
|
1734
|
+
countryCode: { type: "string" },
|
|
1735
|
+
postalCode: { type: "string" },
|
|
1736
|
+
hashedStreetAddress: { type: "string" }
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
};
|
|
1741
|
+
//#endregion
|
|
1742
|
+
//#region src/outputSchemas/campaign.ts
|
|
1743
|
+
const mutateCampaignOutputSchema = {
|
|
1744
|
+
type: "object",
|
|
1745
|
+
properties: {
|
|
1746
|
+
partialFailureError: googleRpcStatusSchema,
|
|
1747
|
+
results: {
|
|
1748
|
+
type: "array",
|
|
1749
|
+
items: mutateResourceNameResultSchema
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
};
|
|
1753
|
+
const mutateCampaignCriteriaOutputSchema = {
|
|
1754
|
+
type: "object",
|
|
1755
|
+
properties: {
|
|
1756
|
+
partialFailureError: googleRpcStatusSchema,
|
|
1757
|
+
results: {
|
|
1758
|
+
type: "array",
|
|
1759
|
+
items: mutateResourceNameResultSchema
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
};
|
|
1763
|
+
//#endregion
|
|
1764
|
+
//#region src/outputSchemas/conversions.ts
|
|
1765
|
+
const uploadCallConversionsOutputSchema = {
|
|
1766
|
+
type: "object",
|
|
1767
|
+
properties: {
|
|
1768
|
+
partialFailureError: googleRpcStatusSchema,
|
|
1769
|
+
results: {
|
|
1770
|
+
type: "array",
|
|
1771
|
+
items: {
|
|
1772
|
+
type: "object",
|
|
1773
|
+
properties: {
|
|
1774
|
+
callerId: { type: "string" },
|
|
1775
|
+
callStartDateTime: { type: "string" },
|
|
1776
|
+
conversionAction: { type: "string" },
|
|
1777
|
+
conversionDateTime: { type: "string" }
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
};
|
|
1783
|
+
const uploadClickConversionsOutputSchema = {
|
|
1784
|
+
type: "object",
|
|
1785
|
+
properties: {
|
|
1786
|
+
partialFailureError: googleRpcStatusSchema,
|
|
1787
|
+
jobId: { type: "string" },
|
|
1788
|
+
results: {
|
|
1789
|
+
type: "array",
|
|
1790
|
+
items: {
|
|
1791
|
+
type: "object",
|
|
1792
|
+
properties: {
|
|
1793
|
+
gclid: { type: "string" },
|
|
1794
|
+
gbraid: { type: "string" },
|
|
1795
|
+
wbraid: { type: "string" },
|
|
1796
|
+
conversionAction: { type: "string" },
|
|
1797
|
+
conversionDateTime: { type: "string" },
|
|
1798
|
+
userIdentifiers: {
|
|
1799
|
+
type: "array",
|
|
1800
|
+
items: userIdentifierSchema
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
};
|
|
1807
|
+
const ingestOfflineConversionsOutputSchema = {
|
|
1808
|
+
type: "object",
|
|
1809
|
+
properties: {
|
|
1810
|
+
requestId: { type: "string" },
|
|
1811
|
+
fieldWarnings: {
|
|
1812
|
+
type: "array",
|
|
1813
|
+
items: {
|
|
1814
|
+
type: "object",
|
|
1815
|
+
properties: {
|
|
1816
|
+
reason: {
|
|
1817
|
+
type: "string",
|
|
1818
|
+
enum: [
|
|
1819
|
+
"WARNING_REASON_UNSPECIFIED",
|
|
1820
|
+
"WARNING_REASON_CUSTOM_VARIABLE_NOT_ENABLED",
|
|
1821
|
+
"WARNING_REASON_CUSTOM_VARIABLE_NOT_PREDEFINED",
|
|
1822
|
+
"WARNING_REASON_CART_DATA_NOT_SUPPORTED_WITH_GBRAID_OR_WBRAID",
|
|
1823
|
+
"WARNING_REASON_CART_DATA_ITEM_MERCHANT_PRODUCT_ID_MISSING",
|
|
1824
|
+
"WARNING_REASON_CART_DATA_ITEM_UNIT_PRICE_MISSING",
|
|
1825
|
+
"WARNING_REASON_GENERIC",
|
|
1826
|
+
"WARNING_REASON_INVALID_CLIENT_ID",
|
|
1827
|
+
"WARNING_REASON_INVALID_SUBDIVISION_CODE",
|
|
1828
|
+
"WARNING_REASON_INVALID_REGION_CODE",
|
|
1829
|
+
"WARNING_REASON_INVALID_SUBCONTINENT_CODE",
|
|
1830
|
+
"WARNING_REASON_INVALID_CONTINENT_CODE",
|
|
1831
|
+
"WARNING_REASON_INVALID_DEVICE_CATEGORY",
|
|
1832
|
+
"WARNING_REASON_INVALID_DEVICE_SCREEN_RESOLUTION",
|
|
1833
|
+
"WARNING_REASON_INVALID_MERCHANT_ID"
|
|
1834
|
+
]
|
|
1835
|
+
},
|
|
1836
|
+
description: { type: "string" },
|
|
1837
|
+
field: { type: "string" }
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
};
|
|
1843
|
+
//#endregion
|
|
1844
|
+
//#region src/outputSchemas/customer.ts
|
|
1845
|
+
const listAccessibleCustomersOutputSchema = {
|
|
1846
|
+
type: "object",
|
|
1847
|
+
properties: { resourceNames: {
|
|
1848
|
+
type: "array",
|
|
1849
|
+
items: { type: "string" }
|
|
1850
|
+
} }
|
|
1851
|
+
};
|
|
1852
|
+
const listCustomersOutputSchema = {
|
|
1853
|
+
type: "object",
|
|
1854
|
+
properties: {
|
|
1855
|
+
results: {
|
|
1856
|
+
type: "array",
|
|
1857
|
+
items: {
|
|
1858
|
+
type: "object",
|
|
1859
|
+
properties: { customerClient: {
|
|
1860
|
+
type: "object",
|
|
1861
|
+
properties: {
|
|
1862
|
+
resourceName: { type: "string" },
|
|
1863
|
+
clientCustomer: { type: "string" },
|
|
1864
|
+
id: { type: "string" },
|
|
1865
|
+
level: { type: "string" },
|
|
1866
|
+
hidden: { type: "boolean" }
|
|
1867
|
+
},
|
|
1868
|
+
required: ["resourceName"]
|
|
1869
|
+
} },
|
|
1870
|
+
required: ["customerClient"]
|
|
1871
|
+
}
|
|
1872
|
+
},
|
|
1873
|
+
nextPageToken: { type: "string" },
|
|
1874
|
+
fieldMask: { type: "string" },
|
|
1875
|
+
queryResourceConsumption: { type: "string" }
|
|
1876
|
+
}
|
|
1877
|
+
};
|
|
1878
|
+
const getCustomerOutputSchema = {
|
|
1879
|
+
type: "object",
|
|
1880
|
+
properties: {
|
|
1881
|
+
results: {
|
|
1882
|
+
type: "array",
|
|
1883
|
+
items: {
|
|
1884
|
+
type: "object",
|
|
1885
|
+
properties: { customer: {
|
|
1886
|
+
type: "object",
|
|
1887
|
+
properties: {
|
|
1888
|
+
resourceName: { type: "string" },
|
|
1889
|
+
id: { type: "string" },
|
|
1890
|
+
descriptiveName: { type: "string" },
|
|
1891
|
+
status: {
|
|
1892
|
+
type: "string",
|
|
1893
|
+
enum: [
|
|
1894
|
+
"UNSPECIFIED",
|
|
1895
|
+
"UNKNOWN",
|
|
1896
|
+
"ENABLED",
|
|
1897
|
+
"CANCELED",
|
|
1898
|
+
"SUSPENDED",
|
|
1899
|
+
"CLOSED"
|
|
1900
|
+
]
|
|
1901
|
+
},
|
|
1902
|
+
testAccount: { type: "boolean" },
|
|
1903
|
+
manager: { type: "boolean" }
|
|
1904
|
+
},
|
|
1905
|
+
required: ["resourceName"]
|
|
1906
|
+
} },
|
|
1907
|
+
required: ["customer"]
|
|
1908
|
+
}
|
|
1909
|
+
},
|
|
1910
|
+
nextPageToken: { type: "string" },
|
|
1911
|
+
fieldMask: { type: "string" },
|
|
1912
|
+
queryResourceConsumption: { type: "string" }
|
|
1913
|
+
}
|
|
1914
|
+
};
|
|
1915
|
+
const getConversionActionOutputSchema = {
|
|
1916
|
+
type: "object",
|
|
1917
|
+
properties: {
|
|
1918
|
+
results: {
|
|
1919
|
+
type: "array",
|
|
1920
|
+
items: {
|
|
1921
|
+
type: "object",
|
|
1922
|
+
properties: { conversionAction: {
|
|
1923
|
+
type: "object",
|
|
1924
|
+
properties: {
|
|
1925
|
+
resourceName: { type: "string" },
|
|
1926
|
+
id: { type: "string" },
|
|
1927
|
+
name: { type: "string" }
|
|
1928
|
+
},
|
|
1929
|
+
required: ["resourceName"]
|
|
1930
|
+
} },
|
|
1931
|
+
required: ["conversionAction"]
|
|
1932
|
+
}
|
|
1933
|
+
},
|
|
1934
|
+
nextPageToken: { type: "string" },
|
|
1935
|
+
fieldMask: { type: "string" },
|
|
1936
|
+
queryResourceConsumption: { type: "string" }
|
|
1937
|
+
}
|
|
1938
|
+
};
|
|
1939
|
+
const createClientLinkOutputSchema = {
|
|
1940
|
+
type: "object",
|
|
1941
|
+
properties: {
|
|
1942
|
+
resourceName: { type: "string" },
|
|
1943
|
+
managerCustomerId: { type: "string" },
|
|
1944
|
+
clientCustomerId: { type: "string" },
|
|
1945
|
+
managerLinkId: { type: "string" }
|
|
1946
|
+
},
|
|
1947
|
+
required: [
|
|
1948
|
+
"resourceName",
|
|
1949
|
+
"managerCustomerId",
|
|
1950
|
+
"clientCustomerId"
|
|
1951
|
+
]
|
|
1952
|
+
};
|
|
1953
|
+
const confirmClientLinkOutputSchema = {
|
|
1954
|
+
type: "object",
|
|
1955
|
+
properties: { results: {
|
|
1956
|
+
type: "array",
|
|
1957
|
+
items: mutateResourceNameResultSchema
|
|
1958
|
+
} }
|
|
1959
|
+
};
|
|
1960
|
+
//#endregion
|
|
1961
|
+
//#region src/outputSchemas/localServices.ts
|
|
1962
|
+
const accountReportsOutputSchema = {
|
|
1963
|
+
type: "object",
|
|
1964
|
+
properties: {
|
|
1965
|
+
accountReports: {
|
|
1966
|
+
type: "array",
|
|
1967
|
+
items: {
|
|
1968
|
+
type: "object",
|
|
1969
|
+
properties: {
|
|
1970
|
+
accountId: { type: "string" },
|
|
1971
|
+
businessName: { type: "string" },
|
|
1972
|
+
averageWeeklyBudget: { type: "number" },
|
|
1973
|
+
averageFiveStarRating: { type: "number" },
|
|
1974
|
+
totalReview: { type: "integer" },
|
|
1975
|
+
impressionsLastTwoDays: { type: "string" },
|
|
1976
|
+
phoneLeadResponsiveness: { type: "number" },
|
|
1977
|
+
currentPeriodChargedLeads: { type: "string" },
|
|
1978
|
+
previousPeriodChargedLeads: { type: "string" },
|
|
1979
|
+
currentPeriodTotalCost: { type: "number" },
|
|
1980
|
+
previousPeriodTotalCost: { type: "number" },
|
|
1981
|
+
currencyCode: { type: "string" },
|
|
1982
|
+
currentPeriodPhoneCalls: { type: "string" },
|
|
1983
|
+
previousPeriodPhoneCalls: { type: "string" },
|
|
1984
|
+
currentPeriodConnectedPhoneCalls: { type: "string" },
|
|
1985
|
+
previousPeriodConnectedPhoneCalls: { type: "string" },
|
|
1986
|
+
aggregatorInfo: localServicesAggregatorInfoSchema
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
},
|
|
1990
|
+
nextPageToken: { type: "string" }
|
|
1991
|
+
}
|
|
1992
|
+
};
|
|
1993
|
+
const detailedLeadReportsOutputSchema = {
|
|
1994
|
+
type: "object",
|
|
1995
|
+
properties: {
|
|
1996
|
+
detailedLeadReports: {
|
|
1997
|
+
type: "array",
|
|
1998
|
+
items: {
|
|
1999
|
+
type: "object",
|
|
2000
|
+
properties: {
|
|
2001
|
+
leadId: { type: "string" },
|
|
2002
|
+
googleAdsLeadId: { type: "string" },
|
|
2003
|
+
accountId: { type: "string" },
|
|
2004
|
+
businessName: { type: "string" },
|
|
2005
|
+
leadCreationTimestamp: { type: "string" },
|
|
2006
|
+
leadType: {
|
|
2007
|
+
type: "string",
|
|
2008
|
+
enum: [
|
|
2009
|
+
"LEAD_TYPE_UNSPECIFIED",
|
|
2010
|
+
"MESSAGE",
|
|
2011
|
+
"PHONE_CALL",
|
|
2012
|
+
"BOOKING"
|
|
2013
|
+
]
|
|
2014
|
+
},
|
|
2015
|
+
leadCategory: { type: "string" },
|
|
2016
|
+
geo: { type: "string" },
|
|
2017
|
+
leadPrice: { type: "number" },
|
|
2018
|
+
chargeStatus: {
|
|
2019
|
+
type: "string",
|
|
2020
|
+
enum: [
|
|
2021
|
+
"CHARGE_STATUS_UNSPECIFIED",
|
|
2022
|
+
"CHARGED",
|
|
2023
|
+
"NOT_CHARGED"
|
|
2024
|
+
]
|
|
2025
|
+
},
|
|
2026
|
+
currencyCode: { type: "string" },
|
|
2027
|
+
disputeStatus: { type: "string" },
|
|
2028
|
+
messageLead: {
|
|
2029
|
+
type: "object",
|
|
2030
|
+
properties: {
|
|
2031
|
+
customerName: { type: "string" },
|
|
2032
|
+
jobType: { type: "string" },
|
|
2033
|
+
postalCode: { type: "string" },
|
|
2034
|
+
consumerPhoneNumber: { type: "string" }
|
|
2035
|
+
}
|
|
2036
|
+
},
|
|
2037
|
+
phoneLead: {
|
|
2038
|
+
type: "object",
|
|
2039
|
+
properties: {
|
|
2040
|
+
chargedCallTimestamp: { type: "string" },
|
|
2041
|
+
chargedConnectedCallDurationSeconds: { type: "string" },
|
|
2042
|
+
consumerPhoneNumber: { type: "string" }
|
|
2043
|
+
}
|
|
2044
|
+
},
|
|
2045
|
+
bookingLead: {
|
|
2046
|
+
type: "object",
|
|
2047
|
+
properties: {
|
|
2048
|
+
customerName: { type: "string" },
|
|
2049
|
+
consumerPhoneNumber: { type: "string" },
|
|
2050
|
+
jobType: { type: "string" },
|
|
2051
|
+
consumerEmail: { type: "string" },
|
|
2052
|
+
bookingAppointmentTimestamp: { type: "string" }
|
|
2053
|
+
}
|
|
2054
|
+
},
|
|
2055
|
+
aggregatorInfo: localServicesAggregatorInfoSchema,
|
|
2056
|
+
timezone: {
|
|
2057
|
+
type: "object",
|
|
2058
|
+
properties: {
|
|
2059
|
+
id: { type: "string" },
|
|
2060
|
+
version: { type: "string" }
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
},
|
|
2066
|
+
nextPageToken: { type: "string" }
|
|
2067
|
+
}
|
|
2068
|
+
};
|
|
2069
|
+
//#endregion
|
|
2070
|
+
//#region src/outputSchemas/search.ts
|
|
2071
|
+
const searchAdsOutputSchema = {
|
|
2072
|
+
type: "object",
|
|
2073
|
+
properties: {
|
|
2074
|
+
results: {
|
|
2075
|
+
type: "array",
|
|
2076
|
+
items: {
|
|
2077
|
+
type: "object",
|
|
2078
|
+
properties: { campaign: {
|
|
2079
|
+
type: "object",
|
|
2080
|
+
properties: {
|
|
2081
|
+
resourceName: { type: "string" },
|
|
2082
|
+
id: { type: "string" },
|
|
2083
|
+
name: { type: "string" },
|
|
2084
|
+
status: {
|
|
2085
|
+
type: "string",
|
|
2086
|
+
enum: [
|
|
2087
|
+
"UNSPECIFIED",
|
|
2088
|
+
"UNKNOWN",
|
|
2089
|
+
"ENABLED",
|
|
2090
|
+
"PAUSED",
|
|
2091
|
+
"REMOVED"
|
|
2092
|
+
]
|
|
2093
|
+
}
|
|
2094
|
+
},
|
|
2095
|
+
required: [],
|
|
2096
|
+
additionalProperties: true
|
|
2097
|
+
} },
|
|
2098
|
+
required: [],
|
|
2099
|
+
additionalProperties: true
|
|
2100
|
+
}
|
|
2101
|
+
},
|
|
2102
|
+
nextPageToken: { type: "string" },
|
|
2103
|
+
totalResultsCount: { type: "string" },
|
|
2104
|
+
fieldMask: { type: "string" },
|
|
2105
|
+
queryResourceConsumption: { type: "string" },
|
|
2106
|
+
metricAttributes: {
|
|
2107
|
+
type: "array",
|
|
2108
|
+
items: {
|
|
2109
|
+
type: "object",
|
|
2110
|
+
properties: {
|
|
2111
|
+
name: { type: "string" },
|
|
2112
|
+
attributes: {
|
|
2113
|
+
type: "array",
|
|
2114
|
+
items: {
|
|
2115
|
+
type: "object",
|
|
2116
|
+
properties: {
|
|
2117
|
+
key: { type: "string" },
|
|
2118
|
+
value: { type: "string" }
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
};
|
|
2127
|
+
//#endregion
|
|
2128
|
+
//#region src/outputSchemas/user.ts
|
|
2129
|
+
const inviteUserOutputSchema = {
|
|
2130
|
+
type: "object",
|
|
2131
|
+
properties: { result: {
|
|
2132
|
+
type: "object",
|
|
2133
|
+
properties: {
|
|
2134
|
+
resourceName: { type: "string" },
|
|
2135
|
+
multiPartyAuthReview: { type: "string" }
|
|
2136
|
+
},
|
|
2137
|
+
required: ["resourceName"]
|
|
2138
|
+
} },
|
|
2139
|
+
required: ["result"]
|
|
2140
|
+
};
|
|
2141
|
+
//#endregion
|
|
2142
|
+
//#region src/actions/campaign/index.ts
|
|
2143
|
+
var campaign_default = {
|
|
2144
|
+
mutateCampaign: (0, _prismatic_io_spectral.action)({
|
|
2145
|
+
display: {
|
|
2146
|
+
label: "Mutate Campaign",
|
|
2147
|
+
description: "Creates, updates, or removes campaigns as well as local services campaigns. Operation statuses are returned. When using API v22+, the containsEuPoliticalAdvertising field is required for campaign creation and location targeting changes."
|
|
2148
|
+
},
|
|
2149
|
+
inputs: mutateCampaignInputs,
|
|
2150
|
+
outputSchema: (0, _prismatic_io_spectral.outputSchema)({
|
|
2151
|
+
type: "actionOutput",
|
|
2152
|
+
schema: mutateCampaignOutputSchema
|
|
2153
|
+
}),
|
|
2154
|
+
performSafety: "notAllowed",
|
|
2155
|
+
perform: async (context, { connection, customerId, managerCustomerId, operations, partialFailure, validateOnly }) => {
|
|
2156
|
+
const { data } = await createClient({
|
|
2157
|
+
connection,
|
|
2158
|
+
debugEnabled: context.debug.enabled,
|
|
2159
|
+
logger: context.logger,
|
|
2160
|
+
loginCustomerId: managerCustomerId
|
|
2161
|
+
}).post(`/customers/${customerId}/campaigns:mutate`, {
|
|
2162
|
+
operations,
|
|
2163
|
+
partialFailure,
|
|
2164
|
+
validateOnly
|
|
2165
|
+
});
|
|
2166
|
+
return { data };
|
|
2167
|
+
},
|
|
2168
|
+
examplePerform: async () => mutateCampaignExamplePayload,
|
|
2169
|
+
examplePayload: mutateCampaignExamplePayload
|
|
2170
|
+
}),
|
|
2171
|
+
mutateCampaignCriteria: (0, _prismatic_io_spectral.action)({
|
|
2172
|
+
display: {
|
|
2173
|
+
label: "Mutate Campaign Criteria",
|
|
2174
|
+
description: "Creates, updates, or removes campaign criteria as well as local services campaign criterion. Operation statuses are returned. When using API v22+, the containsEuPoliticalAdvertising field must be set on the parent campaign before modifying location or proximity targeting criteria."
|
|
2175
|
+
},
|
|
2176
|
+
inputs: mutateCampaignCriteriaInputs,
|
|
2177
|
+
outputSchema: (0, _prismatic_io_spectral.outputSchema)({
|
|
2178
|
+
type: "actionOutput",
|
|
2179
|
+
schema: mutateCampaignCriteriaOutputSchema
|
|
2180
|
+
}),
|
|
2181
|
+
performSafety: "notAllowed",
|
|
2182
|
+
perform: async (context, { connection, customerId, managerCustomerId, operations, partialFailure, validateOnly }) => {
|
|
2183
|
+
const { data } = await createClient({
|
|
2184
|
+
connection,
|
|
2185
|
+
debugEnabled: context.debug.enabled,
|
|
2186
|
+
logger: context.logger,
|
|
2187
|
+
loginCustomerId: managerCustomerId
|
|
2188
|
+
}).post(`/customers/${customerId}/campaignCriteria:mutate`, {
|
|
2189
|
+
operations,
|
|
2190
|
+
partialFailure,
|
|
2191
|
+
validateOnly
|
|
2192
|
+
});
|
|
2193
|
+
return { data };
|
|
2194
|
+
},
|
|
2195
|
+
examplePerform: async () => mutateCampaignCriteriaExamplePayload,
|
|
2196
|
+
examplePayload: mutateCampaignCriteriaExamplePayload
|
|
2197
|
+
})
|
|
2198
|
+
};
|
|
2199
|
+
//#endregion
|
|
2200
|
+
//#region src/actions/conversions/index.ts
|
|
2201
|
+
var conversions_default = {
|
|
2202
|
+
ingestOfflineConversions: (0, _prismatic_io_spectral.action)({
|
|
2203
|
+
display: {
|
|
2204
|
+
label: "Ingest Offline Conversions",
|
|
2205
|
+
description: "Import offline conversion events into Google Ads using the Data Manager API."
|
|
2206
|
+
},
|
|
2207
|
+
inputs: ingestOfflineConversionsInputs,
|
|
2208
|
+
outputSchema: (0, _prismatic_io_spectral.outputSchema)({
|
|
2209
|
+
type: "actionOutput",
|
|
2210
|
+
schema: ingestOfflineConversionsOutputSchema
|
|
2211
|
+
}),
|
|
2212
|
+
performSafety: "notAllowed",
|
|
2213
|
+
perform: async (context, { connection, events, destinations, encoding, validateOnly }) => {
|
|
2214
|
+
const client = createDataManagerClient({
|
|
2215
|
+
connection,
|
|
2216
|
+
debugEnabled: context.debug.enabled,
|
|
2217
|
+
logger: context.logger
|
|
2218
|
+
});
|
|
2219
|
+
const body = {
|
|
2220
|
+
destinations,
|
|
2221
|
+
events,
|
|
2222
|
+
validateOnly,
|
|
2223
|
+
encoding
|
|
2224
|
+
};
|
|
2225
|
+
const { data } = await client.post("/events:ingest", body);
|
|
2226
|
+
return { data };
|
|
2227
|
+
},
|
|
2228
|
+
examplePerform: async () => ingestOfflineConversionsExamplePayload,
|
|
2229
|
+
examplePayload: ingestOfflineConversionsExamplePayload
|
|
2230
|
+
}),
|
|
2231
|
+
uploadCallConversions: (0, _prismatic_io_spectral.action)({
|
|
2232
|
+
display: {
|
|
2233
|
+
label: "Upload Call Conversions",
|
|
2234
|
+
description: "Upload offline call conversions into Google Ads in order to track ads that led to sales."
|
|
2235
|
+
},
|
|
2236
|
+
inputs: uploadCallConversionsInputs,
|
|
2237
|
+
outputSchema: (0, _prismatic_io_spectral.outputSchema)({
|
|
2238
|
+
type: "actionOutput",
|
|
2239
|
+
schema: uploadCallConversionsOutputSchema
|
|
2240
|
+
}),
|
|
2241
|
+
performSafety: "notAllowed",
|
|
2242
|
+
perform: async (context, { connection, customerId, managerCustomerId, conversions, validateOnly }) => {
|
|
2243
|
+
const { data } = await createClient({
|
|
2244
|
+
connection,
|
|
2245
|
+
debugEnabled: context.debug.enabled,
|
|
2246
|
+
logger: context.logger,
|
|
2247
|
+
loginCustomerId: managerCustomerId
|
|
2248
|
+
}).post(`/customers/${customerId}:uploadCallConversions`, {
|
|
2249
|
+
conversions,
|
|
2250
|
+
partialFailure: true,
|
|
2251
|
+
validateOnly
|
|
2252
|
+
});
|
|
2253
|
+
return { data };
|
|
2254
|
+
},
|
|
2255
|
+
examplePerform: async () => uploadCallConversionsExamplePayload,
|
|
2256
|
+
examplePayload: uploadCallConversionsExamplePayload
|
|
2257
|
+
}),
|
|
2258
|
+
uploadClickConversions: (0, _prismatic_io_spectral.action)({
|
|
2259
|
+
display: {
|
|
2260
|
+
label: "Upload Click Conversions",
|
|
2261
|
+
description: "Upload offline click conversions into Google Ads in order to track ads that led to sales. Developer tokens that have not sent an offline conversion upload request between December 17, 2025 and June 15, 2026 will lose access to this endpoint; new integrations should use Ingest Offline Conversions (Data Manager API) instead."
|
|
2262
|
+
},
|
|
2263
|
+
inputs: uploadClickConversionsInputs,
|
|
2264
|
+
outputSchema: (0, _prismatic_io_spectral.outputSchema)({
|
|
2265
|
+
type: "actionOutput",
|
|
2266
|
+
schema: uploadClickConversionsOutputSchema
|
|
2267
|
+
}),
|
|
2268
|
+
performSafety: "notAllowed",
|
|
2269
|
+
perform: async (context, { connection, customerId, managerCustomerId, conversions, validateOnly }) => {
|
|
2270
|
+
const { data } = await createClient({
|
|
2271
|
+
connection,
|
|
2272
|
+
debugEnabled: context.debug.enabled,
|
|
2273
|
+
logger: context.logger,
|
|
2274
|
+
loginCustomerId: managerCustomerId
|
|
2275
|
+
}).post(`/customers/${customerId}:uploadClickConversions`, {
|
|
2276
|
+
conversions,
|
|
2277
|
+
partialFailure: true,
|
|
2278
|
+
validateOnly
|
|
2279
|
+
});
|
|
2280
|
+
return { data };
|
|
2281
|
+
},
|
|
2282
|
+
examplePerform: async () => uploadClickConversionsExamplePayload,
|
|
2283
|
+
examplePayload: uploadClickConversionsExamplePayload
|
|
2284
|
+
})
|
|
2285
|
+
};
|
|
2286
|
+
//#endregion
|
|
2287
|
+
//#region src/actions/customer/confirmClientLink.ts
|
|
2288
|
+
const confirmClientLink = (0, _prismatic_io_spectral.action)({
|
|
2289
|
+
display: {
|
|
2290
|
+
label: "Confirm Client Link",
|
|
2291
|
+
description: "Confirms a pending customer client link."
|
|
2292
|
+
},
|
|
2293
|
+
inputs: confirmClientLinkInputs,
|
|
2294
|
+
outputSchema: (0, _prismatic_io_spectral.outputSchema)({
|
|
2295
|
+
type: "actionOutput",
|
|
2296
|
+
schema: confirmClientLinkOutputSchema
|
|
2297
|
+
}),
|
|
2298
|
+
performSafety: "notAllowed",
|
|
2299
|
+
perform: async (context, { connection, managerCustomerId, customerId, managerLinkId }) => {
|
|
2300
|
+
const client = createClient({
|
|
2301
|
+
connection,
|
|
2302
|
+
debugEnabled: context.debug.enabled,
|
|
2303
|
+
logger: context.logger
|
|
2304
|
+
});
|
|
2305
|
+
const linkResourceName = `customers/${customerId}/customerManagerLinks/${managerCustomerId}~${managerLinkId}`;
|
|
2306
|
+
const { data } = await client.post(`customers/${customerId}/customerManagerLinks:mutate`, { operations: [{
|
|
2307
|
+
updateMask: "status",
|
|
2308
|
+
update: {
|
|
2309
|
+
status: "ACTIVE",
|
|
2310
|
+
resourceName: linkResourceName
|
|
2311
|
+
}
|
|
2312
|
+
}] });
|
|
2313
|
+
return { data };
|
|
2314
|
+
},
|
|
2315
|
+
examplePerform: async () => confirmClientLinkExamplePayload,
|
|
2316
|
+
examplePayload: confirmClientLinkExamplePayload
|
|
2317
|
+
});
|
|
2318
|
+
//#endregion
|
|
2319
|
+
//#region src/actions/customer/createClientLink.ts
|
|
2320
|
+
const createClientLink = (0, _prismatic_io_spectral.action)({
|
|
2321
|
+
display: {
|
|
2322
|
+
label: "Create Client Link",
|
|
2323
|
+
description: "Create an invitation to link a client account to a manager account."
|
|
2324
|
+
},
|
|
2325
|
+
inputs: createClientLinkInputs,
|
|
2326
|
+
outputSchema: (0, _prismatic_io_spectral.outputSchema)({
|
|
2327
|
+
type: "actionOutput",
|
|
2328
|
+
schema: createClientLinkOutputSchema
|
|
2329
|
+
}),
|
|
2330
|
+
performSafety: "notAllowed",
|
|
2331
|
+
perform: async (context, { connection, managerCustomerId, customerId }) => {
|
|
2332
|
+
const client = createClient({
|
|
2333
|
+
connection,
|
|
2334
|
+
debugEnabled: context.debug.enabled,
|
|
2335
|
+
logger: context.logger
|
|
2336
|
+
});
|
|
2337
|
+
const { data } = await client.post(`customers/${managerCustomerId}/customerClientLinks:mutate`, { operation: { create: {
|
|
2338
|
+
status: "PENDING",
|
|
2339
|
+
clientCustomer: `customers/${customerId}`
|
|
2340
|
+
} } });
|
|
2341
|
+
const resourceName = data.result.resourceName;
|
|
2342
|
+
const query = `SELECT customer_client_link.manager_link_id FROM customer_client_link WHERE customer_client_link.resource_name = "${resourceName}"`;
|
|
2343
|
+
const { managerLinkId } = (await client.post(googleAdsSearchPath(managerCustomerId), { query })).data.results?.[0]?.customerClientLink || { managerLinkId: void 0 };
|
|
2344
|
+
return { data: {
|
|
2345
|
+
resourceName,
|
|
2346
|
+
managerCustomerId,
|
|
2347
|
+
clientCustomerId: customerId,
|
|
2348
|
+
managerLinkId
|
|
2349
|
+
} };
|
|
2350
|
+
},
|
|
2351
|
+
examplePerform: async () => createClientLinkExamplePayload,
|
|
2352
|
+
examplePayload: createClientLinkExamplePayload
|
|
2353
|
+
});
|
|
2354
|
+
//#endregion
|
|
2355
|
+
//#region src/actions/customer/customerEntity.ts
|
|
2356
|
+
const customerEntities = {
|
|
2357
|
+
customer: [
|
|
2358
|
+
"id",
|
|
2359
|
+
"descriptive_name",
|
|
2360
|
+
"status",
|
|
2361
|
+
"test_account",
|
|
2362
|
+
"manager"
|
|
2363
|
+
],
|
|
2364
|
+
conversion_action: [
|
|
2365
|
+
"id",
|
|
2366
|
+
"name",
|
|
2367
|
+
"resource_name"
|
|
2368
|
+
]
|
|
2369
|
+
};
|
|
2370
|
+
const entityExamplePayloads = {
|
|
2371
|
+
customer: getCustomerExamplePayload,
|
|
2372
|
+
conversion_action: getConversionActionExamplePayload
|
|
2373
|
+
};
|
|
2374
|
+
const entityOutputSchemas = {
|
|
2375
|
+
customer: getCustomerOutputSchema,
|
|
2376
|
+
conversion_action: getConversionActionOutputSchema
|
|
2377
|
+
};
|
|
2378
|
+
const entityPerformSafety = {
|
|
2379
|
+
customer: "safe",
|
|
2380
|
+
conversion_action: "notAllowed"
|
|
2381
|
+
};
|
|
2382
|
+
const entityExamplePerforms = { conversion_action: async () => getConversionActionExamplePayload };
|
|
2383
|
+
const customerEntityActions = Object.entries(customerEntities).reduce((result, [entityName, fieldsList]) => {
|
|
2384
|
+
const key = (0, lodash.camelCase)(`get ${entityName}`);
|
|
2385
|
+
const name = (0, lodash.startCase)(entityName);
|
|
2386
|
+
const querySelect = fieldsList.map((field) => `${entityName}.${field}`).join(", ");
|
|
2387
|
+
result[key] = (0, _prismatic_io_spectral.action)({
|
|
2388
|
+
display: {
|
|
2389
|
+
label: `Get ${name}`,
|
|
2390
|
+
description: `Retrieve ${name} data for a customer account.`
|
|
2391
|
+
},
|
|
2392
|
+
inputs: customerEntityInputs,
|
|
2393
|
+
outputSchema: (0, _prismatic_io_spectral.outputSchema)({
|
|
2394
|
+
type: "actionOutput",
|
|
2395
|
+
schema: entityOutputSchemas[entityName]
|
|
2396
|
+
}),
|
|
2397
|
+
performSafety: entityPerformSafety[entityName],
|
|
2398
|
+
perform: async (context, params) => {
|
|
2399
|
+
const client = createClient({
|
|
2400
|
+
connection: params.connection,
|
|
2401
|
+
debugEnabled: context.debug.enabled,
|
|
2402
|
+
logger: context.logger,
|
|
2403
|
+
loginCustomerId: params.managerCustomerId
|
|
2404
|
+
});
|
|
2405
|
+
const id = cleanCustomerId(params.customerId);
|
|
2406
|
+
const { data } = await client.post(googleAdsSearchPath(id), {
|
|
2407
|
+
pageToken: params.pageToken || void 0,
|
|
2408
|
+
query: `SELECT ${querySelect} FROM ${entityName}`
|
|
2409
|
+
});
|
|
2410
|
+
return { data };
|
|
2411
|
+
},
|
|
2412
|
+
examplePerform: entityExamplePerforms[entityName],
|
|
2413
|
+
examplePayload: entityExamplePayloads[entityName]
|
|
2414
|
+
});
|
|
2415
|
+
return result;
|
|
2416
|
+
}, {});
|
|
2417
|
+
//#endregion
|
|
2418
|
+
//#region src/actions/customer/index.ts
|
|
2419
|
+
var customer_default = {
|
|
2420
|
+
listAccessibleCustomers: (0, _prismatic_io_spectral.action)({
|
|
2421
|
+
display: {
|
|
2422
|
+
label: "List Accessible Customers",
|
|
2423
|
+
description: "Gets a list of customers accessible to the authenticated user."
|
|
2424
|
+
},
|
|
2425
|
+
inputs: listAccessibleCustomersInputs,
|
|
2426
|
+
outputSchema: (0, _prismatic_io_spectral.outputSchema)({
|
|
2427
|
+
type: "actionOutput",
|
|
2428
|
+
schema: listAccessibleCustomersOutputSchema
|
|
2429
|
+
}),
|
|
2430
|
+
performSafety: "safe",
|
|
2431
|
+
perform: async (context, { connection }) => {
|
|
2432
|
+
const { data } = await createClient({
|
|
2433
|
+
connection,
|
|
2434
|
+
debugEnabled: context.debug.enabled,
|
|
2435
|
+
logger: context.logger
|
|
2436
|
+
}).get("/customers:listAccessibleCustomers");
|
|
2437
|
+
return { data };
|
|
2438
|
+
},
|
|
2439
|
+
examplePayload: listAccessibleCustomersExamplePayload
|
|
2440
|
+
}),
|
|
2441
|
+
listCustomers: (0, _prismatic_io_spectral.action)({
|
|
2442
|
+
display: {
|
|
2443
|
+
label: "List Customers by Manager",
|
|
2444
|
+
description: "Lists all customers under a manager account."
|
|
2445
|
+
},
|
|
2446
|
+
inputs: listCustomersInputs,
|
|
2447
|
+
outputSchema: (0, _prismatic_io_spectral.outputSchema)({
|
|
2448
|
+
type: "actionOutput",
|
|
2449
|
+
schema: listCustomersOutputSchema
|
|
2450
|
+
}),
|
|
2451
|
+
performSafety: "notAllowed",
|
|
2452
|
+
perform: async (context, { connection, managerCustomerId, pageToken, fetchAll }) => {
|
|
2453
|
+
return { data: await searchGoogleAds(createClient({
|
|
2454
|
+
connection,
|
|
2455
|
+
debugEnabled: context.debug.enabled,
|
|
2456
|
+
logger: context.logger
|
|
2457
|
+
}), {
|
|
2458
|
+
customerId: managerCustomerId,
|
|
2459
|
+
params: {
|
|
2460
|
+
pageToken: pageToken || void 0,
|
|
2461
|
+
query: `
|
|
2462
|
+
SELECT customer_client.resource_name,
|
|
2463
|
+
customer_client.client_customer,
|
|
2464
|
+
customer_client.id,
|
|
2465
|
+
customer_client.level,
|
|
2466
|
+
customer_client.hidden,
|
|
2467
|
+
customer_client.level
|
|
2468
|
+
FROM customer_client
|
|
2469
|
+
`
|
|
2470
|
+
},
|
|
2471
|
+
fetchAll
|
|
2472
|
+
}) };
|
|
2473
|
+
},
|
|
2474
|
+
examplePerform: async () => listCustomersExamplePayload,
|
|
2475
|
+
examplePayload: listCustomersExamplePayload
|
|
2476
|
+
}),
|
|
2477
|
+
...customerEntityActions,
|
|
2478
|
+
createClientLink,
|
|
2479
|
+
confirmClientLink
|
|
2480
|
+
};
|
|
2481
|
+
//#endregion
|
|
2482
|
+
//#region src/actions/localServices/index.ts
|
|
2483
|
+
var localServices_default = {
|
|
2484
|
+
accountReports: (0, _prismatic_io_spectral.action)({
|
|
2485
|
+
display: {
|
|
2486
|
+
label: "Get Account Reports",
|
|
2487
|
+
description: "Retrieves account reports showing performance and metrics for Local Services accounts linked to a Manager account."
|
|
2488
|
+
},
|
|
2489
|
+
inputs: accountReportsInputs,
|
|
2490
|
+
outputSchema: (0, _prismatic_io_spectral.outputSchema)({
|
|
2491
|
+
type: "actionOutput",
|
|
2492
|
+
schema: accountReportsOutputSchema
|
|
2493
|
+
}),
|
|
2494
|
+
performSafety: "notAllowed",
|
|
2495
|
+
perform: async (context, { connection, customerIds, managerCustomerIdInput, pagination, endDateInput, startDateInput }) => {
|
|
2496
|
+
const client = createLocalServicesClient({
|
|
2497
|
+
connection,
|
|
2498
|
+
debugEnabled: context.debug.enabled
|
|
2499
|
+
});
|
|
2500
|
+
const startDate = startDateInput ? new Date(startDateInput) : void 0;
|
|
2501
|
+
const endDate = endDateInput ? new Date(endDateInput) : void 0;
|
|
2502
|
+
const query = `manager_customer_id:${managerCustomerIdInput}${customerIds && customerIds !== "" ? `;${customerIds}` : ""}`;
|
|
2503
|
+
const { data } = await client.get("/accountReports:search", { params: {
|
|
2504
|
+
query,
|
|
2505
|
+
pageSize: pagination.pageSizeInput || void 0,
|
|
2506
|
+
pageToken: pagination.pageTokenInput || void 0,
|
|
2507
|
+
...startDate && {
|
|
2508
|
+
"startDate.day": startDate.getDate(),
|
|
2509
|
+
"startDate.month": startDate.getMonth() + 1,
|
|
2510
|
+
"startDate.year": startDate.getFullYear()
|
|
2511
|
+
},
|
|
2512
|
+
...endDate && {
|
|
2513
|
+
"endDate.day": endDate.getDate(),
|
|
2514
|
+
"endDate.month": endDate.getMonth() + 1,
|
|
2515
|
+
"endDate.year": endDate.getFullYear()
|
|
2516
|
+
}
|
|
2517
|
+
} });
|
|
2518
|
+
return { data };
|
|
2519
|
+
},
|
|
2520
|
+
examplePerform: async () => accountReportsExamplePayload,
|
|
2521
|
+
examplePayload: accountReportsExamplePayload
|
|
2522
|
+
}),
|
|
2523
|
+
detailedLeadReports: (0, _prismatic_io_spectral.action)({
|
|
2524
|
+
display: {
|
|
2525
|
+
label: "Get Detailed Lead Reports",
|
|
2526
|
+
description: "Retrieves detailed lead reports providing an in-depth view of leads for Local Services accounts linked to a Manager account."
|
|
2527
|
+
},
|
|
2528
|
+
inputs: detailedLeadReportsInputs,
|
|
2529
|
+
outputSchema: (0, _prismatic_io_spectral.outputSchema)({
|
|
2530
|
+
type: "actionOutput",
|
|
2531
|
+
schema: detailedLeadReportsOutputSchema
|
|
2532
|
+
}),
|
|
2533
|
+
performSafety: "notAllowed",
|
|
2534
|
+
perform: async (context, { connection, customerIds, managerCustomerIdInput, pagination, endDateInput, startDateInput }) => {
|
|
2535
|
+
const client = createLocalServicesClient({
|
|
2536
|
+
connection,
|
|
2537
|
+
debugEnabled: context.debug.enabled
|
|
2538
|
+
});
|
|
2539
|
+
const startDate = startDateInput ? new Date(startDateInput) : void 0;
|
|
2540
|
+
const endDate = endDateInput ? new Date(endDateInput) : void 0;
|
|
2541
|
+
const query = `manager_customer_id:${managerCustomerIdInput}${customerIds && customerIds !== "" ? `;${customerIds}` : ""}`;
|
|
2542
|
+
const { data } = await client.get("/detailedLeadReports:search", { params: {
|
|
2543
|
+
query,
|
|
2544
|
+
pageSize: pagination.pageSizeInput || void 0,
|
|
2545
|
+
pageToken: pagination.pageTokenInput || void 0,
|
|
2546
|
+
...startDate && {
|
|
2547
|
+
"startDate.day": startDate.getDate(),
|
|
2548
|
+
"startDate.month": startDate.getMonth() + 1,
|
|
2549
|
+
"startDate.year": startDate.getFullYear()
|
|
2550
|
+
},
|
|
2551
|
+
...endDate && {
|
|
2552
|
+
"endDate.day": endDate.getDate(),
|
|
2553
|
+
"endDate.month": endDate.getMonth() + 1,
|
|
2554
|
+
"endDate.year": endDate.getFullYear()
|
|
2555
|
+
}
|
|
2556
|
+
} });
|
|
2557
|
+
return { data };
|
|
2558
|
+
},
|
|
2559
|
+
examplePerform: async () => detailedLeadReportsExamplePayload,
|
|
2560
|
+
examplePayload: detailedLeadReportsExamplePayload
|
|
2561
|
+
})
|
|
2562
|
+
};
|
|
2563
|
+
//#endregion
|
|
2564
|
+
//#region src/actions/misc/index.ts
|
|
2565
|
+
var misc_default = { rawRequest: (0, _prismatic_io_spectral.action)({
|
|
2566
|
+
display: {
|
|
2567
|
+
label: "Raw Request",
|
|
2568
|
+
description: "Sends a raw HTTP request to the Google Ads API."
|
|
2569
|
+
},
|
|
2570
|
+
inputs: rawRequestInputs,
|
|
2571
|
+
performSafety: "notAllowed",
|
|
2572
|
+
perform: async (context, { connection, ...rawRequestInputs }) => {
|
|
2573
|
+
const { accessToken } = validateConnection(connection);
|
|
2574
|
+
const authorizationHeaders = { Authorization: `Bearer ${accessToken}` };
|
|
2575
|
+
const { data } = await (0, _prismatic_io_spectral_http.sendRawRequest)(GOOGLE_ADS_BASE_URL, {
|
|
2576
|
+
...rawRequestInputs,
|
|
2577
|
+
debugRequest: context.debug.enabled
|
|
2578
|
+
}, authorizationHeaders);
|
|
2579
|
+
return { data };
|
|
2580
|
+
},
|
|
2581
|
+
examplePayload: rawRequestExamplePayload
|
|
2582
|
+
}) };
|
|
2583
|
+
//#endregion
|
|
2584
|
+
//#region src/actions/search/index.ts
|
|
2585
|
+
var search_default = { searchAdsLocalServices: (0, _prismatic_io_spectral.action)({
|
|
2586
|
+
display: {
|
|
2587
|
+
label: "Search Ads",
|
|
2588
|
+
description: "Returns rows matching a GAQL query against Google Ads resources."
|
|
2589
|
+
},
|
|
2590
|
+
inputs: searchAdsLocalServicesInputs,
|
|
2591
|
+
outputSchema: (0, _prismatic_io_spectral.outputSchema)({
|
|
2592
|
+
type: "actionOutput",
|
|
2593
|
+
schema: searchAdsOutputSchema
|
|
2594
|
+
}),
|
|
2595
|
+
performSafety: "notAllowed",
|
|
2596
|
+
perform: async (context, { connection, customerId, managerCustomerId, pageTokenInput, query, returnTotalResultsCount, fetchAll }) => {
|
|
2597
|
+
return { data: await searchGoogleAds(createClient({
|
|
2598
|
+
connection,
|
|
2599
|
+
debugEnabled: context.debug.enabled,
|
|
2600
|
+
logger: context.logger,
|
|
2601
|
+
loginCustomerId: managerCustomerId
|
|
2602
|
+
}), {
|
|
2603
|
+
customerId,
|
|
2604
|
+
fetchAll,
|
|
2605
|
+
params: {
|
|
2606
|
+
pageToken: pageTokenInput || void 0,
|
|
2607
|
+
query: query || void 0,
|
|
2608
|
+
searchSettings: returnTotalResultsCount ? { returnTotalResultsCount: true } : void 0
|
|
2609
|
+
}
|
|
2610
|
+
}) };
|
|
2611
|
+
},
|
|
2612
|
+
examplePerform: async () => searchAdsLocalServicesExamplePayload,
|
|
2613
|
+
examplePayload: searchAdsLocalServicesExamplePayload
|
|
2614
|
+
}) };
|
|
2615
|
+
//#endregion
|
|
2616
|
+
//#region src/actions/user/index.ts
|
|
2617
|
+
var user_default = { inviteUser: (0, _prismatic_io_spectral.action)({
|
|
2618
|
+
display: {
|
|
2619
|
+
label: "Invite User",
|
|
2620
|
+
description: "Invites a user by email to a customer."
|
|
2621
|
+
},
|
|
2622
|
+
inputs: inviteUserInputs,
|
|
2623
|
+
outputSchema: (0, _prismatic_io_spectral.outputSchema)({
|
|
2624
|
+
type: "actionOutput",
|
|
2625
|
+
schema: inviteUserOutputSchema
|
|
2626
|
+
}),
|
|
2627
|
+
performSafety: "notAllowed",
|
|
2628
|
+
perform: async (context, { connection, customerId, accessRole, emailAddress }) => {
|
|
2629
|
+
const { data } = await createClient({
|
|
2630
|
+
connection,
|
|
2631
|
+
debugEnabled: context.debug.enabled,
|
|
2632
|
+
logger: context.logger
|
|
2633
|
+
}).post(`customers/${customerId}/customerUserAccessInvitations:mutate`, { operation: { create: {
|
|
2634
|
+
accessRole,
|
|
2635
|
+
emailAddress
|
|
2636
|
+
} } });
|
|
2637
|
+
return { data };
|
|
2638
|
+
},
|
|
2639
|
+
examplePerform: async () => inviteUserExamplePayload,
|
|
2640
|
+
examplePayload: inviteUserExamplePayload
|
|
2641
|
+
}) };
|
|
2642
|
+
//#endregion
|
|
2643
|
+
//#region src/actions/index.ts
|
|
2644
|
+
var actions_default = {
|
|
2645
|
+
...campaign_default,
|
|
2646
|
+
...search_default,
|
|
2647
|
+
...conversions_default,
|
|
2648
|
+
...customer_default,
|
|
2649
|
+
...localServices_default,
|
|
2650
|
+
...misc_default,
|
|
2651
|
+
...user_default
|
|
2652
|
+
};
|
|
2653
|
+
//#endregion
|
|
2654
|
+
//#region src/dataSources/index.ts
|
|
2655
|
+
var dataSources_default = {
|
|
2656
|
+
listAccessibleCustomers: (0, _prismatic_io_spectral.dataSource)({
|
|
2657
|
+
display: {
|
|
2658
|
+
label: "List Accessible Customers",
|
|
2659
|
+
description: "Get a list of accessible customers for the logged in user."
|
|
2660
|
+
},
|
|
2661
|
+
inputs: listAccessibleCustomersInputs,
|
|
2662
|
+
perform: async (context, { connection }) => {
|
|
2663
|
+
const client = createClient({
|
|
2664
|
+
connection,
|
|
2665
|
+
debugEnabled: false,
|
|
2666
|
+
logger: context.logger
|
|
2667
|
+
});
|
|
2668
|
+
const { data } = await client.get("customers:listAccessibleCustomers");
|
|
2669
|
+
return { result: await Promise.all(data.resourceNames.map(async (resourceName) => {
|
|
2670
|
+
const customerId = cleanCustomerId(resourceName);
|
|
2671
|
+
return {
|
|
2672
|
+
label: `${await getCustomerDescriptiveName(client, customerId)} - ${formatAccountNumber(customerId)}`,
|
|
2673
|
+
key: customerId
|
|
2674
|
+
};
|
|
2675
|
+
})) };
|
|
2676
|
+
},
|
|
2677
|
+
dataSourceType: "picklist",
|
|
2678
|
+
examplePayload: listAccessibleCustomersDataSourceExamplePayload
|
|
2679
|
+
}),
|
|
2680
|
+
listAccessibleSubAccounts: (0, _prismatic_io_spectral.dataSource)({
|
|
2681
|
+
display: {
|
|
2682
|
+
label: "List Accessible Sub Accounts",
|
|
2683
|
+
description: "Get a list of accessible sub accounts for the customer ID provided."
|
|
2684
|
+
},
|
|
2685
|
+
inputs: listAccessibleSubAccountsInputs,
|
|
2686
|
+
perform: async (context, { connection, customerId, customerClientLevel }) => {
|
|
2687
|
+
return { result: ((await searchGoogleAds(createClient({
|
|
2688
|
+
connection,
|
|
2689
|
+
debugEnabled: false,
|
|
2690
|
+
logger: context.logger
|
|
2691
|
+
}), {
|
|
2692
|
+
customerId,
|
|
2693
|
+
params: { query: `${GET_ACCOUNT_HIERARCHY_QUERY}${customerClientLevel ?? "1"}` },
|
|
2694
|
+
fetchAll: true
|
|
2695
|
+
})).results ?? []).map(({ customerClient }) => {
|
|
2696
|
+
return {
|
|
2697
|
+
label: `${customerClient.descriptiveName} - ${formatAccountNumber(customerClient.id)}`,
|
|
2698
|
+
key: customerClient.id
|
|
2699
|
+
};
|
|
2700
|
+
}) };
|
|
2701
|
+
},
|
|
2702
|
+
dataSourceType: "picklist",
|
|
2703
|
+
examplePayload: listAccessibleSubAccountsExamplePayload
|
|
2704
|
+
})
|
|
2705
|
+
};
|
|
2706
|
+
//#endregion
|
|
2707
|
+
//#region src/triggers/budgetAlertTrigger.ts
|
|
2708
|
+
const budgetAlertPerform = async (context, payload, params) => {
|
|
2709
|
+
const { client, timezone } = await createTriggerClient(context, params);
|
|
2710
|
+
const pollState = getPollingState(context, {
|
|
2711
|
+
lastSyncDate: getPreviousDate(timezone),
|
|
2712
|
+
errorCount: 0,
|
|
2713
|
+
consecutiveErrors: 0
|
|
2714
|
+
});
|
|
2715
|
+
const newSyncDate = getCurrentDate(timezone);
|
|
2716
|
+
const threshold = params.alertThreshold ?? 80;
|
|
2717
|
+
try {
|
|
2718
|
+
const results = (await searchGoogleAds(client, {
|
|
2719
|
+
customerId: params.customerId,
|
|
2720
|
+
params: { query: buildBudgetAlertQuery({ includeSharedBudgets: params.includeSharedBudgets ?? true }) },
|
|
2721
|
+
fetchAll: true
|
|
2722
|
+
})).results ?? [];
|
|
2723
|
+
const budgetAlerts = results.map((campaign) => calculateBudgetStatus(campaign, threshold)).filter((status) => status.shouldAlert);
|
|
2724
|
+
context.polling.setState({
|
|
2725
|
+
lastSyncDate: newSyncDate,
|
|
2726
|
+
errorCount: 0,
|
|
2727
|
+
consecutiveErrors: 0
|
|
2728
|
+
});
|
|
2729
|
+
return Promise.resolve({
|
|
2730
|
+
payload: buildTriggerPayload(payload, {
|
|
2731
|
+
alerts: budgetAlerts,
|
|
2732
|
+
totalCampaignsMonitored: results.length,
|
|
2733
|
+
alertThreshold: threshold
|
|
2734
|
+
}),
|
|
2735
|
+
polledNoChanges: budgetAlerts.length === 0
|
|
2736
|
+
});
|
|
2737
|
+
} catch (e) {
|
|
2738
|
+
handlePollingError(e, pollState, context, "Google Ads budget");
|
|
2739
|
+
}
|
|
2740
|
+
};
|
|
2741
|
+
const budgetAlertTrigger = (0, _prismatic_io_spectral.pollingTrigger)({
|
|
2742
|
+
display: {
|
|
2743
|
+
label: "Campaign Budget Alerts",
|
|
2744
|
+
description: "Checks for campaigns approaching or exceeding budget thresholds on a configured schedule."
|
|
2745
|
+
},
|
|
2746
|
+
inputs: budgetAlertTriggerInputs,
|
|
2747
|
+
triggerResolverSupport: "valid",
|
|
2748
|
+
batchConfig: { batchSize: 50 },
|
|
2749
|
+
triggerResolver: { resolveItems: (_context, { payload }) => resolveBudgetAlerts(payload.body.data) },
|
|
2750
|
+
perform: budgetAlertPerform,
|
|
2751
|
+
examplePayload: budgetAlertTriggerExamplePayload
|
|
2752
|
+
});
|
|
2753
|
+
//#endregion
|
|
2754
|
+
//#region src/triggers/campaignChangesTrigger.ts
|
|
2755
|
+
const campaignChangesPerform = async (context, payload, params) => {
|
|
2756
|
+
const { client, timezone } = await createTriggerClient(context, params);
|
|
2757
|
+
const nowTime = getGAQLDateTime(timezone);
|
|
2758
|
+
const pollState = getPollingState(context, {
|
|
2759
|
+
lastChangeTime: params.lookBackDate ? `${params.lookBackDate} ${LOOK_BACK_DATE_START_TIME}` : getGAQLDateTime(timezone, 1),
|
|
2760
|
+
errorCount: 0,
|
|
2761
|
+
consecutiveErrors: 0
|
|
2762
|
+
});
|
|
2763
|
+
try {
|
|
2764
|
+
const incoming = payload.paginationState;
|
|
2765
|
+
const isContinuation = Boolean(incoming ?? pollState.inFlightCursor);
|
|
2766
|
+
const cursor = resolveChangeEventCursor({
|
|
2767
|
+
incoming,
|
|
2768
|
+
state: pollState,
|
|
2769
|
+
nowTime,
|
|
2770
|
+
timeZone: timezone
|
|
2771
|
+
});
|
|
2772
|
+
const pageSize = resolveChangeEventPageSize(context.batch?.enabled);
|
|
2773
|
+
let rows = [];
|
|
2774
|
+
if (cursor.sinceTime < cursor.toTime) rows = (await searchGoogleAds(client, {
|
|
2775
|
+
customerId: params.customerId,
|
|
2776
|
+
params: { query: buildCampaignChangeEventQuery({
|
|
2777
|
+
sinceTime: cursor.sinceTime,
|
|
2778
|
+
toTime: cursor.toTime,
|
|
2779
|
+
changeTypes: params.changeTypes,
|
|
2780
|
+
limit: pageSize
|
|
2781
|
+
}) },
|
|
2782
|
+
fetchAll: true
|
|
2783
|
+
})).results ?? [];
|
|
2784
|
+
const { emit, nextCursor } = advanceChangeEventCursor(rows, cursor, pageSize, context.logger);
|
|
2785
|
+
const changes = mapChangeEventsToCampaignChanges(emit, params.changeTypes);
|
|
2786
|
+
context.polling.setState({
|
|
2787
|
+
lastChangeTime: nextCursor ? pollState.lastChangeTime : cursor.toTime,
|
|
2788
|
+
...nextCursor ? { inFlightCursor: nextCursor } : {},
|
|
2789
|
+
errorCount: pollState.errorCount,
|
|
2790
|
+
consecutiveErrors: 0
|
|
2791
|
+
});
|
|
2792
|
+
return Promise.resolve({
|
|
2793
|
+
payload: {
|
|
2794
|
+
...buildTriggerPayload(payload, {
|
|
2795
|
+
changes,
|
|
2796
|
+
changesDetected: changes.length,
|
|
2797
|
+
timeRange: {
|
|
2798
|
+
start: cursor.sinceTime,
|
|
2799
|
+
end: cursor.toTime
|
|
2800
|
+
},
|
|
2801
|
+
syncedAt: cursor.toTime
|
|
2802
|
+
}),
|
|
2803
|
+
paginationState: nextCursor ?? void 0
|
|
2804
|
+
},
|
|
2805
|
+
polledNoChanges: changes.length === 0 && nextCursor === null && !isContinuation
|
|
2806
|
+
});
|
|
2807
|
+
} catch (e) {
|
|
2808
|
+
handlePollingError(e, pollState, context, "Google Ads");
|
|
2809
|
+
}
|
|
2810
|
+
};
|
|
2811
|
+
const campaignChangesTrigger = (0, _prismatic_io_spectral.pollingTrigger)({
|
|
2812
|
+
display: {
|
|
2813
|
+
label: "New and Updated Campaigns",
|
|
2814
|
+
description: "Checks for new and updated campaigns in a Google Ads account on a configured schedule."
|
|
2815
|
+
},
|
|
2816
|
+
inputs: campaignChangesTriggerInputs,
|
|
2817
|
+
triggerResolverSupport: "valid",
|
|
2818
|
+
batchConfig: { batchSize: 50 },
|
|
2819
|
+
triggerResolver: {
|
|
2820
|
+
resolveItems: (_context, { payload }) => resolveCampaignChanges(payload.body.data),
|
|
2821
|
+
getNextPaginationState: (_context, { payload }) => payload.paginationState ?? null
|
|
2822
|
+
},
|
|
2823
|
+
perform: campaignChangesPerform,
|
|
2824
|
+
examplePayload: campaignChangesTriggerExamplePayload
|
|
2825
|
+
});
|
|
2826
|
+
//#endregion
|
|
2827
|
+
//#region src/triggers/changeHistoryTrigger.ts
|
|
2828
|
+
const changeHistoryPerform = async (context, payload, params) => {
|
|
2829
|
+
const { client, timezone } = await createTriggerClient(context, params);
|
|
2830
|
+
const nowTime = getGAQLDateTime(timezone);
|
|
2831
|
+
const pollState = getPollingState(context, {
|
|
2832
|
+
lastChangeTime: params.lookBackDate ? `${params.lookBackDate} ${LOOK_BACK_DATE_START_TIME}` : getGAQLDateTime(timezone, 1),
|
|
2833
|
+
errorCount: 0,
|
|
2834
|
+
consecutiveErrors: 0
|
|
2835
|
+
});
|
|
2836
|
+
try {
|
|
2837
|
+
const incoming = payload.paginationState;
|
|
2838
|
+
const isContinuation = Boolean(incoming ?? pollState.inFlightCursor);
|
|
2839
|
+
const cursor = resolveChangeEventCursor({
|
|
2840
|
+
incoming,
|
|
2841
|
+
state: pollState,
|
|
2842
|
+
nowTime,
|
|
2843
|
+
timeZone: timezone
|
|
2844
|
+
});
|
|
2845
|
+
const pageSize = resolveChangeEventPageSize(context.batch?.enabled);
|
|
2846
|
+
let rows = [];
|
|
2847
|
+
if (cursor.sinceTime < cursor.toTime) rows = (await searchGoogleAds(client, {
|
|
2848
|
+
customerId: params.customerId,
|
|
2849
|
+
params: { query: buildChangeHistoryQuery({
|
|
2850
|
+
sinceTime: cursor.sinceTime,
|
|
2851
|
+
toTime: cursor.toTime,
|
|
2852
|
+
resourceTypes: params.resourceTypes,
|
|
2853
|
+
includeUserInfo: params.includeUserInfo,
|
|
2854
|
+
limit: pageSize
|
|
2855
|
+
}) },
|
|
2856
|
+
fetchAll: true
|
|
2857
|
+
})).results ?? [];
|
|
2858
|
+
const { emit, nextCursor } = advanceChangeEventCursor(rows, cursor, pageSize, context.logger);
|
|
2859
|
+
const results = [...emit].reverse();
|
|
2860
|
+
context.polling.setState({
|
|
2861
|
+
lastChangeTime: nextCursor ? pollState.lastChangeTime : cursor.toTime,
|
|
2862
|
+
...nextCursor ? { inFlightCursor: nextCursor } : {},
|
|
2863
|
+
changeCount: results.length,
|
|
2864
|
+
errorCount: 0,
|
|
2865
|
+
consecutiveErrors: 0
|
|
2866
|
+
});
|
|
2867
|
+
return Promise.resolve({
|
|
2868
|
+
payload: {
|
|
2869
|
+
...buildTriggerPayload(payload, {
|
|
2870
|
+
changes: results,
|
|
2871
|
+
changeCount: results.length,
|
|
2872
|
+
timeRange: {
|
|
2873
|
+
start: cursor.sinceTime,
|
|
2874
|
+
end: cursor.toTime
|
|
2875
|
+
}
|
|
2876
|
+
}),
|
|
2877
|
+
paginationState: nextCursor ?? void 0
|
|
2878
|
+
},
|
|
2879
|
+
polledNoChanges: results.length === 0 && nextCursor === null && !isContinuation
|
|
2880
|
+
});
|
|
2881
|
+
} catch (e) {
|
|
2882
|
+
handlePollingError(e, pollState, context, "Google Ads change history");
|
|
2883
|
+
}
|
|
2884
|
+
};
|
|
2885
|
+
//#endregion
|
|
2886
|
+
//#region src/triggers/index.ts
|
|
2887
|
+
var triggers_default = {
|
|
2888
|
+
campaignChangesTrigger,
|
|
2889
|
+
changeHistoryTrigger: (0, _prismatic_io_spectral.pollingTrigger)({
|
|
2890
|
+
display: {
|
|
2891
|
+
label: "Account Change History",
|
|
2892
|
+
description: "Checks for Google Ads account modifications with user attribution on a configured schedule."
|
|
2893
|
+
},
|
|
2894
|
+
inputs: changeHistoryTriggerInputs,
|
|
2895
|
+
triggerResolverSupport: "valid",
|
|
2896
|
+
batchConfig: { batchSize: 50 },
|
|
2897
|
+
triggerResolver: {
|
|
2898
|
+
resolveItems: (_context, { payload }) => resolveChangeHistoryItems(payload.body.data),
|
|
2899
|
+
getNextPaginationState: (_context, { payload }) => payload.paginationState ?? null
|
|
2900
|
+
},
|
|
2901
|
+
perform: changeHistoryPerform,
|
|
2902
|
+
examplePayload: changeHistoryTriggerExamplePayload
|
|
2903
|
+
}),
|
|
2904
|
+
budgetAlertTrigger
|
|
2905
|
+
};
|
|
2906
|
+
//#endregion
|
|
2907
|
+
//#region src/index.ts
|
|
2908
|
+
var src_default = (0, _prismatic_io_spectral.component)({
|
|
2909
|
+
key: "google-ads",
|
|
2910
|
+
documentationUrl: "https://prismatic.io/docs/components/google-ads/",
|
|
2911
|
+
public: true,
|
|
2912
|
+
display: {
|
|
2913
|
+
label: "Google Ads",
|
|
2914
|
+
description: "Manage campaigns, conversions, customers, and local services in Google Ads.",
|
|
2915
|
+
iconPath: "icon.png",
|
|
2916
|
+
category: "Application Connectors"
|
|
2917
|
+
},
|
|
2918
|
+
hooks: { error: _prismatic_io_spectral_http.handleErrors },
|
|
2919
|
+
connections: connections_default,
|
|
2920
|
+
dataSources: dataSources_default,
|
|
2921
|
+
actions: actions_default,
|
|
2922
|
+
triggers: triggers_default
|
|
2923
|
+
});
|
|
2924
|
+
//#endregion
|
|
2925
|
+
//#region npm-dist/.entry.ts
|
|
2926
|
+
var _entry_default = (0, _prismatic_io_spectral.callableComponent)(src_default);
|
|
2927
|
+
//#endregion
|
|
2928
|
+
module.exports = _entry_default;
|