@pipedream/google_ads 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,159 @@
1
+ import {
2
+ USER_LIST_TYPES, USER_LIST_TYPE_OPTIONS,
3
+ } from "./common-constants.mjs";
4
+ import {
5
+ parseObject, parseStringObject,
6
+ } from "../../common/utils.mjs";
7
+ import common from "../common/common.mjs";
8
+ import {
9
+ getAdditionalFields, getListTypeInfo,
10
+ } from "../common/props.mjs";
11
+ import { ConfigurationError } from "@pipedream/platform";
12
+
13
+ export default {
14
+ ...common,
15
+ key: "google_ads-create-customer-list",
16
+ name: "Create Customer List",
17
+ description:
18
+ "Create a new customer list in Google Ads. [See the documentation](https://developers.google.com/google-ads/api/rest/reference/rest/v16/UserList)",
19
+ version: "0.0.1",
20
+ type: "action",
21
+ props: {
22
+ ...common.props,
23
+ name: {
24
+ type: "string",
25
+ label: "Name",
26
+ description: "The name of the customer list.",
27
+ },
28
+ description: {
29
+ type: "string",
30
+ label: "Description",
31
+ description: "Description of the customer list.",
32
+ optional: true,
33
+ },
34
+ listType: {
35
+ type: "string",
36
+ label: "List Type",
37
+ description:
38
+ "The [type of customer list](https://developers.google.com/google-ads/api/rest/reference/rest/v16/UserList#CrmBasedUserListInfo) to create.",
39
+ options: USER_LIST_TYPE_OPTIONS.map(({
40
+ label, value,
41
+ }) => ({
42
+ label,
43
+ value,
44
+ })),
45
+ reloadProps: true,
46
+ },
47
+ },
48
+ methods: {
49
+ parseFields(obj) {
50
+ switch (this.listType) {
51
+ case USER_LIST_TYPES.CRM_BASED:
52
+ break;
53
+
54
+ case USER_LIST_TYPES.RULE_BASED:
55
+ if (obj.prepopulationStatus) {
56
+ obj.prepopulationStatus = "REQUESTED";
57
+ }
58
+ if (obj.flexibleRuleUserList) {
59
+ obj.flexibleRuleUserList = parseObject(obj.flexibleRuleUserList);
60
+ }
61
+ break;
62
+
63
+ case USER_LIST_TYPES.LOGICAL: {
64
+ let { rules } = obj;
65
+ if (rules) {
66
+ rules = rules.map?.((rule) => parseStringObject(rule)) ?? parseStringObject(rules);
67
+ }
68
+ break;
69
+ }
70
+
71
+ case USER_LIST_TYPES.BASIC:
72
+ if (obj?.conversionActions?.length || obj?.remarketingActions?.length) {
73
+ obj.actions = [
74
+ ...(obj.conversionActions ?? []).map((conversionAction) => ({
75
+ conversionAction,
76
+ })),
77
+ ...(obj.remarketingActions ?? []).map((remarketingAction) => ({
78
+ remarketingAction,
79
+ })),
80
+ ];
81
+ delete obj.conversionActions;
82
+ delete obj.remarketingActions;
83
+ } else {
84
+ throw new ConfigurationError("Select at least one Conversion or Remarketing action to build the list with");
85
+ }
86
+ break;
87
+
88
+ case USER_LIST_TYPES.LOOKALIKE:
89
+ break;
90
+ }
91
+
92
+ return obj;
93
+ },
94
+ },
95
+ additionalProps() {
96
+ const { listType } = this;
97
+
98
+ const option = USER_LIST_TYPE_OPTIONS.find(
99
+ ({ value }) => value === listType,
100
+ );
101
+ if (!option) {
102
+ throw new ConfigurationError("Select a valid List Type to proceed.");
103
+ }
104
+
105
+ const {
106
+ docsLink, props,
107
+ } = option;
108
+
109
+ const newProps = {
110
+ listTypeInfo: getListTypeInfo(docsLink),
111
+ };
112
+
113
+ Object.assign(newProps, props);
114
+
115
+ newProps.additionalFields = getAdditionalFields(docsLink);
116
+
117
+ return newProps;
118
+ },
119
+ async run({ $ }) {
120
+ const {
121
+ googleAds,
122
+ accountId,
123
+ customerClientId,
124
+ name,
125
+ description,
126
+ listType,
127
+ additionalFields,
128
+ ...data
129
+ } = this;
130
+ const { results: { [0]: response } } = await googleAds.createUserList({
131
+ $,
132
+ accountId,
133
+ customerClientId,
134
+ data: {
135
+ operations: [
136
+ {
137
+ create: {
138
+ name,
139
+ description,
140
+ [listType]: this.parseFields(data),
141
+ ...parseObject(additionalFields),
142
+ },
143
+ },
144
+ ],
145
+ },
146
+ });
147
+
148
+ const id = response.resourceName.split("/").pop();
149
+
150
+ $.export(
151
+ "$summary",
152
+ `Created customer list of type \`${listType}\` with ID \`${id}\``,
153
+ );
154
+ return {
155
+ id,
156
+ ...response,
157
+ };
158
+ },
159
+ };
@@ -0,0 +1,156 @@
1
+ import common from "../common/common.mjs";
2
+ import { adGroup } from "../../common/resources/adGroup.mjs";
3
+ import { ad } from "../../common/resources/ad.mjs";
4
+ import { campaign } from "../../common/resources/campaign.mjs";
5
+ import { customer } from "../../common/resources/customer.mjs";
6
+ import { ConfigurationError } from "@pipedream/platform";
7
+
8
+ const RESOURCES = [
9
+ adGroup,
10
+ ad,
11
+ campaign,
12
+ customer,
13
+ ];
14
+
15
+ export default {
16
+ ...common,
17
+ key: "google_ads-create-report",
18
+ name: "Create Report",
19
+ description: "Generates a report from your Google Ads data. [See the documentation](https://developers.google.com/google-ads/api/fields/v16/overview)",
20
+ version: "0.0.1",
21
+ type: "action",
22
+ props: {
23
+ ...common.props,
24
+ resource: {
25
+ type: "string",
26
+ label: "Resource",
27
+ description: "The resource to generate a report for.",
28
+ options: RESOURCES.map((r) => r.resourceOption),
29
+ reloadProps: true,
30
+ },
31
+ },
32
+ additionalProps() {
33
+ const resource = RESOURCES.find((r) => r.resourceOption.value === this.resource);
34
+ if (!resource) throw new ConfigurationError("Select one of the available resources.");
35
+
36
+ const {
37
+ label, value,
38
+ } = resource.resourceOption;
39
+
40
+ return {
41
+ docsAlert: {
42
+ type: "alert",
43
+ alertType: "info",
44
+ content: `[See the documentation](https://developers.google.com/google-ads/api/fields/v16/${value}) for more information on available fields, segments and metrics.`,
45
+ },
46
+ fields: {
47
+ type: "string[]",
48
+ label: "Fields",
49
+ description: `${label} data fields to obtain`,
50
+ options: resource.fields,
51
+ optional: true,
52
+ reloadProps: true,
53
+ },
54
+ segments: {
55
+ type: "string[]",
56
+ label: "Segments",
57
+ description: `${label} segments to obtain [more info on the documentation](https://developers.google.com/google-ads/api/fields/v16/segments)`,
58
+ options: resource.segments,
59
+ optional: true,
60
+ reloadProps: true,
61
+ },
62
+ metrics: {
63
+ type: "string[]",
64
+ label: "Metrics",
65
+ description: `${label} metrics to obtain [more info on the documentation](https://developers.google.com/google-ads/api/fields/v16/metrics)`,
66
+ options: resource.metrics,
67
+ optional: true,
68
+ reloadProps: true,
69
+ },
70
+ orderBy: {
71
+ type: "string",
72
+ label: "Order By",
73
+ description: "The field to order the results by",
74
+ optional: true,
75
+ options: [
76
+ ...(this.fields ?? []),
77
+ ...(this.segments ?? []),
78
+ ...(this.metrics ?? []),
79
+ ],
80
+ },
81
+ direction: {
82
+ type: "string",
83
+ label: "Direction",
84
+ description: "The direction to order the results by, if `Order By` is specified",
85
+ optional: true,
86
+ options: [
87
+ {
88
+ label: "Ascending",
89
+ value: "ASC",
90
+ },
91
+ {
92
+ label: "Descending",
93
+ value: "DESC",
94
+ },
95
+ ],
96
+ default: "ASC",
97
+ },
98
+ limit: {
99
+ type: "integer",
100
+ label: "Limit",
101
+ description: "The maximum number of results to return",
102
+ optional: true,
103
+ },
104
+ };
105
+ },
106
+ methods: {
107
+ buildQuery() {
108
+ const {
109
+ resource, limit, orderBy, direction,
110
+ } = this;
111
+ const fields = this.fields?.map((i) => `${resource}.${i}`) ?? [];
112
+ const segments = this.segments?.map((i) => `segments.${i}`) ?? [];
113
+ const metrics = this.metrics?.map((i) => `metrics.${i}`) ?? [];
114
+ const selection = [
115
+ ...fields,
116
+ ...segments,
117
+ ...metrics,
118
+ ];
119
+
120
+ if (!selection.length) {
121
+ throw new ConfigurationError("Select at least one field, segment or metric.");
122
+ }
123
+
124
+ let query = `SELECT ${selection.join(", ")} FROM ${resource}`;
125
+ if (orderBy && direction) {
126
+ query += ` ORDER BY ${`${resource}.${orderBy}`} ${direction}`;
127
+ }
128
+ if (limit) {
129
+ query += ` LIMIT ${limit}`;
130
+ }
131
+
132
+ return query;
133
+ },
134
+ },
135
+ async run({ $ }) {
136
+ const query = this.buildQuery();
137
+ const results = (await this.googleAds.createReport({
138
+ $,
139
+ accountId: this.accountId,
140
+ customerClientId: this.customerClientId,
141
+ data: {
142
+ query,
143
+ },
144
+ })) ?? [];
145
+
146
+ const { length } = results;
147
+
148
+ $.export("$summary", `Sucessfully obtained ${length} result${length === 1
149
+ ? ""
150
+ : "s"}`);
151
+ return {
152
+ query,
153
+ results,
154
+ };
155
+ },
156
+ };
@@ -0,0 +1,40 @@
1
+ export const CONVERSION_TYPE_OPTIONS = [
2
+ {
3
+ label:
4
+ "Conversions that occur when a user clicks on an ad's call extension.",
5
+ value: "AD_CALL",
6
+ },
7
+ {
8
+ label:
9
+ "Conversions that occur when a user on a mobile device clicks a phone number.",
10
+ value: "CLICK_TO_CALL",
11
+ },
12
+ {
13
+ label:
14
+ "Conversions that occur when a user downloads a mobile app from the Google Play Store.",
15
+ value: "GOOGLE_PLAY_DOWNLOAD",
16
+ },
17
+ {
18
+ label:
19
+ "Conversions that occur when a user makes a purchase in an app through Android billing.",
20
+ value: "GOOGLE_PLAY_IN_APP_PURCHASE",
21
+ },
22
+ {
23
+ label: "Call conversions that are tracked by the advertiser and uploaded.",
24
+ value: "UPLOAD_CALLS",
25
+ },
26
+ {
27
+ label:
28
+ "Conversions that are tracked by the advertiser and uploaded with attributed clicks.",
29
+ value: "UPLOAD_CLICKS",
30
+ },
31
+ {
32
+ label: "Conversions that occur on a webpage.",
33
+ value: "WEBPAGE",
34
+ },
35
+ {
36
+ label:
37
+ "Conversions that occur when a user calls a dynamically-generated phone number from an advertiser's website.",
38
+ value: "WEBSITE_CALL",
39
+ },
40
+ ];
@@ -0,0 +1,56 @@
1
+ import { parseObject } from "../../common/utils.mjs";
2
+ import common from "../common/common.mjs";
3
+ import { getAdditionalFields } from "../common/props.mjs";
4
+ import { CONVERSION_TYPE_OPTIONS } from "./common-constants.mjs";
5
+
6
+ export default {
7
+ ...common,
8
+ key: "google_ads-send-offline-conversion",
9
+ name: "Send Offline Conversion",
10
+ description: "Send an event from to Google Ads to track offline conversions. [See the documentation](https://developers.google.com/google-ads/api/rest/reference/rest/v16/ConversionAction)",
11
+ version: "0.0.1",
12
+ type: "action",
13
+ props: {
14
+ ...common.props,
15
+ name: {
16
+ type: "string",
17
+ label: "Name",
18
+ description: "The name of the conversion action.",
19
+ },
20
+ type: {
21
+ type: "string",
22
+ label: "Type",
23
+ description: "[The type](https://developers.google.com/google-ads/api/rest/reference/rest/v16/ConversionAction#ConversionActionType) of the conversion action.",
24
+ options: CONVERSION_TYPE_OPTIONS,
25
+ },
26
+ additionalFields: getAdditionalFields("https://developers.google.com/google-ads/api/rest/reference/rest/v16/ConversionAction"),
27
+ },
28
+ async run({ $ }) {
29
+ const {
30
+ googleAds, accountId, customerClientId, additionalFields, ...data
31
+ } = this;
32
+ const { results: { [0]: response } } = await googleAds.createConversionAction({
33
+ $,
34
+ accountId,
35
+ customerClientId,
36
+ data: {
37
+ operations: [
38
+ {
39
+ create: {
40
+ ...data,
41
+ ...parseObject(additionalFields),
42
+ },
43
+ },
44
+ ],
45
+ },
46
+ });
47
+
48
+ const id = response.resourceName.split("/").pop();
49
+
50
+ $.export("$summary", `Created conversion action with ID ${id}`);
51
+ return {
52
+ id,
53
+ ...response,
54
+ };
55
+ },
56
+ };
@@ -0,0 +1,20 @@
1
+ import googleAds from "../google_ads.app.mjs";
2
+
3
+ export default {
4
+ googleAds,
5
+ accountId: {
6
+ propDefinition: [
7
+ googleAds,
8
+ "accountId",
9
+ ],
10
+ },
11
+ customerClientId: {
12
+ propDefinition: [
13
+ googleAds,
14
+ "customerClientId",
15
+ ({ accountId }) => ({
16
+ accountId,
17
+ }),
18
+ ],
19
+ },
20
+ };
@@ -0,0 +1,105 @@
1
+ function listCustomerClients(query) {
2
+ const fields = [
3
+ "client_customer",
4
+ "descriptive_name",
5
+ "id",
6
+ "level",
7
+ "manager",
8
+ ]
9
+ .map((s) => `customer_client.${s}`)
10
+ .join(", ");
11
+
12
+ const condition = query
13
+ ? `customer_client.descriptive_name LIKE '%${query}%'`
14
+ : "customer_client.level <= 3";
15
+
16
+ return `SELECT ${fields} FROM customer_client WHERE ${condition}`;
17
+ }
18
+
19
+ function listUserLists() {
20
+ const fields = [
21
+ "id",
22
+ "name",
23
+ ].map((s) => `user_list.${s}`).join(", ");
24
+
25
+ return `SELECT ${fields} FROM user_list`;
26
+ }
27
+
28
+ function listConversionActions() {
29
+ const fields = [
30
+ "name",
31
+ ].map((s) => `conversion_action.${s}`).join(", ");
32
+
33
+ return `SELECT ${fields} FROM conversion_action`;
34
+ }
35
+
36
+ function listRemarketingActions() {
37
+ const fields = [
38
+ "name",
39
+ ].map((s) => `remarketing_action.${s}`).join(", ");
40
+
41
+ return `SELECT ${fields} FROM remarketing_action`;
42
+ }
43
+
44
+ function listLeadForms() {
45
+ const assetFields = [
46
+ "id",
47
+ ].map(((s) => `asset.${s}`));
48
+ const leadFormFields = [
49
+ "business_name",
50
+ "headline",
51
+ ].map((s) => `asset.lead_form_asset.${s}`);
52
+
53
+ return `SELECT ${[
54
+ ...assetFields,
55
+ ...leadFormFields,
56
+ ].join(", ")} FROM asset WHERE asset.type = 'LEAD_FORM'`;
57
+ }
58
+
59
+ function listLeadFormSubmissionData(id) {
60
+ const fields = [
61
+ "custom_lead_form_submission_fields",
62
+ "gclid",
63
+ "id",
64
+ "lead_form_submission_fields",
65
+ "submission_date_time",
66
+ ].map((s) => `lead_form_submission_data.${s}`).join(", ");
67
+ return `SELECT ${fields} FROM lead_form_submission_data WHERE asset.id = '${id}'`;
68
+ }
69
+
70
+ function listCampaigns({
71
+ fields, savedIds,
72
+ }) {
73
+ const defaultFields = [
74
+ "id",
75
+ "name",
76
+ ];
77
+ if (typeof fields === "string") {
78
+ fields = fields.split(",").map((s) => s.trim());
79
+ }
80
+ if (!fields?.length) {
81
+ fields = defaultFields;
82
+ } else {
83
+ defaultFields.forEach((f) => {
84
+ if (!fields.includes(f)) {
85
+ fields.push(f);
86
+ }
87
+ });
88
+ }
89
+
90
+ const filter = savedIds?.length
91
+ ? ` WHERE ${savedIds.map((id) => `campaign.id != ${id}`).join(" AND ")}`
92
+ : "";
93
+
94
+ return `SELECT ${fields.map((s) => `campaign.${s}`).join(", ")} FROM campaign${filter}`;
95
+ }
96
+
97
+ export const QUERIES = {
98
+ listCampaigns,
99
+ listConversionActions,
100
+ listCustomerClients,
101
+ listLeadForms,
102
+ listLeadFormSubmissionData,
103
+ listRemarketingActions,
104
+ listUserLists,
105
+ };