@google-cloud/nodejs-common 1.4.0 → 1.5.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.
- package/index.js +1 -0
- package/package.json +2 -1
- package/src/apis/ads_data_hub.js +14 -3
- package/src/apis/analytics.js +27 -12
- package/src/apis/auth_client.js +133 -70
- package/src/apis/cloud_platform_apis.js +13 -3
- package/src/apis/dfa_reporting.js +50 -24
- package/src/apis/doubleclick_bidmanager.js +26 -10
- package/src/apis/doubleclick_search.js +38 -15
- package/src/apis/google_ads.js +43 -28
- package/src/apis/spreadsheets.js +28 -15
- package/src/apis/youtube.js +32 -28
- package/src/components/pubsub.js +27 -1
- package/src/components/secret_manager.js +54 -0
package/index.js
CHANGED
|
@@ -24,6 +24,7 @@ exports.automl = require('./src/components/automl.js');
|
|
|
24
24
|
exports.firestore = require('./src/components/firestore/index.js');
|
|
25
25
|
exports.pubsub = require('./src/components/pubsub.js');
|
|
26
26
|
exports.scheduler = require('./src/components/scheduler.js');
|
|
27
|
+
exports.secretmanager = require('./src/components/secret_manager.js');
|
|
27
28
|
exports.storage = require('./src/components/storage.js');
|
|
28
29
|
exports.utils = require('./src/components/utils.js');
|
|
29
30
|
exports.vertexai = require('./src/components/vertex_ai.js');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@google-cloud/nodejs-common",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "A NodeJs common library for solutions based on Cloud Functions",
|
|
5
5
|
"author": "Google Inc.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"@google-cloud/pubsub": "^3.2.0",
|
|
26
26
|
"@google-cloud/storage": "^6.5.2",
|
|
27
27
|
"@google-cloud/scheduler": "^3.0.4",
|
|
28
|
+
"@google-cloud/secret-manager": "^4.1.3",
|
|
28
29
|
"gaxios": "^5.0.2",
|
|
29
30
|
"google-ads-api": "^11.1.0",
|
|
30
31
|
"google-ads-node": "^9.1.0",
|
package/src/apis/ads_data_hub.js
CHANGED
|
@@ -41,12 +41,22 @@ class AdsDataHub {
|
|
|
41
41
|
* variables.
|
|
42
42
|
*/
|
|
43
43
|
constructor(options, customerId = undefined, env = process.env) {
|
|
44
|
-
|
|
45
|
-
this.auth = authClient.getDefaultAuth();
|
|
44
|
+
this.authClient = new AuthClient(API_SCOPES, env);
|
|
46
45
|
/** @const{GaxiosOptions} */ this.options = options || {};
|
|
47
46
|
/** @const{string|undefined=} */ this.customerId = customerId;
|
|
48
47
|
}
|
|
49
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Gets the auth object.
|
|
51
|
+
* @return {!Promise<{!OAuth2Client|!JWT|!Compute}>}
|
|
52
|
+
*/
|
|
53
|
+
async getAuth_() {
|
|
54
|
+
if (this.auth) return this.auth;
|
|
55
|
+
await this.authClient.prepareCredentials();
|
|
56
|
+
this.auth = this.authClient.getDefaultAuth();
|
|
57
|
+
return this.auth;
|
|
58
|
+
}
|
|
59
|
+
|
|
50
60
|
/**
|
|
51
61
|
* Query name has the form
|
|
52
62
|
* 'customers/[customerId]/analysisQueries/[resource_id]'. For better
|
|
@@ -82,7 +92,8 @@ class AdsDataHub {
|
|
|
82
92
|
* @private
|
|
83
93
|
*/
|
|
84
94
|
async sendRequestAndReturnResponse_(path, method = 'GET', data = undefined) {
|
|
85
|
-
const
|
|
95
|
+
const auth = await this.getAuth_();
|
|
96
|
+
const headers = await auth.getRequestHeaders();
|
|
86
97
|
const url = this.getRequestBaseUrl_() + path;
|
|
87
98
|
const response = await request(/** @type {GaxiosOptions} */
|
|
88
99
|
Object.assign({}, this.options, {
|
package/src/apis/analytics.js
CHANGED
|
@@ -62,15 +62,24 @@ class Analytics {
|
|
|
62
62
|
* variables.
|
|
63
63
|
*/
|
|
64
64
|
constructor(env = process.env) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
this.authClient = new AuthClient(API_SCOPES, env);
|
|
66
|
+
this.logger = getLogger('API.GA');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Prepares the Google Analytics instace.
|
|
71
|
+
* @return {!google.analytics}
|
|
72
|
+
* @private
|
|
73
|
+
*/
|
|
74
|
+
async getApiClient_() {
|
|
75
|
+
if (this.analytics) return this.analytics;
|
|
76
|
+
await this.authClient.prepareCredentials();
|
|
77
|
+
this.logger.debug(`Initialized ${this.constructor.name} instance.`);
|
|
78
|
+
this.analytics = google.analytics({
|
|
69
79
|
version: API_VERSION,
|
|
70
|
-
auth,
|
|
80
|
+
auth: this.authClient.getDefaultAuth(),
|
|
71
81
|
});
|
|
72
|
-
this.
|
|
73
|
-
this.logger.debug(`Init ${this.constructor.name} with Debug Mode.`);
|
|
82
|
+
return this.analytics;
|
|
74
83
|
}
|
|
75
84
|
|
|
76
85
|
/**
|
|
@@ -92,7 +101,9 @@ class Analytics {
|
|
|
92
101
|
}
|
|
93
102
|
},
|
|
94
103
|
config);
|
|
95
|
-
|
|
104
|
+
|
|
105
|
+
const analytics = await this.getApiClient_();
|
|
106
|
+
const response = await analytics.management.uploads.uploadData(
|
|
96
107
|
uploadConfig);
|
|
97
108
|
this.logger.debug('Configuration: ', config);
|
|
98
109
|
this.logger.debug('Upload Data: ', data);
|
|
@@ -140,7 +151,8 @@ class Analytics {
|
|
|
140
151
|
* @return {!Promise<!Schema$Upload>} Updated data import Job status.
|
|
141
152
|
*/
|
|
142
153
|
async checkJobStatus(jobConfig) {
|
|
143
|
-
const
|
|
154
|
+
const analytics = await this.getApiClient_();
|
|
155
|
+
const { data: job } = await analytics.management.uploads.get(jobConfig);
|
|
144
156
|
if (job.status !== 'PENDING') return job;
|
|
145
157
|
this.logger.debug(
|
|
146
158
|
`GA Data Import Job[${jobConfig.uploadId}] is not finished.`);
|
|
@@ -157,7 +169,8 @@ class Analytics {
|
|
|
157
169
|
* @return {!Promise<!Array<string>>}
|
|
158
170
|
*/
|
|
159
171
|
async listAccounts() {
|
|
160
|
-
const
|
|
172
|
+
const analytics = await this.getApiClient_();
|
|
173
|
+
const response = await analytics.management.accounts.list();
|
|
161
174
|
return response.data.items.map(
|
|
162
175
|
(account) => `Account id: ${account.name}[${account.id}]`
|
|
163
176
|
);
|
|
@@ -169,7 +182,8 @@ class Analytics {
|
|
|
169
182
|
* @return {!Promise<!Array<Object>>}
|
|
170
183
|
*/
|
|
171
184
|
async listUploads(config) {
|
|
172
|
-
const
|
|
185
|
+
const analytics = await this.getApiClient_();
|
|
186
|
+
const response = await analytics.management.uploads.list(config);
|
|
173
187
|
return response.data.items;
|
|
174
188
|
}
|
|
175
189
|
|
|
@@ -189,7 +203,8 @@ class Analytics {
|
|
|
189
203
|
const request = Object.assign({}, config, {
|
|
190
204
|
resource: {customDataImportUids},
|
|
191
205
|
});
|
|
192
|
-
await this.
|
|
206
|
+
const analytics = await this.getApiClient_();
|
|
207
|
+
await analytics.management.uploads.deleteUploadData(request);
|
|
193
208
|
this.logger.debug('Delete uploads: ', customDataImportUids);
|
|
194
209
|
}
|
|
195
210
|
|
package/src/apis/auth_client.js
CHANGED
|
@@ -22,7 +22,12 @@
|
|
|
22
22
|
const fs = require('fs');
|
|
23
23
|
const path = require('path');
|
|
24
24
|
const {GoogleAuth, OAuth2Client, JWT, Compute} = require('google-auth-library');
|
|
25
|
-
|
|
25
|
+
const { SecretManager } = require('../components/secret_manager.js');
|
|
26
|
+
const { getLogger } = require('../components/utils.js');
|
|
27
|
+
|
|
28
|
+
/** Environment variable name for Secret Manager secret name. */
|
|
29
|
+
const DEFAULT_ENV_SECRET = 'SECRET_NAME';
|
|
30
|
+
/** Environment variable name for OAuth2 token file location. */
|
|
26
31
|
const DEFAULT_ENV_OAUTH = 'OAUTH2_TOKEN_JSON';
|
|
27
32
|
/** Environment variable name for Service account key file location. */
|
|
28
33
|
const DEFAULT_ENV_KEYFILE = 'API_SERVICE_ACCOUNT';
|
|
@@ -37,15 +42,25 @@ const DEFAULT_ENV_KEYFILE = 'API_SERVICE_ACCOUNT';
|
|
|
37
42
|
*
|
|
38
43
|
* There are two use cases for this authentication helper class:
|
|
39
44
|
* 1. The user only has OAuth access due to some reasons, so ADC can't be used;
|
|
40
|
-
* 2.
|
|
41
|
-
*
|
|
42
|
-
*
|
|
45
|
+
* 2. User-managed service account is requried for external APIs for some
|
|
46
|
+
* specific considerations, e.g. security. In this case, a file based key file
|
|
47
|
+
* can be used to generate a JWT auth client.
|
|
43
48
|
*
|
|
44
|
-
* To solve these challenges, this class tries to probe the
|
|
45
|
-
*
|
|
49
|
+
* To solve these challenges, this class tries to probe the settings from
|
|
50
|
+
* enviroment variables, starts from the name of secret (Secret Manager), OAuth
|
|
51
|
+
* token file (deprecated), then service account key file (deprecated). It will
|
|
46
52
|
* fallback to ADC if those probing failed.
|
|
53
|
+
* Note, Secret Manager is the recommanded way to store tokens because it is a
|
|
54
|
+
* secure and convenient central storage system to manage access across Google
|
|
55
|
+
* Cloud.
|
|
56
|
+
*
|
|
57
|
+
* The recommended environment variable is:
|
|
58
|
+
* SECRET_NAME: the name of secret. The secret can be a oauth token file or a
|
|
59
|
+
* service account key file. This env var is used to offer a global auth for a
|
|
60
|
+
* solution. If different auths are required, the value of passed `env` can be
|
|
61
|
+
* set
|
|
47
62
|
*
|
|
48
|
-
*
|
|
63
|
+
* Alternative environment variable but not recommended for prod environment:
|
|
49
64
|
* OAUTH2_TOKEN_JSON : the oauth token key files, refresh token and proper API
|
|
50
65
|
* scopes are expected here.
|
|
51
66
|
* API_SERVICE_ACCOUNT : the service account key file. The email in the key file
|
|
@@ -55,35 +70,81 @@ class AuthClient {
|
|
|
55
70
|
/**
|
|
56
71
|
* Create a new instance with given API scopes.
|
|
57
72
|
* @param {string|!Array<string>|!ReadonlyArray<string>} scopes
|
|
73
|
+
* @param {!Object<string,string>=} overwrittenEnv The key-value pairs to
|
|
74
|
+
* over write env variables.
|
|
75
|
+
*/
|
|
76
|
+
constructor(scopes, overwrittenEnv = {}) {
|
|
77
|
+
this.logger = getLogger('AUTH');
|
|
78
|
+
this.scopes = scopes;
|
|
79
|
+
this.env = Object.assign({}, process.env, overwrittenEnv);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Prepares the `oauthToken` object and/or `serviceAccountKey` based on the
|
|
84
|
+
* settings in enviroment object.
|
|
85
|
+
* A secret name is preferred to offer the token of the OAuth or key of a
|
|
86
|
+
* service account.
|
|
87
|
+
* To be compatible, this function also checks the env for oauth token file
|
|
88
|
+
* and service account key file if there is no secret name was set in the env.
|
|
89
|
+
*/
|
|
90
|
+
async prepareCredentials() {
|
|
91
|
+
if (this.env[DEFAULT_ENV_SECRET]) {
|
|
92
|
+
const secretmanager = new SecretManager({
|
|
93
|
+
projectId: this.env.GCP_PROJECT,
|
|
94
|
+
});
|
|
95
|
+
const secret = await secretmanager.access(this.env[DEFAULT_ENV_SECRET]);
|
|
96
|
+
if (secret) {
|
|
97
|
+
const secretObj = JSON.parse(secret);
|
|
98
|
+
if (secretObj.token) {
|
|
99
|
+
this.oauthToken = secretObj;
|
|
100
|
+
} else {
|
|
101
|
+
this.serviceAccountKey = secretObj;
|
|
102
|
+
}
|
|
103
|
+
this.logger.info(`Get secret from SM ${this.env[DEFAULT_ENV_SECRET]}.`);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
this.logger.warn(`Cannot find SM ${this.env[DEFAULT_ENV_SECRET]}.`);
|
|
107
|
+
}
|
|
108
|
+
// To be compatible with previous solution.
|
|
109
|
+
const oauthTokenFile = this.getContentFromEnvVar(DEFAULT_ENV_OAUTH);
|
|
110
|
+
if (oauthTokenFile) {
|
|
111
|
+
this.oauthToken = JSON.parse(oauthTokenFile);
|
|
112
|
+
}
|
|
113
|
+
const serviceAccountKeyFile =
|
|
114
|
+
this.getContentFromEnvVar(DEFAULT_ENV_KEYFILE);
|
|
115
|
+
if (serviceAccountKeyFile) {
|
|
116
|
+
this.serviceAccountKey = JSON.parse(serviceAccountKeyFile);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Factory method to offer a prepared AuthClient intance in an async way.
|
|
122
|
+
* @param {string|!Array<string>|!ReadonlyArray<string>} scopes
|
|
58
123
|
* @param {!Object<string,string>=} env The environment object to hold env
|
|
59
124
|
* variables.
|
|
125
|
+
* @return {!Promise<!AuthClient>}
|
|
60
126
|
*/
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
127
|
+
static async build(scopes, env) {
|
|
128
|
+
const instance = new AuthClient(scopes, env);
|
|
129
|
+
await instance.prepareCredentials();
|
|
130
|
+
return instance;
|
|
65
131
|
}
|
|
66
132
|
|
|
67
133
|
/**
|
|
68
134
|
* Generates an authentication client of OAuth, JWT or ADC based on the
|
|
69
|
-
* environment settings. The authentication method is determined by
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* 2. JWT, return JWT client if a user managed service account key file is
|
|
74
|
-
* available.
|
|
135
|
+
* environment settings. The authentication method is determined by the type
|
|
136
|
+
* of available credentials:
|
|
137
|
+
* 1. OAuth, return an OAuth token if available.
|
|
138
|
+
* 2. JWT, return JWT client if a service account key is available.
|
|
75
139
|
* 3. ADC if none of these files exists.
|
|
76
140
|
* @return {!OAuth2Client|!JWT|!Compute}
|
|
77
141
|
*/
|
|
78
142
|
getDefaultAuth() {
|
|
79
|
-
if (typeof this.
|
|
80
|
-
console.log(`Auth mode OAUTH: ${this.oauthTokenFile}`);
|
|
143
|
+
if (typeof this.oauthToken !== 'undefined') {
|
|
81
144
|
return this.getOAuth2Client();
|
|
82
|
-
} else if (typeof this.
|
|
83
|
-
console.log(`Auth mode JWT: ${this.serviceAccountKeyFile}`);
|
|
145
|
+
} else if (typeof this.serviceAccountKey !== 'undefined') {
|
|
84
146
|
return this.getServiceAccount();
|
|
85
147
|
} else {
|
|
86
|
-
console.log(`Auth mode ADC`);
|
|
87
148
|
return this.getApplicationDefaultCredentials();
|
|
88
149
|
}
|
|
89
150
|
}
|
|
@@ -100,33 +161,36 @@ class AuthClient {
|
|
|
100
161
|
* @return {!Compute|!JWT}
|
|
101
162
|
*/
|
|
102
163
|
getApplicationDefaultCredentials() {
|
|
164
|
+
this.logger.info(`Mode ADC`);
|
|
103
165
|
return new GoogleAuth({scopes: this.scopes});
|
|
104
166
|
}
|
|
105
167
|
|
|
106
168
|
/**
|
|
107
169
|
* Returns an OAuth2 client based on the given key file.
|
|
108
|
-
* @param {string=} keyFile Full path for the OAuth key file.
|
|
109
|
-
* `client_id`, `client_secret`, `tokens` are expected in that JSON file.
|
|
110
170
|
* @return {!OAuth2Client}
|
|
111
171
|
*/
|
|
112
|
-
getOAuth2Client(
|
|
113
|
-
this.
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
oAuth2Client.setCredentials({refresh_token: key.token.refresh_token});
|
|
172
|
+
getOAuth2Client() {
|
|
173
|
+
this.ensureCredentialExists_(this.oauthToken, 'OAuth token');
|
|
174
|
+
const { client_id, client_secret, token } = this.oauthToken;
|
|
175
|
+
const oAuth2Client = new OAuth2Client(client_id, client_secret);
|
|
176
|
+
oAuth2Client.setCredentials({ refresh_token: token.refresh_token });
|
|
118
177
|
return oAuth2Client;
|
|
119
178
|
}
|
|
120
179
|
|
|
121
180
|
/**
|
|
122
181
|
* Returns a JWT client based on the given service account key file.
|
|
123
|
-
* @param {string=} keyFile Full path for the service account key file.
|
|
124
182
|
* @return {!JWT}
|
|
125
183
|
*/
|
|
126
|
-
getServiceAccount(
|
|
127
|
-
this.
|
|
128
|
-
|
|
129
|
-
|
|
184
|
+
getServiceAccount() {
|
|
185
|
+
this.ensureCredentialExists_(this.serviceAccountKey, 'Service Account key');
|
|
186
|
+
this.logger.info(`Mode JWT`);
|
|
187
|
+
const { private_key_id, private_key, client_email } = this.serviceAccountKey;
|
|
188
|
+
return new JWT({
|
|
189
|
+
email: client_email,
|
|
190
|
+
key: private_key,
|
|
191
|
+
keyId: private_key_id,
|
|
192
|
+
scopes: this.scopes,
|
|
193
|
+
});
|
|
130
194
|
}
|
|
131
195
|
|
|
132
196
|
/**
|
|
@@ -134,58 +198,57 @@ class AuthClient {
|
|
|
134
198
|
* OAuth2 based on the given key file.
|
|
135
199
|
* Some API library (google-ads-api) doesn't support google-auth-library
|
|
136
200
|
* directly and needs plain keys.
|
|
137
|
-
* @param {string=} keyFile Full path for the OAuth key file.
|
|
138
|
-
* `client_id`, `client_secret`, `tokens` are expected in that JSON file.
|
|
139
201
|
* @return {{
|
|
140
202
|
* clientId:string,
|
|
141
203
|
* clientSecret:string,
|
|
142
204
|
* refreshToken:string,
|
|
143
205
|
* }}
|
|
144
206
|
*/
|
|
145
|
-
getOAuth2Token(
|
|
146
|
-
this.
|
|
147
|
-
|
|
207
|
+
getOAuth2Token() {
|
|
208
|
+
this.ensureCredentialExists_(this.oauthToken, 'OAuth token');
|
|
209
|
+
this.logger.info(`Mode OAUTH`);
|
|
210
|
+
const { client_id, client_secret, token } = this.oauthToken;
|
|
148
211
|
return {
|
|
149
|
-
clientId:
|
|
150
|
-
clientSecret:
|
|
151
|
-
refreshToken:
|
|
212
|
+
clientId: client_id,
|
|
213
|
+
clientSecret: client_secret,
|
|
214
|
+
refreshToken: token.refresh_token,
|
|
152
215
|
};
|
|
153
216
|
}
|
|
154
217
|
|
|
155
218
|
/**
|
|
156
219
|
* Some APIs only support one authorization type, so we have to designate a
|
|
157
|
-
* specific auth method. If the related
|
|
158
|
-
* auth
|
|
159
|
-
*
|
|
220
|
+
* specific auth method rather than the 'getDefaultAuth'. If the related
|
|
221
|
+
* key/token is not available, the auth should fail. The function throws
|
|
222
|
+
* errors with a meaningful message.
|
|
223
|
+
* @param {object} credential - Credential object.
|
|
160
224
|
* @param {string} type Key type name, 'OAuth token' or 'Service Account key'
|
|
161
225
|
* @private
|
|
162
226
|
*/
|
|
163
|
-
|
|
164
|
-
if (!
|
|
227
|
+
ensureCredentialExists_(credential, type) {
|
|
228
|
+
if (!credential) throw new Error(`Required ${type} does not exist.`);
|
|
165
229
|
}
|
|
166
|
-
}
|
|
167
230
|
|
|
168
|
-
/**
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
231
|
+
/**
|
|
232
|
+
* Returns the content of a file whose path is set as an env variable.
|
|
233
|
+
* The path can be relative or absolute. The function will append current path
|
|
234
|
+
* before the relative path.
|
|
235
|
+
* @param {string} varName The name of environment variable for the file path.
|
|
236
|
+
* @return {?string} Content of the file what set as an environment variable.
|
|
237
|
+
*/
|
|
238
|
+
getContentFromEnvVar(varName) {
|
|
239
|
+
const value = this.env[varName];
|
|
240
|
+
if (value) {
|
|
241
|
+
const fullPath = value.startsWith('/')
|
|
242
|
+
? value
|
|
243
|
+
: path.join(__dirname, value);
|
|
244
|
+
if (fs.existsSync(fullPath)) {
|
|
245
|
+
this.logger.info(`Find file '${fullPath}' as [${varName}] in env.`);
|
|
246
|
+
return fs.readFileSync(fullPath).toString();
|
|
247
|
+
} else {
|
|
248
|
+
this.logger.error(
|
|
249
|
+
`Cannot find '${fullPath}' which is as env[${varName}].`
|
|
250
|
+
);
|
|
251
|
+
}
|
|
189
252
|
}
|
|
190
253
|
}
|
|
191
254
|
}
|
|
@@ -34,11 +34,21 @@ const API_VERSION = 'v1';
|
|
|
34
34
|
class CloudPlatformApis {
|
|
35
35
|
constructor(env = process.env) {
|
|
36
36
|
/** @const {!AuthClient} */
|
|
37
|
-
|
|
38
|
-
this.auth = authClient.getDefaultAuth();
|
|
37
|
+
this.authClient = new AuthClient(API_SCOPES, env);
|
|
39
38
|
this.projectId = env['GCP_PROJECT'];
|
|
40
39
|
}
|
|
41
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Gets the auth object.
|
|
43
|
+
* @return {!Promise<{!OAuth2Client|!JWT|!Compute}>}
|
|
44
|
+
*/
|
|
45
|
+
async getAuth_() {
|
|
46
|
+
if (this.auth) return this.auth;
|
|
47
|
+
await this.authClient.prepareCredentials();
|
|
48
|
+
this.auth = this.authClient.getDefaultAuth();
|
|
49
|
+
return this.auth;
|
|
50
|
+
}
|
|
51
|
+
|
|
42
52
|
/**
|
|
43
53
|
* Gets the GCP project Id. In Cloud Functions, it *should* be passed in
|
|
44
54
|
* through environment variable during the deployment. But if it doesn't exist
|
|
@@ -67,7 +77,7 @@ class CloudPlatformApis {
|
|
|
67
77
|
async testIamPermissions(permissions) {
|
|
68
78
|
const resourceManager = google.cloudresourcemanager({
|
|
69
79
|
version: API_VERSION,
|
|
70
|
-
auth: this.
|
|
80
|
+
auth: await this.getAuth_(),
|
|
71
81
|
});
|
|
72
82
|
const projectId = await this.getProjectId_();
|
|
73
83
|
const request = {
|
|
@@ -34,7 +34,7 @@ const API_SCOPES = Object.freeze([
|
|
|
34
34
|
'https://www.googleapis.com/auth/dfareporting',
|
|
35
35
|
'https://www.googleapis.com/auth/dfatrafficking',
|
|
36
36
|
]);
|
|
37
|
-
const API_VERSION = '
|
|
37
|
+
const API_VERSION = 'v4';
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
40
|
* Configuration for preparing conversions for Campaign Manager, includes:
|
|
@@ -65,7 +65,7 @@ let InsertConversionsConfig;
|
|
|
65
65
|
/**
|
|
66
66
|
* List of properties that will be take from the data file as elements of a
|
|
67
67
|
* conversion.
|
|
68
|
-
* See https://developers.google.com/doubleclick-advertisers/rest/
|
|
68
|
+
* See https://developers.google.com/doubleclick-advertisers/rest/v4/Conversion
|
|
69
69
|
* @type {Array<string>}
|
|
70
70
|
*/
|
|
71
71
|
const PICKED_PROPERTIES = [
|
|
@@ -87,14 +87,34 @@ class DfaReporting {
|
|
|
87
87
|
* variables.
|
|
88
88
|
*/
|
|
89
89
|
constructor(env = process.env) {
|
|
90
|
-
|
|
91
|
-
this.
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
this.authClient = new AuthClient(API_SCOPES, env);
|
|
91
|
+
this.logger = getLogger('API.CM');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Prepares the Google DfaReport API instance.
|
|
96
|
+
* @return {!google.dfareporting}
|
|
97
|
+
* @private
|
|
98
|
+
*/
|
|
99
|
+
async getApiClient_() {
|
|
100
|
+
if (this.dfareporting) return this.dfareporting;
|
|
101
|
+
this.logger.debug(`Initialized ${this.constructor.name} instance.`);
|
|
102
|
+
this.dfareporting = google.dfareporting({
|
|
94
103
|
version: API_VERSION,
|
|
95
|
-
auth: this.
|
|
104
|
+
auth: await this.getAuth_(),
|
|
96
105
|
});
|
|
97
|
-
this.
|
|
106
|
+
return this.dfareporting;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Gets the auth object.
|
|
111
|
+
* @return {!Promise<{!OAuth2Client|!JWT|!Compute}>}
|
|
112
|
+
*/
|
|
113
|
+
async getAuth_() {
|
|
114
|
+
if (this.auth) return this.auth;
|
|
115
|
+
await this.authClient.prepareCredentials();
|
|
116
|
+
this.auth = this.authClient.getDefaultAuth();
|
|
117
|
+
return this.auth;
|
|
98
118
|
}
|
|
99
119
|
|
|
100
120
|
/**
|
|
@@ -105,13 +125,14 @@ class DfaReporting {
|
|
|
105
125
|
* @return {!Promise<string>}
|
|
106
126
|
*/
|
|
107
127
|
async getProfileId(accountId) {
|
|
108
|
-
const
|
|
128
|
+
const dfareporting = await this.getApiClient_();
|
|
129
|
+
const { data: { items } } = await dfareporting.userProfiles.list();
|
|
109
130
|
const profiles = items.filter(
|
|
110
131
|
(profile) => profile.accountId === accountId
|
|
111
132
|
);
|
|
112
133
|
if (profiles.length === 0) {
|
|
113
|
-
throw new Error(
|
|
114
|
-
|
|
134
|
+
throw new Error(
|
|
135
|
+
`Failed to find profile of current user for CM account ${accountId}`);
|
|
115
136
|
} else {
|
|
116
137
|
const {profileId, userName, accountId, accountName,} = profiles[0];
|
|
117
138
|
this.logger.debug(`Find UserProfile: ${profileId}[${userName}] for`
|
|
@@ -166,7 +187,8 @@ class DfaReporting {
|
|
|
166
187
|
numberOfLines: lines.length,
|
|
167
188
|
};
|
|
168
189
|
try {
|
|
169
|
-
const
|
|
190
|
+
const dfareporting = await this.getApiClient_();
|
|
191
|
+
const response = await dfareporting.conversions.batchinsert({
|
|
170
192
|
profileId: config.profileId,
|
|
171
193
|
requestBody: requestBody,
|
|
172
194
|
});
|
|
@@ -194,9 +216,9 @@ class DfaReporting {
|
|
|
194
216
|
* ConversionStatus object. This function extras failed lines and error
|
|
195
217
|
* messages based on the 'errors'.
|
|
196
218
|
* For 'ConversionStatus', see:
|
|
197
|
-
* https://developers.google.com/doubleclick-advertisers/rest/
|
|
219
|
+
* https://developers.google.com/doubleclick-advertisers/rest/v4/ConversionStatus
|
|
198
220
|
* For 'ConversionError', see:
|
|
199
|
-
* https://developers.google.com/doubleclick-advertisers/rest/
|
|
221
|
+
* https://developers.google.com/doubleclick-advertisers/rest/v4/ConversionStatus#ConversionError
|
|
200
222
|
* @param {!BatchResult} batchResult
|
|
201
223
|
* @param {!Array<!Schema$ConversionStatus>} statuses
|
|
202
224
|
* @param {!Array<string>} lines The original input data.
|
|
@@ -232,7 +254,8 @@ class DfaReporting {
|
|
|
232
254
|
* @return {!Promise<!Array<string>>}
|
|
233
255
|
*/
|
|
234
256
|
async listUserProfiles() {
|
|
235
|
-
const
|
|
257
|
+
const dfareporting = await this.getApiClient_();
|
|
258
|
+
const { data: { items } } = await dfareporting.userProfiles.list();
|
|
236
259
|
return items.map(({profileId, userName, accountId, accountName}) => {
|
|
237
260
|
return `Profile: ${profileId}[${userName}] `
|
|
238
261
|
+ `Account: ${accountId}[${accountName}]`;
|
|
@@ -261,7 +284,7 @@ class DfaReporting {
|
|
|
261
284
|
* Runs a report and return the file Id. As an asynchronized process, the
|
|
262
285
|
* returned file Id will be a placeholder until the status changes to
|
|
263
286
|
* 'REPORT_AVAILABLE' in the response of `getFile`.
|
|
264
|
-
* @see https://developers.google.com/doubleclick-advertisers/rest/
|
|
287
|
+
* @see https://developers.google.com/doubleclick-advertisers/rest/v4/reports/run
|
|
265
288
|
*
|
|
266
289
|
* @param {{
|
|
267
290
|
* accountId:(string|undefined),
|
|
@@ -272,7 +295,8 @@ class DfaReporting {
|
|
|
272
295
|
*/
|
|
273
296
|
async runReport(config) {
|
|
274
297
|
const profileId = await this.getProfileForOperation_(config);
|
|
275
|
-
const
|
|
298
|
+
const dfareporting = await this.getApiClient_();
|
|
299
|
+
const response = await dfareporting.reports.run({
|
|
276
300
|
profileId,
|
|
277
301
|
reportId: config.reportId,
|
|
278
302
|
synchronous: false,
|
|
@@ -282,9 +306,9 @@ class DfaReporting {
|
|
|
282
306
|
|
|
283
307
|
/**
|
|
284
308
|
* Returns file url from a report. If the report status is 'REPORT_AVAILABLE',
|
|
285
|
-
* then return the apiUrl from the response; if the status is 'PROCESSING'
|
|
286
|
-
* returns undefined; otherwise throws an error.
|
|
287
|
-
* @see https://developers.google.com/doubleclick-advertisers/rest/
|
|
309
|
+
* then return the apiUrl from the response; if the status is 'PROCESSING' or
|
|
310
|
+
* 'QUEUED', returns undefined as it is unfinished; otherwise throws an error.
|
|
311
|
+
* @see https://developers.google.com/doubleclick-advertisers/rest/v4/reports/get
|
|
288
312
|
*
|
|
289
313
|
* @param {{
|
|
290
314
|
* accountId:(string|undefined),
|
|
@@ -296,13 +320,14 @@ class DfaReporting {
|
|
|
296
320
|
*/
|
|
297
321
|
async getReportFileUrl(config) {
|
|
298
322
|
const profileId = await this.getProfileForOperation_(config);
|
|
299
|
-
const
|
|
323
|
+
const dfareporting = await this.getApiClient_();
|
|
324
|
+
const response = await dfareporting.reports.files.get({
|
|
300
325
|
profileId,
|
|
301
326
|
reportId: config.reportId,
|
|
302
327
|
fileId: config.fileId,
|
|
303
328
|
});
|
|
304
|
-
const {data} = response;
|
|
305
|
-
if (data.status === 'PROCESSING') return;
|
|
329
|
+
const { data } = response;
|
|
330
|
+
if (data.status === 'PROCESSING' || data.status === 'QUEUED') return;
|
|
306
331
|
if (data.status === 'REPORT_AVAILABLE') return data.urls.apiUrl;
|
|
307
332
|
throw new Error(`Unsupported report status: ${data.status}`);
|
|
308
333
|
}
|
|
@@ -314,7 +339,8 @@ class DfaReporting {
|
|
|
314
339
|
* @return {!Promise<string>}
|
|
315
340
|
*/
|
|
316
341
|
async downloadReportFile(url) {
|
|
317
|
-
const
|
|
342
|
+
const auth = await this.getAuth_();
|
|
343
|
+
const headers = await auth.getRequestHeaders();
|
|
318
344
|
const response = await request({
|
|
319
345
|
method: 'GET',
|
|
320
346
|
headers,
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
const {google} = require('googleapis');
|
|
23
23
|
const AuthClient = require('./auth_client.js');
|
|
24
|
+
const { getLogger } = require('../components/utils.js');
|
|
24
25
|
|
|
25
26
|
const API_SCOPES = Object.freeze([
|
|
26
27
|
'https://www.googleapis.com/auth/doubleclickbidmanager',
|
|
@@ -62,13 +63,24 @@ class DoubleClickBidManager {
|
|
|
62
63
|
* variables.
|
|
63
64
|
*/
|
|
64
65
|
constructor(env = process.env) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
this.authClient = new AuthClient(API_SCOPES, env);
|
|
67
|
+
this.logger = getLogger('API.DV3');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Prepares the Google DBM instance.
|
|
72
|
+
* @return {!google.doubleclickbidmanager}
|
|
73
|
+
* @private
|
|
74
|
+
*/
|
|
75
|
+
async getApiClient_() {
|
|
76
|
+
if (this.doubleclickbidmanager) return this.doubleclickbidmanager;
|
|
77
|
+
await this.authClient.prepareCredentials();
|
|
78
|
+
this.logger.debug(`Initialized ${this.constructor.name} instance.`);
|
|
79
|
+
this.doubleclickbidmanager = google.doubleclickbidmanager({
|
|
69
80
|
version: API_VERSION,
|
|
70
|
-
auth,
|
|
81
|
+
auth: this.authClient.getDefaultAuth(),
|
|
71
82
|
});
|
|
83
|
+
return this.doubleclickbidmanager;
|
|
72
84
|
}
|
|
73
85
|
|
|
74
86
|
/**
|
|
@@ -80,7 +92,8 @@ class DoubleClickBidManager {
|
|
|
80
92
|
* @return {!Promise<boolean>} Whether it starts successfully.
|
|
81
93
|
*/
|
|
82
94
|
async runQuery(queryId, requestBody = undefined) {
|
|
83
|
-
const
|
|
95
|
+
const doubleclickbidmanager = await this.getApiClient_();
|
|
96
|
+
const response = await doubleclickbidmanager.queries.runquery(
|
|
84
97
|
{queryId, requestBody});
|
|
85
98
|
return response.status >= 200 && response.status < 300;
|
|
86
99
|
}
|
|
@@ -88,12 +101,13 @@ class DoubleClickBidManager {
|
|
|
88
101
|
/**
|
|
89
102
|
* Gets a query resource.
|
|
90
103
|
* See https://developers.google.com/bid-manager/v1.1/queries/getquery
|
|
91
|
-
* @param {number} queryId
|
|
104
|
+
* @param {number} queryId Id of the query.
|
|
92
105
|
* @return {!Promise<!QueryResource>} Query resource, see
|
|
93
106
|
* https://developers.google.com/bid-manager/v1.1/queries#resource
|
|
94
107
|
*/
|
|
95
108
|
async getQuery(queryId) {
|
|
96
|
-
const
|
|
109
|
+
const doubleclickbidmanager = await this.getApiClient_();
|
|
110
|
+
const response = await doubleclickbidmanager.queries.getquery({ queryId });
|
|
97
111
|
return response.data.metadata;
|
|
98
112
|
}
|
|
99
113
|
|
|
@@ -104,7 +118,8 @@ class DoubleClickBidManager {
|
|
|
104
118
|
* @return {!Promise<number>} Id of created query.
|
|
105
119
|
*/
|
|
106
120
|
async createQuery(query) {
|
|
107
|
-
const
|
|
121
|
+
const doubleclickbidmanager = await this.getApiClient_();
|
|
122
|
+
const response = await doubleclickbidmanager.queries.createquery(
|
|
108
123
|
{requestBody: query});
|
|
109
124
|
return response.data.queryId;
|
|
110
125
|
}
|
|
@@ -115,8 +130,9 @@ class DoubleClickBidManager {
|
|
|
115
130
|
* @return {!Promise<boolean>} Whether the query was deleted.
|
|
116
131
|
*/
|
|
117
132
|
async deleteQuery(queryId) {
|
|
133
|
+
const doubleclickbidmanager = await this.getApiClient_();
|
|
118
134
|
try {
|
|
119
|
-
await
|
|
135
|
+
await doubleclickbidmanager.queries.deletequery({ queryId });
|
|
120
136
|
return true;
|
|
121
137
|
} catch (error) {
|
|
122
138
|
console.error(error);
|
|
@@ -126,17 +126,34 @@ class DoubleClickSearch {
|
|
|
126
126
|
* variables.
|
|
127
127
|
*/
|
|
128
128
|
constructor(env = process.env) {
|
|
129
|
-
|
|
130
|
-
this.
|
|
131
|
-
|
|
132
|
-
|
|
129
|
+
this.authClient = new AuthClient(API_SCOPES, env);
|
|
130
|
+
this.logger = getLogger('API.DS');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Prepares the Google SA360 instance.
|
|
135
|
+
* @return {!google.doubleclicksearch}
|
|
136
|
+
* @private
|
|
137
|
+
*/
|
|
138
|
+
async getApiClient_() {
|
|
139
|
+
if (this.doubleclicksearch) return this.doubleclicksearch;
|
|
140
|
+
this.logger.debug(`Initialized ${this.constructor.name} instance.`);
|
|
141
|
+
this.doubleclicksearch = google.doubleclicksearch({
|
|
133
142
|
version: API_VERSION,
|
|
134
|
-
auth: this.
|
|
143
|
+
auth: await this.getAuth_(),
|
|
135
144
|
});
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
145
|
+
return this.doubleclicksearch;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Gets the auth object.
|
|
150
|
+
* @return {!Promise<{!OAuth2Client|!JWT|!Compute}>}
|
|
151
|
+
*/
|
|
152
|
+
async getAuth_() {
|
|
153
|
+
if (this.auth) return this.auth;
|
|
154
|
+
await this.authClient.prepareCredentials();
|
|
155
|
+
this.auth = this.authClient.getDefaultAuth();
|
|
156
|
+
return this.auth;
|
|
140
157
|
}
|
|
141
158
|
|
|
142
159
|
/**
|
|
@@ -155,7 +172,8 @@ class DoubleClickSearch {
|
|
|
155
172
|
});
|
|
156
173
|
this.logger.debug('Sending out availabilities', availabilities);
|
|
157
174
|
try {
|
|
158
|
-
const
|
|
175
|
+
const doubleclicksearch = await this.getApiClient_();
|
|
176
|
+
const response = await doubleclicksearch.conversion.updateAvailability(
|
|
159
177
|
{requestBody: {availabilities}});
|
|
160
178
|
this.logger.debug('Get response: ', response);
|
|
161
179
|
return response.status === 200;
|
|
@@ -203,7 +221,8 @@ class DoubleClickSearch {
|
|
|
203
221
|
numberOfLines: lines.length,
|
|
204
222
|
};
|
|
205
223
|
try {
|
|
206
|
-
const
|
|
224
|
+
const doubleclicksearch = await this.getApiClient_();
|
|
225
|
+
const response = await doubleclicksearch.conversion.insert(
|
|
207
226
|
{requestBody: {conversion: conversions}}
|
|
208
227
|
);
|
|
209
228
|
this.logger.debug('Response: ', response);
|
|
@@ -303,7 +322,8 @@ class DoubleClickSearch {
|
|
|
303
322
|
* @return {!Promise<string>}
|
|
304
323
|
*/
|
|
305
324
|
async requestReports(requestBody) {
|
|
306
|
-
const
|
|
325
|
+
const doubleclicksearch = await this.getApiClient_();
|
|
326
|
+
const { status, data } = await doubleclicksearch.reports.request({ requestBody });
|
|
307
327
|
if (status >= 200 && status < 300) {
|
|
308
328
|
return data.id;
|
|
309
329
|
}
|
|
@@ -321,7 +341,8 @@ class DoubleClickSearch {
|
|
|
321
341
|
* }>>}
|
|
322
342
|
*/
|
|
323
343
|
async getReportUrls(reportId) {
|
|
324
|
-
const
|
|
344
|
+
const doubleclicksearch = await this.getApiClient_();
|
|
345
|
+
const { status, data } = await doubleclicksearch.reports.get({ reportId });
|
|
325
346
|
switch (status) {
|
|
326
347
|
case 200:
|
|
327
348
|
const {rowCount, files} = data;
|
|
@@ -346,7 +367,8 @@ class DoubleClickSearch {
|
|
|
346
367
|
* @return {!Promise<string>}
|
|
347
368
|
*/
|
|
348
369
|
async getReportFile(reportId, reportFragment) {
|
|
349
|
-
const
|
|
370
|
+
const doubleclicksearch = await this.getApiClient_();
|
|
371
|
+
const response = await doubleclicksearch.reports.getFile(
|
|
350
372
|
{reportId, reportFragment});
|
|
351
373
|
if (response.status === 200) return response.data;
|
|
352
374
|
const errorMsg =
|
|
@@ -363,7 +385,8 @@ class DoubleClickSearch {
|
|
|
363
385
|
* @return {!Promise<ReadableStream>}
|
|
364
386
|
*/
|
|
365
387
|
async getReportFileStream(url) {
|
|
366
|
-
const
|
|
388
|
+
const auth = await this.getAuth_();
|
|
389
|
+
const headers = await auth.getRequestHeaders();
|
|
367
390
|
const response = await request({
|
|
368
391
|
method: 'GET',
|
|
369
392
|
headers,
|
package/src/apis/google_ads.js
CHANGED
|
@@ -251,14 +251,9 @@ class GoogleAds {
|
|
|
251
251
|
* variables.
|
|
252
252
|
*/
|
|
253
253
|
constructor(developerToken, debugMode = false, env = process.env) {
|
|
254
|
+
this.developerToken = developerToken;
|
|
254
255
|
this.debugMode = debugMode;
|
|
255
|
-
|
|
256
|
-
/** @const {GoogleAdsApi} */ this.apiClient = new GoogleAdsApi({
|
|
257
|
-
client_id: oauthClient.clientId,
|
|
258
|
-
client_secret: oauthClient.clientSecret,
|
|
259
|
-
developer_token: developerToken,
|
|
260
|
-
});
|
|
261
|
-
/** @const {string} */ this.refreshToken = oauthClient.refreshToken;
|
|
256
|
+
this.authClient = new AuthClient(API_SCOPES, env);
|
|
262
257
|
this.logger = getLogger('API.ADS');
|
|
263
258
|
this.logger.debug(`Init ${this.constructor.name} with Debug Mode.`);
|
|
264
259
|
}
|
|
@@ -272,7 +267,8 @@ class GoogleAds {
|
|
|
272
267
|
* @return {!ReadableStream}
|
|
273
268
|
*/
|
|
274
269
|
async getReport(customerId, loginCustomerId, reportQueryConfig) {
|
|
275
|
-
const customer = this.getGoogleAdsApiCustomer_(
|
|
270
|
+
const customer = await this.getGoogleAdsApiCustomer_(
|
|
271
|
+
loginCustomerId, customerId);
|
|
276
272
|
return customer.report(reportQueryConfig);
|
|
277
273
|
}
|
|
278
274
|
|
|
@@ -284,7 +280,8 @@ class GoogleAds {
|
|
|
284
280
|
* @return {!ReadableStream}
|
|
285
281
|
*/
|
|
286
282
|
async generatorReport(customerId, loginCustomerId, reportQueryConfig) {
|
|
287
|
-
const customer = this.getGoogleAdsApiCustomer_(
|
|
283
|
+
const customer = await this.getGoogleAdsApiCustomer_(
|
|
284
|
+
loginCustomerId, customerId);
|
|
288
285
|
return customer.reportStream(reportQueryConfig);
|
|
289
286
|
}
|
|
290
287
|
|
|
@@ -296,7 +293,8 @@ class GoogleAds {
|
|
|
296
293
|
* @return {!ReadableStream}
|
|
297
294
|
*/
|
|
298
295
|
async streamReport(customerId, loginCustomerId, reportQueryConfig) {
|
|
299
|
-
const customer = this.getGoogleAdsApiCustomer_(
|
|
296
|
+
const customer = await this.getGoogleAdsApiCustomer_(
|
|
297
|
+
loginCustomerId, customerId);
|
|
300
298
|
return customer.reportStreamRaw(reportQueryConfig);
|
|
301
299
|
}
|
|
302
300
|
|
|
@@ -313,7 +311,7 @@ class GoogleAds {
|
|
|
313
311
|
*/
|
|
314
312
|
async searchMetaData(loginCustomerId, adFields, metadata = [
|
|
315
313
|
'name', 'data_type', 'is_repeated', 'type_url',]) {
|
|
316
|
-
const customer = this.getGoogleAdsApiCustomer_(loginCustomerId);
|
|
314
|
+
const customer = await this.getGoogleAdsApiCustomer_(loginCustomerId);
|
|
317
315
|
const selectClause = metadata.join(',');
|
|
318
316
|
const fields = adFields.join('","');
|
|
319
317
|
const query = `SELECT ${selectClause} WHERE name IN ("${fields}")`;
|
|
@@ -560,9 +558,10 @@ class GoogleAds {
|
|
|
560
558
|
* @param {string} loginCustomerId Login customer account ID (Mcc Account id).
|
|
561
559
|
* @return {!Promise<!UploadCallConversionsResponse>}
|
|
562
560
|
*/
|
|
563
|
-
uploadCallConversions(callConversions, customerId, loginCustomerId) {
|
|
561
|
+
async uploadCallConversions(callConversions, customerId, loginCustomerId) {
|
|
564
562
|
this.logger.debug('Upload call conversions for customerId:', customerId);
|
|
565
|
-
const customer = this.getGoogleAdsApiCustomer_(
|
|
563
|
+
const customer = await this.getGoogleAdsApiCustomer_(
|
|
564
|
+
loginCustomerId, customerId);
|
|
566
565
|
const request = new UploadCallConversionsRequest({
|
|
567
566
|
conversions: callConversions,
|
|
568
567
|
customer_id: customerId,
|
|
@@ -581,9 +580,10 @@ class GoogleAds {
|
|
|
581
580
|
* @param {string} loginCustomerId Login customer account ID (Mcc Account id).
|
|
582
581
|
* @return {!Promise<!UploadClickConversionsResponse>}
|
|
583
582
|
*/
|
|
584
|
-
uploadClickConversions(clickConversions, customerId, loginCustomerId) {
|
|
583
|
+
async uploadClickConversions(clickConversions, customerId, loginCustomerId) {
|
|
585
584
|
this.logger.debug('Upload click conversions for customerId:', customerId);
|
|
586
|
-
const customer = this.getGoogleAdsApiCustomer_(
|
|
585
|
+
const customer = await this.getGoogleAdsApiCustomer_(
|
|
586
|
+
loginCustomerId, customerId);
|
|
587
587
|
const request = new UploadClickConversionsRequest({
|
|
588
588
|
conversions: clickConversions,
|
|
589
589
|
customer_id: customerId,
|
|
@@ -603,11 +603,12 @@ class GoogleAds {
|
|
|
603
603
|
* @param {string} loginCustomerId Login customer account ID (Mcc Account id).
|
|
604
604
|
* @return {!Promise<!UploadConversionAdjustmentsResponse>}
|
|
605
605
|
*/
|
|
606
|
-
uploadConversionAdjustments(conversionAdjustments, customerId,
|
|
606
|
+
async uploadConversionAdjustments(conversionAdjustments, customerId,
|
|
607
607
|
loginCustomerId) {
|
|
608
608
|
this.logger.debug('Upload conversion adjustments for customerId:',
|
|
609
609
|
customerId);
|
|
610
|
-
const customer = this.getGoogleAdsApiCustomer_(
|
|
610
|
+
const customer = await this.getGoogleAdsApiCustomer_(
|
|
611
|
+
loginCustomerId, customerId);
|
|
611
612
|
const request = new UploadConversionAdjustmentsRequest({
|
|
612
613
|
conversion_adjustments: conversionAdjustments,
|
|
613
614
|
customer_id: customerId,
|
|
@@ -626,7 +627,8 @@ class GoogleAds {
|
|
|
626
627
|
* @return {Promise<number|undefined>} Returns undefined if can't find tag.
|
|
627
628
|
*/
|
|
628
629
|
async getConversionCustomVariableId(tag, customerId, loginCustomerId) {
|
|
629
|
-
const customer = this.getGoogleAdsApiCustomer_(
|
|
630
|
+
const customer = await this.getGoogleAdsApiCustomer_(
|
|
631
|
+
loginCustomerId, customerId);
|
|
630
632
|
const customVariables = await customer.query(`
|
|
631
633
|
SELECT conversion_custom_variable.id,
|
|
632
634
|
conversion_custom_variable.tag
|
|
@@ -692,7 +694,8 @@ class GoogleAds {
|
|
|
692
694
|
validate_only: this.debugMode, // when true makes no changes
|
|
693
695
|
partial_failure: true, // Will still create the non-failed entities
|
|
694
696
|
};
|
|
695
|
-
const customer = this.getGoogleAdsApiCustomer_(
|
|
697
|
+
const customer = await this.getGoogleAdsApiCustomer_(
|
|
698
|
+
loginCustomerId, customerId);
|
|
696
699
|
const response = await customer.userLists.create([userList], options);
|
|
697
700
|
const { results, partial_failure_error: failed } = response;
|
|
698
701
|
if (this.logger.isDebugEnabled()) {
|
|
@@ -775,7 +778,8 @@ class GoogleAds {
|
|
|
775
778
|
const userListId = customerMatchConfig.list_id;
|
|
776
779
|
const operation = customerMatchConfig.operation;
|
|
777
780
|
|
|
778
|
-
const customer = this.getGoogleAdsApiCustomer_(
|
|
781
|
+
const customer = await this.getGoogleAdsApiCustomer_(
|
|
782
|
+
loginCustomerId, customerId);
|
|
779
783
|
const operationsList = this.buildOperationsList_(operation,
|
|
780
784
|
customerMatchRecords);
|
|
781
785
|
const metadata = this.buildCustomerMatchUserListMetadata_(customerId,
|
|
@@ -893,8 +897,8 @@ class GoogleAds {
|
|
|
893
897
|
const customerId = this.getCleanCid_(config.customer_id);
|
|
894
898
|
const { list_id: userListId, type } = config;
|
|
895
899
|
this.logger.debug('Creating OfflineUserDataJob for CID:', customerId);
|
|
896
|
-
const customer = this.getGoogleAdsApiCustomer_(
|
|
897
|
-
|
|
900
|
+
const customer = await this.getGoogleAdsApiCustomer_(
|
|
901
|
+
loginCustomerId, customerId);
|
|
898
902
|
const job = OfflineUserDataJob.create({
|
|
899
903
|
type,
|
|
900
904
|
});
|
|
@@ -936,7 +940,8 @@ class GoogleAds {
|
|
|
936
940
|
const loginCustomerId = this.getCleanCid_(config.login_customer_id);
|
|
937
941
|
const customerId = this.getCleanCid_(config.customer_id);
|
|
938
942
|
const operation = config.operation;
|
|
939
|
-
const customer = this.getGoogleAdsApiCustomer_(
|
|
943
|
+
const customer = await this.getGoogleAdsApiCustomer_(
|
|
944
|
+
loginCustomerId, customerId);
|
|
940
945
|
const operationsList = this.buildOperationsList_(operation, records);
|
|
941
946
|
const request = AddOfflineUserDataJobOperationsRequest.create({
|
|
942
947
|
resource_name: jobResourceName,
|
|
@@ -961,7 +966,8 @@ class GoogleAds {
|
|
|
961
966
|
async runOfflineUserDataJob(config, jobResourceName) {
|
|
962
967
|
const loginCustomerId = this.getCleanCid_(config.login_customer_id);
|
|
963
968
|
const customerId = this.getCleanCid_(config.customer_id);
|
|
964
|
-
const customer = this.getGoogleAdsApiCustomer_(
|
|
969
|
+
const customer = await this.getGoogleAdsApiCustomer_(
|
|
970
|
+
loginCustomerId, customerId);
|
|
965
971
|
const request = RunOfflineUserDataJobRequest.create({
|
|
966
972
|
resource_name: jobResourceName,
|
|
967
973
|
validate_only: false,//this.debugMode,
|
|
@@ -1025,13 +1031,22 @@ class GoogleAds {
|
|
|
1025
1031
|
* @return {GoogleAdsApi.Customer}
|
|
1026
1032
|
* @private
|
|
1027
1033
|
*/
|
|
1028
|
-
getGoogleAdsApiCustomer_(loginCustomerId, customerId = loginCustomerId) {
|
|
1029
|
-
|
|
1030
|
-
|
|
1034
|
+
async getGoogleAdsApiCustomer_(loginCustomerId, customerId = loginCustomerId) {
|
|
1035
|
+
if (this.googleAds) return this.googleAds;
|
|
1036
|
+
await this.authClient.prepareCredentials();
|
|
1037
|
+
const oauthClient = await this.authClient.getOAuth2Token();
|
|
1038
|
+
/** @const {GoogleAdsApi} */
|
|
1039
|
+
const googleAdsApiClient = new GoogleAdsApi({
|
|
1040
|
+
client_id: oauthClient.clientId,
|
|
1041
|
+
client_secret: oauthClient.clientSecret,
|
|
1042
|
+
developer_token: this.developerToken,
|
|
1043
|
+
});
|
|
1044
|
+
this.googleAds = googleAdsApiClient.Customer({
|
|
1031
1045
|
customer_id: customerId,
|
|
1032
1046
|
login_customer_id: loginCustomerId,
|
|
1033
|
-
refresh_token:
|
|
1047
|
+
refresh_token: oauthClient.refreshToken,
|
|
1034
1048
|
});
|
|
1049
|
+
return this.googleAds;
|
|
1035
1050
|
}
|
|
1036
1051
|
|
|
1037
1052
|
}
|
package/src/apis/spreadsheets.js
CHANGED
|
@@ -73,33 +73,43 @@ class Spreadsheets {
|
|
|
73
73
|
constructor(spreadsheetId, env = process.env) {
|
|
74
74
|
/** @const {string} */
|
|
75
75
|
this.spreadsheetId = spreadsheetId;
|
|
76
|
-
|
|
77
|
-
const auth = authClient.getDefaultAuth();
|
|
78
|
-
/** @const {!!google.sheets} */
|
|
79
|
-
this.instance = google.sheets({
|
|
80
|
-
version: API_VERSION,
|
|
81
|
-
auth,
|
|
82
|
-
});
|
|
76
|
+
this.authClient = new AuthClient(API_SCOPES, env);
|
|
83
77
|
/**
|
|
84
78
|
* Logger object from 'log4js' package where this type is not exported.
|
|
85
79
|
*/
|
|
86
80
|
this.logger = getLogger('API.GS');
|
|
87
|
-
|
|
88
|
-
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Prepares the Google Sheets instance.
|
|
85
|
+
* @return {!google.sheets}
|
|
86
|
+
* @private
|
|
87
|
+
*/
|
|
88
|
+
async getApiClient_() {
|
|
89
|
+
if (this.sheets) return this.sheets;
|
|
90
|
+
await this.authClient.prepareCredentials();
|
|
91
|
+
this.logger.debug(`Initialized ${this.constructor.name} instance.`);
|
|
92
|
+
this.sheets = google.sheets({
|
|
93
|
+
version: API_VERSION,
|
|
94
|
+
auth: this.authClient.getDefaultAuth(),
|
|
95
|
+
});
|
|
96
|
+
return this.sheets;
|
|
89
97
|
}
|
|
90
98
|
|
|
91
99
|
/**
|
|
92
100
|
* Gets the Sheet Id of the given Spreadsheet and possible Sheet name. If the
|
|
93
101
|
* Sheet name is missing, it will return the first Sheet's Id.
|
|
94
102
|
* @param {string} sheetName Name of the Sheet.
|
|
95
|
-
* @return {!Promise<number>}
|
|
103
|
+
* @return {!Promise<number>} The ID for the sheet that is unique to the
|
|
104
|
+
* spreadsheet.
|
|
96
105
|
*/
|
|
97
106
|
async getSheetId(sheetName) {
|
|
98
107
|
const request = /** @type{Params$Resource$Spreadsheets$Get} */ {
|
|
99
108
|
spreadsheetId: this.spreadsheetId,
|
|
100
109
|
ranges: sheetName,
|
|
101
110
|
};
|
|
102
|
-
const
|
|
111
|
+
const sheets = await this.getApiClient_();
|
|
112
|
+
const response = await sheets.spreadsheets.get(request);
|
|
103
113
|
const sheet = response.data.sheets[0];
|
|
104
114
|
this.logger.debug(`Get sheet[${sheetName}]: `, sheet);
|
|
105
115
|
return sheet.properties.sheetId;
|
|
@@ -117,7 +127,8 @@ class Spreadsheets {
|
|
|
117
127
|
range: sheetName,
|
|
118
128
|
};
|
|
119
129
|
try {
|
|
120
|
-
const
|
|
130
|
+
const sheets = await this.getApiClient_();
|
|
131
|
+
const response = await sheets.spreadsheets.values.clear(request);
|
|
121
132
|
const data = response.data;
|
|
122
133
|
this.logger.debug(`Clear sheet[${sheetName}}]: `, data);
|
|
123
134
|
} catch (error) {
|
|
@@ -169,7 +180,8 @@ class Spreadsheets {
|
|
|
169
180
|
ranges: sheetName,
|
|
170
181
|
};
|
|
171
182
|
try {
|
|
172
|
-
const
|
|
183
|
+
const sheets = await this.getApiClient_();
|
|
184
|
+
const response = await sheets.spreadsheets.get(request);
|
|
173
185
|
const sheet = response.data.sheets[0];
|
|
174
186
|
const sheetId = sheet.properties.sheetId;
|
|
175
187
|
const rowCount = sheet.properties.gridProperties.rowCount;
|
|
@@ -191,7 +203,7 @@ class Spreadsheets {
|
|
|
191
203
|
columnCount}] to [${targetRows}, ${targetColumns}]`,
|
|
192
204
|
JSON.stringify(requests.resource.requests));
|
|
193
205
|
if (requests.resource.requests.length > 0) {
|
|
194
|
-
const {data} = await
|
|
206
|
+
const { data } = await sheets.spreadsheets.batchUpdate(requests);
|
|
195
207
|
this.logger.debug(`Reshape Sheet [${sheetName}]: `, data);
|
|
196
208
|
} else {
|
|
197
209
|
this.logger.debug('No need to reshape.');
|
|
@@ -220,7 +232,8 @@ class Spreadsheets {
|
|
|
220
232
|
numberOfLines: data.trim().split('\n').length,
|
|
221
233
|
};
|
|
222
234
|
try {
|
|
223
|
-
const
|
|
235
|
+
const sheets = await this.getApiClient_();
|
|
236
|
+
const response = await sheets.spreadsheets.batchUpdate(request);
|
|
224
237
|
const data = response.data;
|
|
225
238
|
this.logger.debug(`Batch[${batchId}] uploaded: `, data);
|
|
226
239
|
batchResult.result = true;
|
package/src/apis/youtube.js
CHANGED
|
@@ -166,18 +166,29 @@ class YouTube {
|
|
|
166
166
|
* variables.
|
|
167
167
|
*/
|
|
168
168
|
constructor(env = process.env) {
|
|
169
|
-
|
|
170
|
-
this.auth = authClient.getDefaultAuth();
|
|
171
|
-
/** @const {!google.youtube} */
|
|
172
|
-
this.instance = google.youtube({
|
|
173
|
-
version: API_VERSION,
|
|
174
|
-
});
|
|
169
|
+
this.authClient = new AuthClient(API_SCOPES, env);
|
|
175
170
|
/**
|
|
176
171
|
* Logger object from 'log4js' package where this type is not exported.
|
|
177
172
|
*/
|
|
178
173
|
this.logger = getLogger('API.YT');
|
|
179
174
|
}
|
|
180
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Prepares the Google YouTube instance.
|
|
178
|
+
* @return {!google.youtube}
|
|
179
|
+
* @private
|
|
180
|
+
*/
|
|
181
|
+
async getApiClient_() {
|
|
182
|
+
if (this.youtube) return this.youtube;
|
|
183
|
+
await this.authClient.prepareCredentials();
|
|
184
|
+
this.logger.debug(`Initialized ${this.constructor.name} instance.`);
|
|
185
|
+
this.youtube = google.youtube({
|
|
186
|
+
version: API_VERSION,
|
|
187
|
+
auth: this.authClient.getDefaultAuth(),
|
|
188
|
+
});
|
|
189
|
+
return this.youtube;
|
|
190
|
+
}
|
|
191
|
+
|
|
181
192
|
/**
|
|
182
193
|
* Returns a collection of zero or more channel resources that match the
|
|
183
194
|
* request criteria.
|
|
@@ -186,12 +197,11 @@ class YouTube {
|
|
|
186
197
|
* @return {!Promise<Array<Schema$Channel>>}
|
|
187
198
|
*/
|
|
188
199
|
async listChannels(config) {
|
|
189
|
-
const channelListRequest = Object.assign({
|
|
190
|
-
auth: this.auth,
|
|
191
|
-
}, config);
|
|
200
|
+
const channelListRequest = Object.assign({}, config);
|
|
192
201
|
channelListRequest.part = channelListRequest.part.join(',')
|
|
193
202
|
try {
|
|
194
|
-
const
|
|
203
|
+
const youtube = await this.getApiClient_();
|
|
204
|
+
const response = await youtube.channels.list(channelListRequest);
|
|
195
205
|
this.logger.debug('Response: ', response);
|
|
196
206
|
return response.data.items;
|
|
197
207
|
} catch (error) {
|
|
@@ -210,12 +220,11 @@ class YouTube {
|
|
|
210
220
|
* @return {!Promise<Array<Schema$Video>>}
|
|
211
221
|
*/
|
|
212
222
|
async listVideos(config) {
|
|
213
|
-
const videoListRequest = Object.assign({
|
|
214
|
-
auth: this.auth,
|
|
215
|
-
}, config);
|
|
223
|
+
const videoListRequest = Object.assign({}, config);
|
|
216
224
|
videoListRequest.part = videoListRequest.part.join(',')
|
|
217
225
|
try {
|
|
218
|
-
const
|
|
226
|
+
const youtube = await this.getApiClient_();
|
|
227
|
+
const response = await youtube.videos.list(videoListRequest);
|
|
219
228
|
this.logger.debug('Response: ', response);
|
|
220
229
|
return response.data.items;
|
|
221
230
|
} catch (error) {
|
|
@@ -235,13 +244,11 @@ class YouTube {
|
|
|
235
244
|
* @return {!Promise<Array<Schema$CommentThread>>}
|
|
236
245
|
*/
|
|
237
246
|
async listCommentThreads(config) {
|
|
238
|
-
const commentThreadsRequest = Object.assign({
|
|
239
|
-
auth: this.auth,
|
|
240
|
-
}, config);
|
|
247
|
+
const commentThreadsRequest = Object.assign({}, config);
|
|
241
248
|
commentThreadsRequest.part = commentThreadsRequest.part.join(',')
|
|
242
249
|
try {
|
|
243
|
-
const
|
|
244
|
-
|
|
250
|
+
const youtube = await this.getApiClient_();
|
|
251
|
+
const response = await youtube.commentThreads.list(commentThreadsRequest);
|
|
245
252
|
this.logger.debug('Response: ', response.data);
|
|
246
253
|
return response.data.items;
|
|
247
254
|
} catch (error) {
|
|
@@ -265,7 +272,7 @@ class YouTube {
|
|
|
265
272
|
if (resultLimit <= 0) return [];
|
|
266
273
|
|
|
267
274
|
const playlistsRequest = Object.assign({
|
|
268
|
-
auth: this.auth,
|
|
275
|
+
// auth: this.auth,
|
|
269
276
|
}, config, {
|
|
270
277
|
pageToken
|
|
271
278
|
});
|
|
@@ -275,8 +282,8 @@ class YouTube {
|
|
|
275
282
|
}
|
|
276
283
|
|
|
277
284
|
try {
|
|
278
|
-
const
|
|
279
|
-
|
|
285
|
+
const youtube = await this.getApiClient_();
|
|
286
|
+
const response = await youtube.playlists.list(playlistsRequest);
|
|
280
287
|
this.logger.debug('Response: ', response.data);
|
|
281
288
|
if (response.data.nextPageToken) {
|
|
282
289
|
this.logger.debug(
|
|
@@ -310,18 +317,15 @@ class YouTube {
|
|
|
310
317
|
async listSearchResults(config, resultLimit = 1000, pageToken = null) {
|
|
311
318
|
if (resultLimit <= 0) return [];
|
|
312
319
|
|
|
313
|
-
const searchRequest = Object.assign({
|
|
314
|
-
auth: this.auth,
|
|
315
|
-
}, config, {
|
|
316
|
-
pageToken
|
|
317
|
-
});
|
|
320
|
+
const searchRequest = Object.assign({}, config, { pageToken });
|
|
318
321
|
|
|
319
322
|
if (Array.isArray(searchRequest.part)) {
|
|
320
323
|
searchRequest.part = searchRequest.part.join(',');
|
|
321
324
|
}
|
|
322
325
|
|
|
323
326
|
try {
|
|
324
|
-
const
|
|
327
|
+
const youtube = await this.getApiClient_();
|
|
328
|
+
const response = await youtube.search.list(searchRequest);
|
|
325
329
|
this.logger.debug('Response: ', response.data);
|
|
326
330
|
if (response.data.nextPageToken) {
|
|
327
331
|
this.logger.debug(
|
package/src/components/pubsub.js
CHANGED
|
@@ -20,7 +20,13 @@
|
|
|
20
20
|
'use strict';
|
|
21
21
|
|
|
22
22
|
const {
|
|
23
|
-
PubSub,
|
|
23
|
+
PubSub,
|
|
24
|
+
Message,
|
|
25
|
+
Topic,
|
|
26
|
+
Subscription,
|
|
27
|
+
ClientConfig,
|
|
28
|
+
CreateSubscriptionOptions,
|
|
29
|
+
v1: { SubscriberClient },
|
|
24
30
|
} = require('@google-cloud/pubsub');
|
|
25
31
|
|
|
26
32
|
/**
|
|
@@ -28,6 +34,7 @@ const {
|
|
|
28
34
|
* 1. gets or creates a topic.
|
|
29
35
|
* 2. gets or creates a subscription.
|
|
30
36
|
* 3. publishes a message after confirms the topic exists.
|
|
37
|
+
* 4. acknowledge message(s).
|
|
31
38
|
*/
|
|
32
39
|
class EnhancedPubSub {
|
|
33
40
|
/**
|
|
@@ -38,6 +45,8 @@ class EnhancedPubSub {
|
|
|
38
45
|
constructor(options = undefined) {
|
|
39
46
|
/** @type {!PubSub} */
|
|
40
47
|
this.pubsub = new PubSub(options);
|
|
48
|
+
/** @type {!SubscriberClient} */
|
|
49
|
+
this.subClient = new SubscriberClient(options);
|
|
41
50
|
}
|
|
42
51
|
|
|
43
52
|
/**
|
|
@@ -118,6 +127,23 @@ class EnhancedPubSub {
|
|
|
118
127
|
}
|
|
119
128
|
};
|
|
120
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Using `SubscriberClient` to acknowledge messages.
|
|
132
|
+
* 2022.11.02 The methon `ack()` in Message doesn't work properly due to a
|
|
133
|
+
* unknown reason. Use this function to acknowledge a message for now.
|
|
134
|
+
*
|
|
135
|
+
* @param {string} subscription Subscription name.
|
|
136
|
+
* @param {(string|!Array<string>)} ackIds Message ackIds.
|
|
137
|
+
*/
|
|
138
|
+
async acknowledge(subscription, ackIds) {
|
|
139
|
+
const projectId = await this.subClient.getProjectId();
|
|
140
|
+
const ackRequest = {
|
|
141
|
+
subscription: this.subClient.subscriptionPath(projectId, subscription),
|
|
142
|
+
ackIds: Array.isArray(ackIds) ? ackIds : [ackIds],
|
|
143
|
+
};
|
|
144
|
+
await this.subClient.acknowledge(ackRequest);
|
|
145
|
+
}
|
|
146
|
+
|
|
121
147
|
/**
|
|
122
148
|
* Returns a new instance of this class. Using this function to replace
|
|
123
149
|
* constructor to be more friendly to unit tests.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Copyright 2022 Google Inc.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @fileoverview Secret Manager wrapper class.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const { GoogleAuthOptions } = require('google-auth-library');
|
|
20
|
+
const { SecretManagerServiceClient } = require('@google-cloud/secret-manager');
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Google Cloud Secret Manager class based on cloud client library.
|
|
24
|
+
* @see https://cloud.google.com/nodejs/docs/reference/secret-manager/latest
|
|
25
|
+
* @see https://cloud.google.com/secret-manager/docs/reference/libraries
|
|
26
|
+
*/
|
|
27
|
+
class SecretManager {
|
|
28
|
+
/**
|
|
29
|
+
* @constructor
|
|
30
|
+
* @param {GoogleAuthOptions=} options
|
|
31
|
+
*/
|
|
32
|
+
constructor(options = {}) {
|
|
33
|
+
if (!options.projectId) {
|
|
34
|
+
options.projectId = process.env['GCP_PROJECT'];
|
|
35
|
+
}
|
|
36
|
+
this.client = new SecretManagerServiceClient(options);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async access(secret, version = 'latest') {
|
|
40
|
+
const projectId = await this.client.getProjectId();
|
|
41
|
+
const name = `projects/${projectId}/secrets/${secret}/versions/${version}`;
|
|
42
|
+
try {
|
|
43
|
+
const [secretObj] = await this.client.accessSecretVersion({ name });
|
|
44
|
+
return secretObj.payload.data.toString('utf8');
|
|
45
|
+
} catch (error) {
|
|
46
|
+
if (error.details.indexOf('not found') > -1) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
exports.SecretManager = SecretManager;
|