@google-cloud/nodejs-common 1.2.1-beta → 1.3.9-beta
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/package.json +17 -17
- package/src/apis/google_ads.js +110 -31
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@google-cloud/nodejs-common",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.9-beta",
|
|
4
4
|
"description": "A NodeJs common library for solutions based on Cloud Functions",
|
|
5
5
|
"author": "Google Inc.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -16,25 +16,25 @@
|
|
|
16
16
|
},
|
|
17
17
|
"homepage": "https://github.com/GoogleCloudPlatform/cloud-for-marketing/blob/master/marketing-analytics/activation/common-libs/nodejs-common/README.md",
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@google-cloud/aiplatform": "^
|
|
20
|
-
"@google-cloud/automl": "^
|
|
21
|
-
"@google-cloud/bigquery": "^6.0.
|
|
22
|
-
"@google-cloud/datastore": "^
|
|
23
|
-
"@google-cloud/firestore": "^
|
|
24
|
-
"@google-cloud/logging-winston": "^5.1.
|
|
25
|
-
"@google-cloud/pubsub": "^3.0
|
|
26
|
-
"@google-cloud/storage": "^6.
|
|
27
|
-
"@google-cloud/scheduler": "^3.0.
|
|
28
|
-
"gaxios": "^5.0.
|
|
29
|
-
"google-ads-api": "^11.
|
|
30
|
-
"google-ads-node": "^9.
|
|
31
|
-
"google-auth-library": "^8.
|
|
32
|
-
"googleapis": "^
|
|
33
|
-
"winston": "^3.
|
|
19
|
+
"@google-cloud/aiplatform": "^2.3.0",
|
|
20
|
+
"@google-cloud/automl": "^3.1.0",
|
|
21
|
+
"@google-cloud/bigquery": "^6.0.3",
|
|
22
|
+
"@google-cloud/datastore": "^7.0.0",
|
|
23
|
+
"@google-cloud/firestore": "^6.3.0",
|
|
24
|
+
"@google-cloud/logging-winston": "^5.1.5",
|
|
25
|
+
"@google-cloud/pubsub": "^3.2.0",
|
|
26
|
+
"@google-cloud/storage": "^6.5.2",
|
|
27
|
+
"@google-cloud/scheduler": "^3.0.4",
|
|
28
|
+
"gaxios": "^5.0.2",
|
|
29
|
+
"google-ads-api": "^11.1.0",
|
|
30
|
+
"google-ads-node": "^9.1.0",
|
|
31
|
+
"google-auth-library": "^8.5.2",
|
|
32
|
+
"googleapis": "^108.0.0",
|
|
33
|
+
"winston": "^3.8.2",
|
|
34
34
|
"lodash": "^4.17.21"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"jasmine": "^4.
|
|
37
|
+
"jasmine": "^4.4.0"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
40
|
"test": "node node_modules/jasmine/bin/jasmine"
|
package/src/apis/google_ads.js
CHANGED
|
@@ -28,6 +28,7 @@ const {
|
|
|
28
28
|
resources: {
|
|
29
29
|
GoogleAdsField,
|
|
30
30
|
OfflineUserDataJob,
|
|
31
|
+
UserList,
|
|
31
32
|
},
|
|
32
33
|
services: {
|
|
33
34
|
CreateOfflineUserDataJobRequest,
|
|
@@ -50,6 +51,9 @@ const {
|
|
|
50
51
|
enums: {
|
|
51
52
|
OfflineUserDataJobTypeEnum: { OfflineUserDataJobType },
|
|
52
53
|
OfflineUserDataJobStatusEnum: { OfflineUserDataJobStatus },
|
|
54
|
+
UserListMembershipStatusEnum: { UserListMembershipStatus },
|
|
55
|
+
UserListTypeEnum: { UserListType },
|
|
56
|
+
CustomerMatchUploadKeyTypeEnum: { CustomerMatchUploadKeyType },
|
|
53
57
|
},
|
|
54
58
|
} = googleAdsLib;
|
|
55
59
|
const {GoogleAdsApi} = require('google-ads-api');
|
|
@@ -154,13 +158,18 @@ let ConversionConfig;
|
|
|
154
158
|
/**
|
|
155
159
|
* Configuration for uploading customer match to Google Ads, includes:
|
|
156
160
|
* customer_id, login_customer_id, list_id and operation.
|
|
161
|
+
* If audience list_id is not present, 'list_name' and 'upload_key_type' need to
|
|
162
|
+
* be there so they can be used to create a customer match user list.
|
|
157
163
|
* operation must be one of the two: 'create' or 'remove'.
|
|
158
164
|
* @see https://developers.google.com/google-ads/api/reference/rpc/latest/UserDataOperation
|
|
165
|
+
* @see https://developers.google.com/google-ads/api/reference/rpc/latest/CustomerMatchUploadKeyTypeEnum.CustomerMatchUploadKeyType
|
|
159
166
|
* @typedef {{
|
|
160
|
-
* customer_id: string,
|
|
161
|
-
* login_customer_id: string,
|
|
162
|
-
* list_id: string,
|
|
163
|
-
*
|
|
167
|
+
* customer_id: (string|number),
|
|
168
|
+
* login_customer_id: (string|number),
|
|
169
|
+
* list_id: (string|undefined),
|
|
170
|
+
* list_name: (string|undefined),
|
|
171
|
+
* upload_key_type: ('CONTACT_INFO'|'CRM_ID'|'MOBILE_ADVERTISING_ID'|undefined),
|
|
172
|
+
* operation: ('create'|'remove'),
|
|
164
173
|
* }}
|
|
165
174
|
*/
|
|
166
175
|
let CustomerMatchConfig;
|
|
@@ -171,12 +180,17 @@ let CustomerMatchConfig;
|
|
|
171
180
|
* 'operation' should be one of the two: 'create' or 'remove',
|
|
172
181
|
* 'type' is OfflineUserDataJobType, it can be 'CUSTOMER_MATCH_USER_LIST' or
|
|
173
182
|
* 'STORE_SALES_UPLOAD_FIRST_PARTY'.
|
|
183
|
+
* For job type 'CUSTOMER_MATCH_USER_LIST', if `list_id` is not present,
|
|
184
|
+
* 'list_name' and 'upload_key_type' need to be there so they can be used to
|
|
185
|
+
* create a customer match user list.
|
|
174
186
|
* @see https://developers.google.com/google-ads/api/reference/rpc/latest/OfflineUserDataJob
|
|
175
187
|
* @typedef {{
|
|
176
|
-
* customer_id: string,
|
|
177
|
-
* login_customer_id: string,
|
|
178
|
-
* list_id: (undefined
|
|
179
|
-
*
|
|
188
|
+
* customer_id: (string|number),
|
|
189
|
+
* login_customer_id: (string|number),
|
|
190
|
+
* list_id: (string|undefined),
|
|
191
|
+
* list_name: (string|undefined),
|
|
192
|
+
* upload_key_type: ('CONTACT_INFO'|'CRM_ID'|'MOBILE_ADVERTISING_ID'|undefined),
|
|
193
|
+
* operation: ('create'|'remove'),
|
|
180
194
|
* type: !OfflineUserDataJobType,
|
|
181
195
|
* storeSalesMetadata: (undefined|object),
|
|
182
196
|
* }}
|
|
@@ -624,6 +638,83 @@ class GoogleAds {
|
|
|
624
638
|
}
|
|
625
639
|
}
|
|
626
640
|
|
|
641
|
+
/**
|
|
642
|
+
* Gets the user list_id of a given list name and upload key type. It
|
|
643
|
+
* only looks for a CRM_BASED and OPEN list.
|
|
644
|
+
* @param {!CustomerMatchConfig} customerMatchConfig
|
|
645
|
+
* @return {number|undefined} User list_id if it exists.
|
|
646
|
+
*/
|
|
647
|
+
async getCustomerMatchUserListId(customerMatchConfig) {
|
|
648
|
+
const customerId = this.getCleanCid_(customerMatchConfig.customer_id);
|
|
649
|
+
const loginCustomerId = this.getCleanCid_(
|
|
650
|
+
customerMatchConfig.login_customer_id);
|
|
651
|
+
const listName = customerMatchConfig.list_name;
|
|
652
|
+
const uploadKeyType = customerMatchConfig.upload_key_type;
|
|
653
|
+
const reportConfig = {
|
|
654
|
+
entity: 'user_list',
|
|
655
|
+
attributes: [
|
|
656
|
+
'user_list.id',
|
|
657
|
+
'user_list.resource_name',
|
|
658
|
+
],
|
|
659
|
+
constraints: {
|
|
660
|
+
'user_list.name': listName,
|
|
661
|
+
'customer.id': customerId,
|
|
662
|
+
'user_list.type': UserListType.CRM_BASED,
|
|
663
|
+
'user_list.membership_status': UserListMembershipStatus.OPEN,
|
|
664
|
+
'user_list.crm_based_user_list.upload_key_type': uploadKeyType,
|
|
665
|
+
},
|
|
666
|
+
};
|
|
667
|
+
const userlists =
|
|
668
|
+
await this.getReport(customerId, loginCustomerId, reportConfig);
|
|
669
|
+
return userlists.length === 0 ? undefined : userlists[0].user_list.id;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Creates the user list based on a given customerMatchConfig and returns the
|
|
674
|
+
* Id. The user list would be a CRM_BASED type.
|
|
675
|
+
* Trying to create a list with an used name will fail.
|
|
676
|
+
* @param {!CustomerMatchConfig} customerMatchConfig
|
|
677
|
+
* @return {number} The created user list id. Note this is not the resource
|
|
678
|
+
* name.
|
|
679
|
+
*/
|
|
680
|
+
async createCustomerMatchUserList(customerMatchConfig) {
|
|
681
|
+
const customerId = this.getCleanCid_(customerMatchConfig.customer_id);
|
|
682
|
+
const loginCustomerId = this.getCleanCid_(
|
|
683
|
+
customerMatchConfig.login_customer_id);
|
|
684
|
+
const listName = customerMatchConfig.list_name;
|
|
685
|
+
const uploadKeyType = customerMatchConfig.upload_key_type;
|
|
686
|
+
const userList = UserList.create({
|
|
687
|
+
name: listName,
|
|
688
|
+
type: UserListType.CRM_BASED,
|
|
689
|
+
crm_based_user_list: { upload_key_type: uploadKeyType },
|
|
690
|
+
});
|
|
691
|
+
const options = {
|
|
692
|
+
validate_only: this.debugMode, // when true makes no changes
|
|
693
|
+
partial_failure: true, // Will still create the non-failed entities
|
|
694
|
+
};
|
|
695
|
+
const customer = this.getGoogleAdsApiCustomer_(loginCustomerId, customerId);
|
|
696
|
+
const response = await customer.userLists.create([userList], options);
|
|
697
|
+
const { results, partial_failure_error: failed } = response;
|
|
698
|
+
if (this.logger.isDebugEnabled()) {
|
|
699
|
+
this.logger.debug(`Created crm userlist from`, customerMatchConfig);
|
|
700
|
+
}
|
|
701
|
+
if (failed) {
|
|
702
|
+
const failures = failed.errors.map(({ message }) => message).join(' ');
|
|
703
|
+
this.logger.info('partial_failure_error:', failures);
|
|
704
|
+
throw new Error(failures);
|
|
705
|
+
}
|
|
706
|
+
if (!results[0]) {
|
|
707
|
+
if (this.debugMode) {
|
|
708
|
+
throw new Error('No UserList was created in DEBUG mode.');
|
|
709
|
+
} else {
|
|
710
|
+
throw new Error('No UserList was created.');
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
const resourceName = results[0].resource_name;
|
|
714
|
+
const splitted = resourceName.split('/');
|
|
715
|
+
return splitted[splitted.length - 1];
|
|
716
|
+
}
|
|
717
|
+
|
|
627
718
|
/**
|
|
628
719
|
* Returns the function to send out a request to Google Ads API with
|
|
629
720
|
* user ids for Customer Match upload
|
|
@@ -792,8 +883,6 @@ class GoogleAds {
|
|
|
792
883
|
return OfflineUserDataJobStatus[jobs[0].offline_user_data_job.status];
|
|
793
884
|
}
|
|
794
885
|
|
|
795
|
-
//resource_name: 'customers/8368692804/offlineUserDataJobs/23130531867'
|
|
796
|
-
//'customers/8368692804/offlineUserDataJobs/23232922761'
|
|
797
886
|
/**
|
|
798
887
|
* Creates a OfflineUserDataJob and returns resource name.
|
|
799
888
|
* @param {OfflineUserDataJobConfig} config Offline user data job config.
|
|
@@ -818,7 +907,7 @@ class GoogleAds {
|
|
|
818
907
|
} else if (type.startsWith('STORE_SALES')) {
|
|
819
908
|
// If there is StoreSalesMetadata in the config
|
|
820
909
|
if (config.storeSalesMetadata) {
|
|
821
|
-
job.
|
|
910
|
+
job.store_sales_metadata = config.storeSalesMetadata;
|
|
822
911
|
}
|
|
823
912
|
} else {
|
|
824
913
|
throw new Error(`UNSUPPORTED OfflineUserDataJobType: ${type}.`);
|
|
@@ -829,14 +918,10 @@ class GoogleAds {
|
|
|
829
918
|
validate_only: this.debugMode, // when true makes no changes
|
|
830
919
|
enable_match_rate_range_preview: true,
|
|
831
920
|
});
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
return resourceName;
|
|
837
|
-
} catch (error) {
|
|
838
|
-
this.logger.error(error);
|
|
839
|
-
}
|
|
921
|
+
const { resource_name: resourceName } =
|
|
922
|
+
await customer.offlineUserDataJobs.createOfflineUserDataJob(request);
|
|
923
|
+
this.logger.info('Created OfflineUserDataJob:', resourceName);
|
|
924
|
+
return resourceName;
|
|
840
925
|
}
|
|
841
926
|
|
|
842
927
|
/**
|
|
@@ -860,12 +945,10 @@ class GoogleAds {
|
|
|
860
945
|
enable_partial_failure: true,
|
|
861
946
|
enable_warnings: true,
|
|
862
947
|
});
|
|
863
|
-
// console.log(request);
|
|
864
948
|
const response = await customer.
|
|
865
949
|
offlineUserDataJobs.addOfflineUserDataJobOperations(request);
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
console.log('Time used:', new Date().getTime() - start);
|
|
950
|
+
this.logger.debug(`Added ${records.length} records in (ms):`,
|
|
951
|
+
new Date().getTime() - start);
|
|
869
952
|
return response;
|
|
870
953
|
}
|
|
871
954
|
|
|
@@ -883,15 +966,11 @@ class GoogleAds {
|
|
|
883
966
|
resource_name: jobResourceName,
|
|
884
967
|
validate_only: false,//this.debugMode,
|
|
885
968
|
});
|
|
886
|
-
|
|
887
|
-
const rawResponse = await customer.
|
|
969
|
+
const rawResponse = await customer.
|
|
888
970
|
offlineUserDataJobs.runOfflineUserDataJob(request);
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
} catch (e) {
|
|
893
|
-
console.log(e);
|
|
894
|
-
}
|
|
971
|
+
const response = lodash.pick(rawResponse, ['name', 'done', 'error']);
|
|
972
|
+
this.logger.debug('runOfflineUserDataJob response: ', response);
|
|
973
|
+
return response;
|
|
895
974
|
}
|
|
896
975
|
|
|
897
976
|
/**
|