@google-cloud/nodejs-common 0.8.3 → 0.9.2-beta2
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/bin/install_functions.sh +130 -178
- package/index.js +1 -0
- package/package.json +4 -3
- package/src/apis/analytics.js +28 -14
- package/src/apis/auth_client.js +14 -9
- package/src/apis/cloud_platform_apis.js +2 -2
- package/src/apis/google_ads.js +16 -17
- package/src/apis/measurement_protocol.js +32 -19
- package/src/components/firestore/access_base.js +6 -3
- package/src/components/firestore/data_access_object.js +6 -3
- package/src/components/firestore/datastore_mode_access.js +3 -2
- package/src/components/firestore/native_mode_access.js +3 -2
- package/src/components/scheduler.js +2 -2
- package/src/components/utils.js +17 -3
- package/src/components/vertex_ai.js +115 -0
package/bin/install_functions.sh
CHANGED
|
@@ -138,13 +138,6 @@ EXTERNAL_API_SCOPES=(
|
|
|
138
138
|
# Enabled APIs' OAuth scopes.
|
|
139
139
|
ENABLED_OAUTH_SCOPES=()
|
|
140
140
|
|
|
141
|
-
# https://cloud.google.com/iam/docs/understanding-roles#service-accounts-roles
|
|
142
|
-
declare -A GOOGLE_SERVICE_ACCOUNT_PERMISSIONS
|
|
143
|
-
GOOGLE_SERVICE_ACCOUNT_PERMISSIONS=(
|
|
144
|
-
["Service Account Admin"]="iam.serviceAccounts.create"
|
|
145
|
-
["Service Account Key Admin"]="iam.serviceAccounts.create"
|
|
146
|
-
)
|
|
147
|
-
|
|
148
141
|
# Preparation functions.
|
|
149
142
|
#######################################
|
|
150
143
|
# Mimic a Cloud Shell environment to enable local running.
|
|
@@ -437,8 +430,8 @@ select_functions_location() {
|
|
|
437
430
|
local regionVariableName defaultValue locations
|
|
438
431
|
regionVariableName="${1:-"REGION"}"
|
|
439
432
|
defaultValue="${!regionVariableName}"
|
|
440
|
-
locations=($(gcloud functions regions list
|
|
441
|
-
's/projects\/.*\/locations\///'))
|
|
433
|
+
locations=($(gcloud functions regions list --format="csv[no-heading](name)"| \
|
|
434
|
+
sed 's/projects\/.*\/locations\///'))
|
|
442
435
|
local region
|
|
443
436
|
while :; do
|
|
444
437
|
local exist_functions
|
|
@@ -561,7 +554,6 @@ need to be enabled."
|
|
|
561
554
|
# NEED_OAUTH
|
|
562
555
|
# NEED_SERVICE_ACCOUNT
|
|
563
556
|
# GOOGLE_CLOUD_PERMISSIONS
|
|
564
|
-
# GOOGLE_SERVICE_ACCOUNT_PERMISSIONS
|
|
565
557
|
# Arguments:
|
|
566
558
|
# None
|
|
567
559
|
#######################################
|
|
@@ -590,11 +582,6 @@ authentication method:"
|
|
|
590
582
|
printf '%s\n' "... OAuth is selected."
|
|
591
583
|
else
|
|
592
584
|
NEED_SERVICE_ACCOUNT="true"
|
|
593
|
-
local role
|
|
594
|
-
for role in "${!GOOGLE_SERVICE_ACCOUNT_PERMISSIONS[@]}"; do
|
|
595
|
-
GOOGLE_CLOUD_PERMISSIONS["${role}"]=\
|
|
596
|
-
"${GOOGLE_SERVICE_ACCOUNT_PERMISSIONS["${role}"]}"
|
|
597
|
-
done
|
|
598
585
|
printf '%s\n' "... Service Account is selected."
|
|
599
586
|
fi
|
|
600
587
|
}
|
|
@@ -1192,6 +1179,87 @@ EOF
|
|
|
1192
1179
|
printf '%s\n' "OK. Continue with monitored folder [${folder}]."
|
|
1193
1180
|
}
|
|
1194
1181
|
|
|
1182
|
+
#######################################
|
|
1183
|
+
# Create or update a Log router sink.
|
|
1184
|
+
# Globals:
|
|
1185
|
+
# None
|
|
1186
|
+
# Arguments:
|
|
1187
|
+
# Name of the sink
|
|
1188
|
+
# Filter conditions
|
|
1189
|
+
# Sink destination
|
|
1190
|
+
#######################################
|
|
1191
|
+
create_or_update_sink() {
|
|
1192
|
+
local sinkName=${1}
|
|
1193
|
+
local logFilter=${2}
|
|
1194
|
+
local sinkDestAndFlags=(${3})
|
|
1195
|
+
local existingFilter
|
|
1196
|
+
existingFilter=$(gcloud logging sinks list --filter="name:${sinkName}" \
|
|
1197
|
+
--format="value(filter)")
|
|
1198
|
+
if [[ "${existingFilter}" != "${logFilter}" ]]; then
|
|
1199
|
+
local action
|
|
1200
|
+
if [[ -z "${existingFilter}" ]]; then
|
|
1201
|
+
action="create"
|
|
1202
|
+
printf '%s\n' " Logging Export [${sinkName}] doesn't exist. Creating..."
|
|
1203
|
+
else
|
|
1204
|
+
action="update"
|
|
1205
|
+
printf '%s\n' " Logging Export [${sinkName}] exists with a different \
|
|
1206
|
+
filter. Updating..."
|
|
1207
|
+
fi
|
|
1208
|
+
gcloud -q logging sinks ${action} "${sinkName}" "${sinkDestAndFlags[@]}" \
|
|
1209
|
+
--log-filter="${logFilter}"
|
|
1210
|
+
else
|
|
1211
|
+
printf '%s\n' " Logging Export [${sinkName}] exists. Continue..."
|
|
1212
|
+
fi
|
|
1213
|
+
if [[ $? -gt 0 ]];then
|
|
1214
|
+
printf '%s\n' "Failed to create or update Logs router sink."
|
|
1215
|
+
return 1
|
|
1216
|
+
fi
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
#######################################
|
|
1220
|
+
# Confirm the service account of the given sink has proper permission to dump
|
|
1221
|
+
# the logs.
|
|
1222
|
+
# Globals:
|
|
1223
|
+
# None
|
|
1224
|
+
# Arguments:
|
|
1225
|
+
# Name of the sink
|
|
1226
|
+
# Bindings role, e.g. "pubsub.publisher" or "bigquery.dataEditor"
|
|
1227
|
+
# Role name, e.g. "Pub/Sub Publisher" or "BigQuery Data Editor"
|
|
1228
|
+
#######################################
|
|
1229
|
+
confirm_sink_service_account_permission() {
|
|
1230
|
+
local sinkName=${1}
|
|
1231
|
+
local bindingsRole=${2}
|
|
1232
|
+
local roleName=${3}
|
|
1233
|
+
local serviceAccount existingRole
|
|
1234
|
+
serviceAccount=$(gcloud logging sinks describe "${sinkName}" \
|
|
1235
|
+
--format="get(writerIdentity)")
|
|
1236
|
+
while :;do
|
|
1237
|
+
printf '%s\n' " Checking the role of the sink's service account \
|
|
1238
|
+
[${serviceAccount}]..."
|
|
1239
|
+
existingRole=$(gcloud projects get-iam-policy "${GCP_PROJECT}" \
|
|
1240
|
+
--flatten=bindings --filter="bindings.members:${serviceAccount} AND \
|
|
1241
|
+
bindings.role:roles/${bindingsRole}" --format="get(bindings.members)")
|
|
1242
|
+
if [[ -z "${existingRole}" ]];then
|
|
1243
|
+
printf '%s\n' " Granting Role '${roleName}' to the service \
|
|
1244
|
+
account..."
|
|
1245
|
+
gcloud -q projects add-iam-policy-binding "${GCP_PROJECT}" --member \
|
|
1246
|
+
"${serviceAccount}" --role roles/${bindingsRole}
|
|
1247
|
+
if [[ $? -gt 0 ]];then
|
|
1248
|
+
printf '%s\n' "Failed to grant the role. Use this link \
|
|
1249
|
+
https://console.cloud.google.com/iam-admin/iam?project=${GCP_PROJECT} to \
|
|
1250
|
+
manually grant Role '${roleName}' to ${serviceAccount}."
|
|
1251
|
+
printf '%s' "Press any key to continue after you grant the access..."
|
|
1252
|
+
local any
|
|
1253
|
+
read -n1 -s any
|
|
1254
|
+
continue
|
|
1255
|
+
fi
|
|
1256
|
+
else
|
|
1257
|
+
printf '%s\n' " The role has already been granted."
|
|
1258
|
+
return 0
|
|
1259
|
+
fi
|
|
1260
|
+
done
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1195
1263
|
#######################################
|
|
1196
1264
|
# Save the configuration to a local file.
|
|
1197
1265
|
# Globals:
|
|
@@ -1228,9 +1296,15 @@ save_config() {
|
|
|
1228
1296
|
|
|
1229
1297
|
#######################################
|
|
1230
1298
|
# Based on the authentication method, guide user complete authentication
|
|
1231
|
-
# process.
|
|
1232
|
-
#
|
|
1233
|
-
#
|
|
1299
|
+
# process.
|
|
1300
|
+
# For service account, given that Cloud Functions can extend the authorized API
|
|
1301
|
+
# scopes now, it will use the default service account rather than an explicit
|
|
1302
|
+
# service account with the key file downloaded. By doing so, we can reduce the
|
|
1303
|
+
# risk of leaking service account key. If there is a service key in the current
|
|
1304
|
+
# folder from previous installation, the code will continue using it, otherwise
|
|
1305
|
+
# it will use the Cloud Functions' default service account.
|
|
1306
|
+
# For OAuth 2.0, guide user to complete OAuth authentication and save the
|
|
1307
|
+
# refresh token.
|
|
1234
1308
|
# Globals:
|
|
1235
1309
|
# NEED_SERVICE_ACCOUNT
|
|
1236
1310
|
# NEED_OAUTH
|
|
@@ -1238,152 +1312,11 @@ save_config() {
|
|
|
1238
1312
|
# None
|
|
1239
1313
|
#######################################
|
|
1240
1314
|
do_authentication(){
|
|
1241
|
-
if [[ ${NEED_SERVICE_ACCOUNT} == "true" ]]; then
|
|
1242
|
-
download_service_account_key
|
|
1243
|
-
fi
|
|
1244
1315
|
if [[ ${NEED_OAUTH} == "true" ]]; then
|
|
1245
1316
|
do_oauth
|
|
1246
1317
|
fi
|
|
1247
1318
|
}
|
|
1248
1319
|
|
|
1249
|
-
#######################################
|
|
1250
|
-
# Download a service account key file and save as `$SA_KEY_FILE`.
|
|
1251
|
-
# Globals:
|
|
1252
|
-
# SA_NAME
|
|
1253
|
-
# GCP_PROJECT
|
|
1254
|
-
# SA_KEY_FILE
|
|
1255
|
-
# Arguments:
|
|
1256
|
-
# None
|
|
1257
|
-
# Returns:
|
|
1258
|
-
# 0 if service key files exists or created, non-zero on error.
|
|
1259
|
-
#######################################
|
|
1260
|
-
download_service_account_key() {
|
|
1261
|
-
(( STEP += 1 ))
|
|
1262
|
-
printf '%s\n' "Step ${STEP}: Downloading the key file for the service \
|
|
1263
|
-
account..."
|
|
1264
|
-
if [[ -z ${SA_NAME} ]];then
|
|
1265
|
-
confirm_service_account
|
|
1266
|
-
fi
|
|
1267
|
-
local suffix exist
|
|
1268
|
-
suffix=$(get_sa_domain_from_gcp_id "${GCP_PROJECT}")
|
|
1269
|
-
local email="${SA_NAME}@${suffix}"
|
|
1270
|
-
local prompt="Would you like to download the key file for [${email}] and \
|
|
1271
|
-
save it as ${SA_KEY_FILE}? [Y/n]: "
|
|
1272
|
-
local default_value="y"
|
|
1273
|
-
if [[ -f "${SA_KEY_FILE}" && -s "${SA_KEY_FILE}" ]]; then
|
|
1274
|
-
exist=$(get_value_from_json_file ${SA_KEY_FILE} 'client_email' 2>&1)
|
|
1275
|
-
if [[ ${exist} =~ .*("@${suffix}") ]]; then
|
|
1276
|
-
prompt="A key file for [${exist}] with the key ID '\
|
|
1277
|
-
$(get_value_from_json_file ${SA_KEY_FILE} 'private_key_id') already exists'. \
|
|
1278
|
-
Would you like to create a new key to overwrite it? [N/y]: "
|
|
1279
|
-
default_value="n"
|
|
1280
|
-
fi
|
|
1281
|
-
fi
|
|
1282
|
-
printf '%s' "${prompt}"
|
|
1283
|
-
local input
|
|
1284
|
-
read -r input
|
|
1285
|
-
input=${input:-"${default_value}"}
|
|
1286
|
-
if [[ ${input} == 'y' || ${input} == 'Y' ]];then
|
|
1287
|
-
printf '%s\n' "Downloading a new key file for [${email}]..."
|
|
1288
|
-
gcloud iam service-accounts keys create "${SA_KEY_FILE}" --iam-account \
|
|
1289
|
-
"${email}"
|
|
1290
|
-
if [[ $? -gt 0 ]]; then
|
|
1291
|
-
printf '%s\n' "Failed to download new key files for [${email}]."
|
|
1292
|
-
return 1
|
|
1293
|
-
else
|
|
1294
|
-
printf '%s\n' "OK. New key file is saved at [${SA_KEY_FILE}]."
|
|
1295
|
-
return 0
|
|
1296
|
-
fi
|
|
1297
|
-
else
|
|
1298
|
-
printf '%s\n' "Skipped downloading new key file. See \
|
|
1299
|
-
https://cloud.google.com/iam/docs/creating-managing-service-account-keys \
|
|
1300
|
-
to learn more about service account key files."
|
|
1301
|
-
return 0
|
|
1302
|
-
fi
|
|
1303
|
-
}
|
|
1304
|
-
|
|
1305
|
-
#######################################
|
|
1306
|
-
# Make sure a service account for this integration exists and set the email of
|
|
1307
|
-
# the service account to the global variable `SA_NAME`.
|
|
1308
|
-
# Globals:
|
|
1309
|
-
# GCP_PROJECT
|
|
1310
|
-
# SA_KEY_FILE
|
|
1311
|
-
# SA_NAME
|
|
1312
|
-
# DEFAULT_SERVICE_ACCOUNT
|
|
1313
|
-
# Arguments:
|
|
1314
|
-
# None
|
|
1315
|
-
#######################################
|
|
1316
|
-
confirm_service_account() {
|
|
1317
|
-
cat <<EOF
|
|
1318
|
-
Some external APIs might require authentication based on OAuth or \
|
|
1319
|
-
JWT(service account), for example, Google Analytics or Campaign Manager. \
|
|
1320
|
-
In this step, you prepare the service account. For more information, see \
|
|
1321
|
-
https://cloud.google.com/iam/docs/creating-managing-service-accounts
|
|
1322
|
-
EOF
|
|
1323
|
-
|
|
1324
|
-
local suffix
|
|
1325
|
-
suffix=$(get_sa_domain_from_gcp_id "${GCP_PROJECT}")
|
|
1326
|
-
local email
|
|
1327
|
-
if [[ -f "${SA_KEY_FILE}" && -s "${SA_KEY_FILE}" ]]; then
|
|
1328
|
-
email=$(get_value_from_json_file "${SA_KEY_FILE}" 'client_email')
|
|
1329
|
-
if [[ ${email} =~ .*("@${suffix}") ]]; then
|
|
1330
|
-
printf '%s' "A key file for service account [${email}] already exists. \
|
|
1331
|
-
Would you like to create a new service account? [N/y]: "
|
|
1332
|
-
local input
|
|
1333
|
-
read -r input
|
|
1334
|
-
if [[ ${input} != 'y' && ${input} != 'Y' ]]; then
|
|
1335
|
-
printf '%s\n' "OK. Will use existing service account [${email}]."
|
|
1336
|
-
SA_NAME=$(printf "${email}" | cut -d@ -f1)
|
|
1337
|
-
return 0
|
|
1338
|
-
fi
|
|
1339
|
-
fi
|
|
1340
|
-
fi
|
|
1341
|
-
|
|
1342
|
-
SA_NAME="${SA_NAME:-"${PROJECT_NAMESPACE}-api"}"
|
|
1343
|
-
while :; do
|
|
1344
|
-
printf '%s' "Enter the name of service account [${SA_NAME}]: "
|
|
1345
|
-
local input sa_elements=() sa
|
|
1346
|
-
read -r input
|
|
1347
|
-
input=${input:-"${SA_NAME}"}
|
|
1348
|
-
IFS='@' read -a sa_elements <<< "${input}"
|
|
1349
|
-
if [[ ${#sa_elements[@]} == 1 ]]; then
|
|
1350
|
-
echo " Append default suffix to service account name and get: ${email}"
|
|
1351
|
-
sa="${input}"
|
|
1352
|
-
email="${sa}@${suffix}"
|
|
1353
|
-
else
|
|
1354
|
-
if [[ ${sa_elements[1]} != "${suffix}" ]]; then
|
|
1355
|
-
printf '%s\n' " Error: Service account domain name ${sa_elements[1]} \
|
|
1356
|
-
doesn't belong to the current project. The service account domain name for the \
|
|
1357
|
-
current project should be: ${suffix}."
|
|
1358
|
-
continue
|
|
1359
|
-
fi
|
|
1360
|
-
sa="${sa_elements[0]}"
|
|
1361
|
-
email="${input}"
|
|
1362
|
-
fi
|
|
1363
|
-
|
|
1364
|
-
printf '%s\n' "Checking the existence of the service account [${email}]..."
|
|
1365
|
-
if ! result=$(gcloud iam service-accounts describe "${email}" 2>&1); then
|
|
1366
|
-
printf '%s\n' " Service account [${email}] does not exist. Trying to \
|
|
1367
|
-
create..."
|
|
1368
|
-
gcloud iam service-accounts create "${sa}" --display-name \
|
|
1369
|
-
"Tentacles API requester"
|
|
1370
|
-
if [[ $? -gt 0 ]]; then
|
|
1371
|
-
printf '%s\n' "Creating the service account [${email}] failed. Please \
|
|
1372
|
-
try again..."
|
|
1373
|
-
else
|
|
1374
|
-
printf '%s\n' "The service account [${email}] was successfully created."
|
|
1375
|
-
SA_NAME=${sa}
|
|
1376
|
-
break
|
|
1377
|
-
fi
|
|
1378
|
-
else
|
|
1379
|
-
printf ' found.\n'
|
|
1380
|
-
SA_NAME=${sa}
|
|
1381
|
-
break
|
|
1382
|
-
fi
|
|
1383
|
-
done
|
|
1384
|
-
printf '%s\n' "OK. Service account [${SA_NAME}] is ready."
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1387
1320
|
#######################################
|
|
1388
1321
|
# Guide an OAuth process and save the token to file.
|
|
1389
1322
|
# The whole process will be:
|
|
@@ -1393,8 +1326,8 @@ try again..."
|
|
|
1393
1326
|
# 3. Prompt user to enter the OAuth Client secret.
|
|
1394
1327
|
# 4. Print the OAuth authentication URL which users should open in a browser \
|
|
1395
1328
|
# and complete the process.
|
|
1396
|
-
# 5. Copy the
|
|
1397
|
-
# 6. Use the
|
|
1329
|
+
# 5. Copy the authorization code from the browser and paste here.
|
|
1330
|
+
# 6. Use the authorization code to redeem an OAuth token and save it.
|
|
1398
1331
|
# Globals:
|
|
1399
1332
|
# ENABLED_OAUTH_SCOPES
|
|
1400
1333
|
# OAUTH2_TOKEN_JSON
|
|
@@ -1480,10 +1413,10 @@ ${auth_url}"
|
|
|
1480
1413
|
"Error 400: redirect_uri_mismatch" shown up on the page. In this case, press \
|
|
1481
1414
|
"Enter" to start again with a native application OAuth client ID.
|
|
1482
1415
|
EOF
|
|
1483
|
-
printf '%s' "4. Copy the
|
|
1416
|
+
printf '%s' "4. Copy the authorization code from browser and paste here: "
|
|
1484
1417
|
read -r auth_code
|
|
1485
1418
|
if [[ -z ${auth_code} ]]; then
|
|
1486
|
-
printf '%s\n\n' "No
|
|
1419
|
+
printf '%s\n\n' "No authorization code. Starting from beginning again..."
|
|
1487
1420
|
continue
|
|
1488
1421
|
fi
|
|
1489
1422
|
auth_response=$(curl -s -d "code=${auth_code}" -d "client_id=${client_id}" \
|
|
@@ -1492,7 +1425,7 @@ EOF
|
|
|
1492
1425
|
auth_error=$(node -e "console.log(!!JSON.parse(process.argv[1]).error)" \
|
|
1493
1426
|
"${auth_response}")
|
|
1494
1427
|
if [[ ${auth_error} == "true" ]]; then
|
|
1495
|
-
printf '%s\n' "Error happened in redeem the
|
|
1428
|
+
printf '%s\n' "Error happened in redeem the authorization code: \
|
|
1496
1429
|
${auth_response}"
|
|
1497
1430
|
continue
|
|
1498
1431
|
fi
|
|
@@ -1549,7 +1482,7 @@ set_authentication_env_for_cloud_functions() {
|
|
|
1549
1482
|
}
|
|
1550
1483
|
|
|
1551
1484
|
#######################################
|
|
1552
|
-
# Create or update a Cloud
|
|
1485
|
+
# Create or update a Cloud Scheduler
|
|
1553
1486
|
# Globals:
|
|
1554
1487
|
# None
|
|
1555
1488
|
# Arguments:
|
|
@@ -1635,8 +1568,27 @@ print_finished(){
|
|
|
1635
1568
|
print_service_account(){
|
|
1636
1569
|
printf '%s\n' "=========================="
|
|
1637
1570
|
local email
|
|
1638
|
-
email=$(
|
|
1639
|
-
printf '%s\n' "The email address of the current service account is ${email}."
|
|
1571
|
+
email=$(get_service_account)
|
|
1572
|
+
printf '%s\n' "The email address of the current service account is: ${email}."
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
#######################################
|
|
1576
|
+
# Returns the email address of the service account. If there is a key file of
|
|
1577
|
+
# service account, it returns the email of that service account; otherwise it
|
|
1578
|
+
# it will get the default service account of Cloud Functions.
|
|
1579
|
+
# Globals:
|
|
1580
|
+
# SA_KEY_FILE
|
|
1581
|
+
# Arguments:
|
|
1582
|
+
# None
|
|
1583
|
+
#######################################
|
|
1584
|
+
get_service_account(){
|
|
1585
|
+
local email
|
|
1586
|
+
if [[ -f "${SA_KEY_FILE}" ]]; then
|
|
1587
|
+
email=$(get_value_from_json_file "${SA_KEY_FILE}" 'client_email')
|
|
1588
|
+
else
|
|
1589
|
+
email=$(get_cloud_functions_service_account)
|
|
1590
|
+
fi
|
|
1591
|
+
printf '%s' "${email}"
|
|
1640
1592
|
}
|
|
1641
1593
|
|
|
1642
1594
|
#######################################
|
|
@@ -1843,25 +1795,25 @@ sed -r 's/^([^:]*):(.*)$/\2.\1/').iam.gserviceaccount.com"
|
|
|
1843
1795
|
|
|
1844
1796
|
#######################################
|
|
1845
1797
|
# Returns the default service account of the given Cloud Functions.
|
|
1846
|
-
# Use cases:
|
|
1847
|
-
# 1. When BigQuery query Google Sheet based external tables, this service
|
|
1848
|
-
# account needs to be added to the Google Sheet as a viewer.
|
|
1849
1798
|
# Globals:
|
|
1850
1799
|
# None
|
|
1851
1800
|
# Arguments:
|
|
1852
|
-
#
|
|
1801
|
+
# The name of Cloud Functions, default value is a random one starts with the
|
|
1802
|
+
# namespace.
|
|
1853
1803
|
# Returns:
|
|
1854
1804
|
# The default service account of the given Cloud Functions.
|
|
1855
1805
|
#######################################
|
|
1856
1806
|
get_cloud_functions_service_account() {
|
|
1857
|
-
local
|
|
1858
|
-
| grep "${1}" |
|
|
1859
|
-
if [[
|
|
1860
|
-
printf '%s\n' "Cloud Functions [$1] doesn't exist."
|
|
1807
|
+
local cf=($(gcloud functions list --format="csv[no-heading,separator=\
|
|
1808
|
+
' '](name,REGION)" | grep "${1:-"${PROJECT_NAMESPACE}"}" | head -1))
|
|
1809
|
+
if [[ ${#cf[@]} -lt 1 ]]; then
|
|
1810
|
+
printf '%s\n' "Cloud Functions [${1}] doesn't exist."
|
|
1861
1811
|
else
|
|
1862
|
-
local
|
|
1812
|
+
local name="${cf[0]}"
|
|
1813
|
+
local region="${cf[1]}"
|
|
1814
|
+
local service_account=$(gcloud functions describe "${name}" \
|
|
1863
1815
|
--region="${region}" --format="get(serviceAccountEmail)")
|
|
1864
|
-
printf '%s' "$service_account"
|
|
1816
|
+
printf '%s' "${service_account}"
|
|
1865
1817
|
fi
|
|
1866
1818
|
}
|
|
1867
1819
|
|
|
@@ -1903,7 +1855,7 @@ EOF
|
|
|
1903
1855
|
#######################################
|
|
1904
1856
|
quit_if_failed() {
|
|
1905
1857
|
printf '\n'
|
|
1906
|
-
if [[ $1 -gt 0 ]];then
|
|
1858
|
+
if [[ ${1} -gt 0 ]];then
|
|
1907
1859
|
printf '%s\n' "[Error] Quit."
|
|
1908
1860
|
exit 1
|
|
1909
1861
|
fi
|
package/index.js
CHANGED
|
@@ -26,3 +26,4 @@ exports.pubsub = require('./src/components/pubsub.js');
|
|
|
26
26
|
exports.scheduler = require('./src/components/scheduler.js');
|
|
27
27
|
exports.storage = require('./src/components/storage.js');
|
|
28
28
|
exports.utils = require('./src/components/utils.js');
|
|
29
|
+
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": "0.
|
|
3
|
+
"version": "0.9.2-beta2",
|
|
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,6 +16,7 @@
|
|
|
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": "^1.10.1",
|
|
19
20
|
"@google-cloud/automl": "^2.4.2",
|
|
20
21
|
"@google-cloud/bigquery": "^5.6.0",
|
|
21
22
|
"@google-cloud/datastore": "^6.4.0",
|
|
@@ -24,8 +25,8 @@
|
|
|
24
25
|
"@google-cloud/pubsub": "^2.12.0",
|
|
25
26
|
"@google-cloud/storage": "^5.8.5",
|
|
26
27
|
"gaxios": "^4.3.0",
|
|
27
|
-
"google-ads-api": "^
|
|
28
|
-
"google-ads-node": "^
|
|
28
|
+
"google-ads-api": "^8.1.0",
|
|
29
|
+
"google-ads-node": "^6.1.3",
|
|
29
30
|
"google-auth-library": "^7.1.0",
|
|
30
31
|
"googleapis": "^74.2.0",
|
|
31
32
|
"soap": "^0.38.0",
|
package/src/apis/analytics.js
CHANGED
|
@@ -23,7 +23,7 @@ const stream = require('stream');
|
|
|
23
23
|
const {google} = require('googleapis');
|
|
24
24
|
const {Schema$Upload} = google.analytics;
|
|
25
25
|
const AuthClient = require('./auth_client.js');
|
|
26
|
-
const {wait, getLogger} = require('../components/utils.js');
|
|
26
|
+
const {wait, getLogger, BatchResult} = require('../components/utils.js');
|
|
27
27
|
|
|
28
28
|
const API_SCOPES = Object.freeze([
|
|
29
29
|
'https://www.googleapis.com/auth/analytics',
|
|
@@ -56,8 +56,13 @@ let DataImportClearConfig;
|
|
|
56
56
|
* Google Analytics API v3 stub.
|
|
57
57
|
*/
|
|
58
58
|
class Analytics {
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
/**
|
|
60
|
+
* @constructor
|
|
61
|
+
* @param {!Object<string,string>=} env The environment object to hold env
|
|
62
|
+
* variables.
|
|
63
|
+
*/
|
|
64
|
+
constructor(env = process.env) {
|
|
65
|
+
const authClient = new AuthClient(API_SCOPES, env);
|
|
61
66
|
const auth = authClient.getDefaultAuth();
|
|
62
67
|
/** @type {!google.analytics} */
|
|
63
68
|
this.instance = google.analytics({
|
|
@@ -76,8 +81,7 @@ class Analytics {
|
|
|
76
81
|
* @param {string|!stream.Readable} data A string or a stream to be uploaded.
|
|
77
82
|
* @param {!DataImportConfig} config GA data import configuration.
|
|
78
83
|
* @param {string=} batchId A tag for log.
|
|
79
|
-
* @return {!Promise
|
|
80
|
-
* succeeded.
|
|
84
|
+
* @return {!Promise<!BatchResult>}
|
|
81
85
|
*/
|
|
82
86
|
async uploadData(data, config, batchId = 'unnamed') {
|
|
83
87
|
const uploadConfig = Object.assign(
|
|
@@ -95,28 +99,38 @@ class Analytics {
|
|
|
95
99
|
this.logger.debug('Response: ', response);
|
|
96
100
|
const job = /** @type {Schema$Upload} */ response.data;
|
|
97
101
|
const uploadId = (/** @type {Schema$Upload} */job).id;
|
|
98
|
-
|
|
102
|
+
this.logger.info(
|
|
103
|
+
`Task [${batchId}] creates GA Data import job: ${uploadId}`);
|
|
99
104
|
const jobConfig = Object.assign({uploadId}, config);
|
|
100
105
|
const result = await Promise.race([
|
|
101
106
|
this.checkJobStatus(jobConfig),
|
|
102
107
|
wait(8 * 60 * 1000, job), // wait up to 8 minutes here
|
|
103
108
|
]);
|
|
109
|
+
/** @type {BatchResult} */ const batchResult = {};
|
|
104
110
|
switch ((/** @type {Schema$Upload} */ result).status) {
|
|
105
111
|
case 'FAILED':
|
|
106
|
-
this.logger.error('GA Data Import failed',
|
|
107
|
-
|
|
112
|
+
this.logger.error('GA Data Import failed', result);
|
|
113
|
+
batchResult.result = false;
|
|
114
|
+
batchResult.errors = result.errors
|
|
115
|
+
|| [`Unknown reason. ID: ${uploadId}`];
|
|
116
|
+
break;
|
|
108
117
|
case 'COMPLETED':
|
|
109
118
|
this.logger.info(`GA Data Import job[${uploadId}] completed.`);
|
|
110
|
-
this.logger.debug('Response: ',
|
|
111
|
-
|
|
119
|
+
this.logger.debug('Response: ', result);
|
|
120
|
+
batchResult.result = true;
|
|
121
|
+
break;
|
|
112
122
|
case 'PENDING':
|
|
113
|
-
this.logger.info('GA Data Import pending.',
|
|
123
|
+
this.logger.info('GA Data Import pending.', result);
|
|
114
124
|
this.logger.info('Still will return true here.');
|
|
115
|
-
|
|
125
|
+
batchResult.result = true;
|
|
126
|
+
break;
|
|
116
127
|
default:
|
|
117
|
-
this.logger.error('Unknown results of GA Data Import: ',
|
|
118
|
-
|
|
128
|
+
this.logger.error('Unknown results of GA Data Import: ', result);
|
|
129
|
+
batchResult.result = false;
|
|
130
|
+
batchResult.errors = [`Unknown status. ID: ${uploadId}`];
|
|
119
131
|
}
|
|
132
|
+
console.log(batchResult);
|
|
133
|
+
return batchResult;
|
|
120
134
|
}
|
|
121
135
|
|
|
122
136
|
/**
|
package/src/apis/auth_client.js
CHANGED
|
@@ -55,11 +55,13 @@ class AuthClient {
|
|
|
55
55
|
/**
|
|
56
56
|
* Create a new instance with given API scopes.
|
|
57
57
|
* @param {string|!Array<string>|!ReadonlyArray<string>} scopes
|
|
58
|
+
* @param {!Object<string,string>=} env The environment object to hold env
|
|
59
|
+
* variables.
|
|
58
60
|
*/
|
|
59
|
-
constructor(scopes) {
|
|
61
|
+
constructor(scopes, env = process.env) {
|
|
60
62
|
this.scopes = scopes;
|
|
61
|
-
this.oauthTokenFile = getFullPathForEnv(DEFAULT_ENV_OAUTH);
|
|
62
|
-
this.serviceAccountKeyFile = getFullPathForEnv(DEFAULT_ENV_KEYFILE);
|
|
63
|
+
this.oauthTokenFile = getFullPathForEnv(DEFAULT_ENV_OAUTH, env);
|
|
64
|
+
this.serviceAccountKeyFile = getFullPathForEnv(DEFAULT_ENV_KEYFILE, env);
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
/**
|
|
@@ -109,7 +111,7 @@ class AuthClient {
|
|
|
109
111
|
*/
|
|
110
112
|
getOAuth2Client(keyFile = this.oauthTokenFile) {
|
|
111
113
|
const key = JSON.parse(fs.readFileSync(keyFile).toString());
|
|
112
|
-
console.log(`Get OAuth token with
|
|
114
|
+
console.log(`Get OAuth token with client Id: ${key.client_id}`);
|
|
113
115
|
const oAuth2Client = new OAuth2Client(key.client_id, key.client_secret);
|
|
114
116
|
oAuth2Client.setCredentials({refresh_token: key.token.refresh_token});
|
|
115
117
|
return oAuth2Client;
|
|
@@ -152,15 +154,18 @@ class AuthClient {
|
|
|
152
154
|
* Returns the full path of a existent file whose path (relative or
|
|
153
155
|
* absolute) is the value of the given environment variable.
|
|
154
156
|
* @param {string} envName The name of environment variable for the file path.
|
|
157
|
+
* @param {!Object<string,string>=} env The environment object to hold env
|
|
158
|
+
* variables.
|
|
155
159
|
* @return {?string} Full path of the file what set as an environment variable.
|
|
156
160
|
*/
|
|
157
|
-
function getFullPathForEnv(envName) {
|
|
158
|
-
|
|
161
|
+
function getFullPathForEnv(envName, env) {
|
|
162
|
+
const envValue = env[envName];
|
|
163
|
+
if (typeof envValue === 'undefined') {
|
|
159
164
|
console.log(`Env[${envName}] doesn't have a value.`);
|
|
160
165
|
} else {
|
|
161
|
-
const fullPath =
|
|
162
|
-
|
|
163
|
-
path.join(__dirname,
|
|
166
|
+
const fullPath = envValue.startsWith('/') ?
|
|
167
|
+
envValue :
|
|
168
|
+
path.join(__dirname, envValue);
|
|
164
169
|
if (fs.existsSync(fullPath)) {
|
|
165
170
|
console.log(`Find file '${fullPath}' set in env as [${envName}].`);
|
|
166
171
|
return fullPath;
|
|
@@ -32,11 +32,11 @@ const API_VERSION = 'v1';
|
|
|
32
32
|
* Google cloud client libraries.
|
|
33
33
|
*/
|
|
34
34
|
class CloudPlatformApis {
|
|
35
|
-
constructor() {
|
|
35
|
+
constructor(projectId = process.env['GCP_PROJECT']) {
|
|
36
36
|
/** @const {!AuthClient} */
|
|
37
37
|
const authClient = new AuthClient(API_SCOPES);
|
|
38
38
|
this.auth = authClient.getApplicationDefaultCredentials();
|
|
39
|
-
this.projectId =
|
|
39
|
+
this.projectId = projectId;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
/**
|
package/src/apis/google_ads.js
CHANGED
|
@@ -18,24 +18,23 @@
|
|
|
18
18
|
'use strict';
|
|
19
19
|
|
|
20
20
|
const {protos: {google: {ads: {googleads}}}} = require('google-ads-node');
|
|
21
|
+
const googleAdsLib = googleads[Object.keys(googleads)[0]];
|
|
21
22
|
const {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
} = googleads;
|
|
23
|
+
common: {
|
|
24
|
+
UserData,
|
|
25
|
+
UserIdentifier,
|
|
26
|
+
CustomerMatchUserListMetadata,
|
|
27
|
+
},
|
|
28
|
+
resources: {
|
|
29
|
+
GoogleAdsField,
|
|
30
|
+
},
|
|
31
|
+
services: {
|
|
32
|
+
UploadClickConversionsRequest,
|
|
33
|
+
UploadUserDataRequest,
|
|
34
|
+
UserDataOperation,
|
|
35
|
+
SearchGoogleAdsFieldsRequest,
|
|
36
|
+
},
|
|
37
|
+
} = googleAdsLib;
|
|
39
38
|
const {GoogleAdsApi} = require('google-ads-api');
|
|
40
39
|
const AuthClient = require('./auth_client.js');
|
|
41
40
|
const {getLogger} = require('../components/utils.js');
|
|
@@ -64,7 +64,7 @@ class MeasurementProtocol {
|
|
|
64
64
|
* @param {string} batchId The tag for log.
|
|
65
65
|
* @return {!Promise<boolean>}
|
|
66
66
|
*/
|
|
67
|
-
return (lines, batchId) => {
|
|
67
|
+
return async (lines, batchId) => {
|
|
68
68
|
const payload =
|
|
69
69
|
lines
|
|
70
70
|
.map((line) => {
|
|
@@ -85,24 +85,37 @@ class MeasurementProtocol {
|
|
|
85
85
|
body: payload,
|
|
86
86
|
headers: {'User-Agent': 'Tentacles/MeasurementProtocol-v1'}
|
|
87
87
|
};
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
88
|
+
const response = await request(requestOptions);
|
|
89
|
+
console.log(response);
|
|
90
|
+
if (response.status < 200 || response.status >= 300) {
|
|
91
|
+
const errorMessages = [
|
|
92
|
+
`Measurement Protocol [${batchId}] didn't succeed.`,
|
|
93
|
+
`Get response code: ${response.status}`,
|
|
94
|
+
`response: ${response.data}`,
|
|
95
|
+
];
|
|
96
|
+
console.error(errorMessages.join('\n'));
|
|
97
|
+
throw new Error(`Status code not 2XX`);
|
|
98
|
+
}
|
|
99
|
+
this.logger.debug(`Configuration:`, config);
|
|
100
|
+
this.logger.debug(`Input Data: `, lines);
|
|
101
|
+
this.logger.debug(`Batch[${batchId}] status: ${response.status}`);
|
|
102
|
+
this.logger.debug(response.data);
|
|
103
|
+
// There is not enough information from the non-debug mode.
|
|
104
|
+
if (!this.debugMode) return true;
|
|
105
|
+
const failedHits = [];
|
|
106
|
+
const failedReasons = new Set();
|
|
107
|
+
const errorMessage = response.data.hitParsingResult
|
|
108
|
+
.forEach((result, index) => {
|
|
109
|
+
if (!result.valid) {
|
|
110
|
+
failedHits.push(lines[index]);
|
|
111
|
+
result.parserMessage.forEach(({description}) => {
|
|
112
|
+
failedReasons.add(description);
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
console.log(failedHits);
|
|
117
|
+
console.log(failedReasons);
|
|
118
|
+
return errorMessage;
|
|
106
119
|
};
|
|
107
120
|
};
|
|
108
121
|
|
|
@@ -217,11 +217,14 @@ class FirestoreAccessBase {
|
|
|
217
217
|
* Returns whether the mode of Firestore is 'Native'.
|
|
218
218
|
* @return {!Promise<boolean>}
|
|
219
219
|
*/
|
|
220
|
-
static isNativeMode() {
|
|
221
|
-
|
|
220
|
+
static async isNativeMode(projectId = process.env['GCP_PROJECT']) {
|
|
221
|
+
try {
|
|
222
|
+
await new Firestore({projectId}).listCollections();
|
|
223
|
+
return true;
|
|
224
|
+
} catch (error) {
|
|
222
225
|
console.log(`In detecting Firestore mode: `, error.message);
|
|
223
226
|
return false;
|
|
224
|
-
}
|
|
227
|
+
}
|
|
225
228
|
};
|
|
226
229
|
}
|
|
227
230
|
|
|
@@ -54,19 +54,22 @@ class DataAccessObject {
|
|
|
54
54
|
* @param {string} kind The data model name.
|
|
55
55
|
* @param {string} namespace The namespace of the data.
|
|
56
56
|
* @param {!DataSource} dataSource The data source type.
|
|
57
|
+
* @param {string} projectId The Id of Cloud project.
|
|
57
58
|
*/
|
|
58
59
|
constructor(kind, namespace,
|
|
59
|
-
dataSource = DataAccessObject.getDataSourceFromEnvironment()
|
|
60
|
+
dataSource = DataAccessObject.getDataSourceFromEnvironment(),
|
|
61
|
+
projectId = process.env['GCP_PROJECT']) {
|
|
60
62
|
/** @const {string} */ this.namespace = namespace;
|
|
61
63
|
/** @const {!DataSource} */ this.dataSource = dataSource;
|
|
62
64
|
/** @type {!FirestoreAccessBase} */ this.accessObject = undefined;
|
|
63
65
|
switch (this.dataSource) {
|
|
64
66
|
case DataSource.FIRESTORE:
|
|
65
67
|
this.accessObject = new NativeModeAccess(
|
|
66
|
-
`${this.namespace}/database/${kind}
|
|
68
|
+
`${this.namespace}/database/${kind}`, projectId);
|
|
67
69
|
break;
|
|
68
70
|
case DataSource.DATASTORE:
|
|
69
|
-
this.accessObject = new DatastoreModeAccess(this.namespace, kind
|
|
71
|
+
this.accessObject = new DatastoreModeAccess(this.namespace, kind,
|
|
72
|
+
projectId);
|
|
70
73
|
break;
|
|
71
74
|
default:
|
|
72
75
|
throw new Error(`Unknown DataSource item: ${this.dataSource}.`);
|
|
@@ -46,10 +46,11 @@ class DatastoreModeAccess {
|
|
|
46
46
|
* Initializes DatastoreModeAccess instance.
|
|
47
47
|
* @param {string} namespace The namespace for data.
|
|
48
48
|
* @param {string} kind The kind of this entity.
|
|
49
|
+
* @param {string} projectId The Id of Cloud project.
|
|
49
50
|
*/
|
|
50
|
-
constructor(namespace, kind) {
|
|
51
|
+
constructor(namespace, kind, projectId = process.env['GCP_PROJECT']) {
|
|
51
52
|
/** @type{Datastore} */
|
|
52
|
-
this.datastore = new Datastore();
|
|
53
|
+
this.datastore = new Datastore({projectId});
|
|
53
54
|
this.kind = kind;
|
|
54
55
|
this.namespace = namespace;
|
|
55
56
|
this.logger = getLogger('DS.ACC');
|
|
@@ -41,10 +41,11 @@ class NativeModeAccess {
|
|
|
41
41
|
* way. This constructor will check the path to make sure it presents a
|
|
42
42
|
* 'collection', otherwise an Error will be thrown.
|
|
43
43
|
* @param {string} path Path for the 'collection'.
|
|
44
|
+
* @param {string} projectId The Id of Cloud project.
|
|
44
45
|
*/
|
|
45
|
-
constructor(path) {
|
|
46
|
+
constructor(path, projectId = process.env['GCP_PROJECT']) {
|
|
46
47
|
/** @type {!Firestore} */
|
|
47
|
-
this.firestore = new Firestore();
|
|
48
|
+
this.firestore = new Firestore({projectId});
|
|
48
49
|
if (path.split('/').length % 2 === 0) {
|
|
49
50
|
throw new Error(`Invalid path for Collection: ${path}`);
|
|
50
51
|
}
|
|
@@ -30,11 +30,11 @@ const API_VERSION = 'v1';
|
|
|
30
30
|
* to get/pause/resume a job.
|
|
31
31
|
*/
|
|
32
32
|
class CloudScheduler {
|
|
33
|
-
constructor() {
|
|
33
|
+
constructor(projectId = process.env['GCP_PROJECT']) {
|
|
34
34
|
/** @const {!AuthClient} */
|
|
35
35
|
const authClient = new AuthClient(API_SCOPES);
|
|
36
36
|
this.auth = authClient.getApplicationDefaultCredentials();
|
|
37
|
-
this.projectId =
|
|
37
|
+
this.projectId = projectId;
|
|
38
38
|
this.instance = cloudscheduler({
|
|
39
39
|
version: API_VERSION,
|
|
40
40
|
auth: this.auth,
|
package/src/components/utils.js
CHANGED
|
@@ -23,12 +23,23 @@ const {inspect} = require('util');
|
|
|
23
23
|
const {LoggingWinston} = require('@google-cloud/logging-winston');
|
|
24
24
|
const {CloudPlatformApis} = require('../apis/cloud_platform_apis.js');
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* The result of a batch of data sent to target API.
|
|
28
|
+
*
|
|
29
|
+
* @typedef {{
|
|
30
|
+
* result: boolean,
|
|
31
|
+
* errors: (Array<string>|undefined),
|
|
32
|
+
* output: (Array<string>|undefined),
|
|
33
|
+
* }}
|
|
34
|
+
*/
|
|
35
|
+
let BatchResult;
|
|
36
|
+
|
|
26
37
|
/**
|
|
27
38
|
* Function which sends a batch of data. Takes two parameters:
|
|
28
39
|
* {!Array<string>} Data for single request. It should be guaranteed that it
|
|
29
40
|
* doesn't exceed quota limitation.
|
|
30
41
|
* {string} The tag for log.
|
|
31
|
-
* @typedef {function(!Array<string>,string): !Promise
|
|
42
|
+
* @typedef {function(!Array<string>,string): !Promise<!BatchResult>}
|
|
32
43
|
*/
|
|
33
44
|
let SendSingleBatch;
|
|
34
45
|
|
|
@@ -414,10 +425,12 @@ const extractObject = (paths) => {
|
|
|
414
425
|
* status code 1 to let the invoker (the Bash installation script) know that it
|
|
415
426
|
* doesn't pass.
|
|
416
427
|
* @param {!Array<string>} permissions Array of permissions to check.
|
|
428
|
+
* @param {string} projectId The Id of Cloud project.
|
|
417
429
|
* @return {!Promise<undefined>}
|
|
418
430
|
*/
|
|
419
|
-
const checkPermissions = (permissions
|
|
420
|
-
|
|
431
|
+
const checkPermissions = (permissions,
|
|
432
|
+
projectId = process.env['GCP_PROJECT']) => {
|
|
433
|
+
const cloudPlatformApis = new CloudPlatformApis(projectId);
|
|
421
434
|
return cloudPlatformApis.testIamPermissions(permissions)
|
|
422
435
|
.then((grantedPermissions) => {
|
|
423
436
|
console.log(grantedPermissions);
|
|
@@ -461,6 +474,7 @@ const changeNamingFromSnakeToLowerCamel = (name) => {
|
|
|
461
474
|
module.exports = {
|
|
462
475
|
getLogger,
|
|
463
476
|
wait,
|
|
477
|
+
BatchResult,
|
|
464
478
|
SendSingleBatch,
|
|
465
479
|
apiSpeedControl,
|
|
466
480
|
splitArray,
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// Copyright 2021 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 Google Cloud Vertex AI API helper.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
'use strict';
|
|
20
|
+
|
|
21
|
+
const {JobServiceClient} = require('@google-cloud/aiplatform');
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Wrapper class for AI Platform (Vertex AI) API.
|
|
25
|
+
* 1. Create or get batch prediction jobs.
|
|
26
|
+
* @see https://cloud.google.com/vertex-ai/docs/reference/rest/v1/projects.locations.batchPredictionJobs#BatchPredictionJob
|
|
27
|
+
*/
|
|
28
|
+
class VertexAi {
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Initialize an instance.
|
|
32
|
+
* @param {{keyFilename:(string|undefined)}} options
|
|
33
|
+
*/
|
|
34
|
+
constructor(options = {}) {
|
|
35
|
+
this.options = options;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Starts a BatchPredictionJob and returns the job name.
|
|
40
|
+
* @see https://googleapis.dev/nodejs/aiplatform/latest/v1.JobServiceClient.html#createBatchPredictionJob
|
|
41
|
+
* @see https://googleapis.dev/nodejs/aiplatform/latest/google.cloud.aiplatform.v1.BatchPredictionJob.html
|
|
42
|
+
* @param {string} projectId
|
|
43
|
+
* @param {string} location
|
|
44
|
+
* @param {string} modelId
|
|
45
|
+
* @param {google.cloud.aiplatform.v1.BatchPredictionJob.IInputConfig} inputConfig
|
|
46
|
+
* @param {google.cloud.aiplatform.v1.BatchPredictionJob.IOutputConfig} outputConfig
|
|
47
|
+
* @param {string=} displayName
|
|
48
|
+
* @return {Promise<string>} BatchPredictionJob name.
|
|
49
|
+
*/
|
|
50
|
+
async batchPredict(projectId, location, modelId, inputConfig,
|
|
51
|
+
outputConfig, displayName = 'Batch Prediction Job') {
|
|
52
|
+
const jobServiceClient = this.getJobServiceClient_(location);
|
|
53
|
+
const parent = `projects/${projectId}/locations/${location}`;
|
|
54
|
+
const model = jobServiceClient.modelPath(projectId, location, modelId);
|
|
55
|
+
const batchPredictionJob = {
|
|
56
|
+
displayName,
|
|
57
|
+
model,
|
|
58
|
+
inputConfig,
|
|
59
|
+
outputConfig,
|
|
60
|
+
};
|
|
61
|
+
const request = {
|
|
62
|
+
parent,
|
|
63
|
+
batchPredictionJob,
|
|
64
|
+
};
|
|
65
|
+
const [response] = await jobServiceClient.createBatchPredictionJob(request);
|
|
66
|
+
return response.name;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Gets a BatchPredictionJob.
|
|
71
|
+
* @see https://googleapis.dev/nodejs/aiplatform/latest/v1.JobServiceClient.html#getBatchPredictionJob
|
|
72
|
+
* @param {string} jobName
|
|
73
|
+
* @param {(string|undefined)=} explicitLocation Location of the service
|
|
74
|
+
* endpoint. A location will be extracted from jobName if omitted.
|
|
75
|
+
* @return {Promise<google.cloud.aiplatform.v1.BatchPredictionJob>}
|
|
76
|
+
*/
|
|
77
|
+
async getBatchPredictionJob(jobName, explicitLocation = undefined) {
|
|
78
|
+
let location = explicitLocation;
|
|
79
|
+
if (!location) {
|
|
80
|
+
location = /projects\/[^\/]*\/locations\/([^\/]*)\/.*/.exec(jobName)[1];
|
|
81
|
+
}
|
|
82
|
+
const jobServiceClient = this.getJobServiceClient_(location);
|
|
83
|
+
const request = {name: jobName};
|
|
84
|
+
const [response] = await jobServiceClient.getBatchPredictionJob(request);
|
|
85
|
+
return response;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Gets the service endpoint for Vertex AI.
|
|
90
|
+
* @see https://cloud.google.com/vertex-ai/docs/general/locations
|
|
91
|
+
* @see https://cloud.google.com/vertex-ai/docs/reference/rest#service-endpoint
|
|
92
|
+
* @param {string} location
|
|
93
|
+
* @return {string} The service endpoint.
|
|
94
|
+
* @private
|
|
95
|
+
*/
|
|
96
|
+
getServiceEndpoint_(location) {
|
|
97
|
+
return `${location}-aiplatform.googleapis.com`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Gets the service for creating and managing AI Platform's jobs.
|
|
102
|
+
* @see https://googleapis.dev/nodejs/aiplatform/latest/v1.JobServiceClient.html
|
|
103
|
+
* @param {string} location
|
|
104
|
+
* @return {!JobServiceClient}
|
|
105
|
+
* @private
|
|
106
|
+
*/
|
|
107
|
+
getJobServiceClient_(location) {
|
|
108
|
+
const clientOptions = {
|
|
109
|
+
apiEndpoint: this.getServiceEndpoint_(location),
|
|
110
|
+
};
|
|
111
|
+
return new JobServiceClient(clientOptions);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
exports.VertexAi = VertexAi;
|