@google-cloud/nodejs-common 1.0.5 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/bin/apps_scripts.sh +209 -0
- package/bin/bigquery.sh +53 -0
- package/bin/google_ads.sh +115 -0
- package/bin/install_functions.sh +124 -38
- package/package.json +16 -16
- package/src/components/automl.js +36 -56
- package/src/components/scheduler.js +36 -24
- package/src/components/vertex_ai.js +4 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# NodeJS Common Library
|
|
2
2
|
|
|
3
|
-
<!--* freshness: { owner: 'lushu' reviewed: '
|
|
3
|
+
<!--* freshness: { owner: 'lushu' reviewed: '2022-04-07' } *-->
|
|
4
4
|
|
|
5
5
|
A NodeJs common library for other projects, e.g. [GMP and Google Ads Connector]
|
|
6
6
|
and [Data Tasks Coordinator]. This library includes:
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2022 Google Inc.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
# Const the folder name of Apps Script.
|
|
18
|
+
DEFAULT_APPS_SCRIPT_FOLDER="apps_script"
|
|
19
|
+
|
|
20
|
+
#######################################
|
|
21
|
+
# Clasp login.
|
|
22
|
+
# Globals:
|
|
23
|
+
# None
|
|
24
|
+
# Arguments:
|
|
25
|
+
# None
|
|
26
|
+
#######################################
|
|
27
|
+
clasp_login() {
|
|
28
|
+
while :; do
|
|
29
|
+
local claspLogin=$(clasp login --status)
|
|
30
|
+
if [[ "${claspLogin}" != "You are not logged in." ]]; then
|
|
31
|
+
printf '%s' "${claspLogin} Would you like to continue with it? [Y/n]"
|
|
32
|
+
local logout
|
|
33
|
+
read -r logout
|
|
34
|
+
logout=${logout:-"Y"}
|
|
35
|
+
if [[ ${logout} == "Y" || ${logout} == "y" ]]; then
|
|
36
|
+
break
|
|
37
|
+
else
|
|
38
|
+
clasp logout
|
|
39
|
+
fi
|
|
40
|
+
fi
|
|
41
|
+
clasp login --no-localhost
|
|
42
|
+
done
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
#######################################
|
|
46
|
+
# Initialize a AppsScript project. Usually it involves following steps:
|
|
47
|
+
# 1. Create a AppsScript project within a new Google Sheet.
|
|
48
|
+
# 2. Prompt to update the Google Cloud Project number of the AppsScript project
|
|
49
|
+
# to enable external APIs for this AppsScript project.
|
|
50
|
+
# 3. Prompt to grant the access of Cloud Functions' default service account to
|
|
51
|
+
# this Google Sheet, so the Cloud Functions can query this Sheet later.
|
|
52
|
+
# 4. Initialize the Sheet based on requests.
|
|
53
|
+
# Globals:
|
|
54
|
+
# None
|
|
55
|
+
# Arguments:
|
|
56
|
+
# The Google Sheet name.
|
|
57
|
+
# The folder for Apps Script code, default value ${DEFAULT_APPS_SCRIPT_FOLDER}
|
|
58
|
+
#######################################
|
|
59
|
+
clasp_initialize() {
|
|
60
|
+
((STEP += 1))
|
|
61
|
+
printf '%s\n' "Step ${STEP}: Starting to create Google Sheets..."
|
|
62
|
+
local sheetName="${1}"
|
|
63
|
+
local apps_script_src="${2-"${DEFAULT_APPS_SCRIPT_FOLDER}"}"
|
|
64
|
+
clasp_login
|
|
65
|
+
while :; do
|
|
66
|
+
local claspStatus=$(
|
|
67
|
+
clasp status -P "${apps_script_src}" >/dev/null 2>&1
|
|
68
|
+
echo $?
|
|
69
|
+
)
|
|
70
|
+
if [[ $claspStatus -gt 0 ]]; then
|
|
71
|
+
clasp create --type sheets --title "${sheetName}" --rootDir "${apps_script_src}"
|
|
72
|
+
local createResult=$?
|
|
73
|
+
if [[ $createResult -gt 0 ]]; then
|
|
74
|
+
printf '%s' "Press any key to continue after you enable the Google \
|
|
75
|
+
Apps Script API: https://script.google.com/home/usersettings..."
|
|
76
|
+
local any
|
|
77
|
+
read -n1 -s any
|
|
78
|
+
printf '\n\n'
|
|
79
|
+
continue
|
|
80
|
+
fi
|
|
81
|
+
break
|
|
82
|
+
else
|
|
83
|
+
printf '%s' "AppsScript project exists. Would you like to continue with \
|
|
84
|
+
it? [Y/n]"
|
|
85
|
+
local useCurrent
|
|
86
|
+
read -r useCurrent
|
|
87
|
+
useCurrent=${useCurrent:-"Y"}
|
|
88
|
+
if [[ ${useCurrent} = "Y" || ${useCurrent} = "y" ]]; then
|
|
89
|
+
break
|
|
90
|
+
else
|
|
91
|
+
printf '%s' "Would you like to delete current AppsScript and create a \
|
|
92
|
+
new one? [N/y]"
|
|
93
|
+
local deleteCurrent
|
|
94
|
+
read -r deleteCurrent
|
|
95
|
+
deleteCurrent=${deleteCurrent:-"N"}
|
|
96
|
+
if [[ ${deleteCurrent} = "Y" || ${deleteCurrent} = "y" ]]; then
|
|
97
|
+
rm "${apps_script_src}/.clasp.json"
|
|
98
|
+
continue
|
|
99
|
+
fi
|
|
100
|
+
fi
|
|
101
|
+
fi
|
|
102
|
+
done
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
#######################################
|
|
106
|
+
# Copy GCP project configuration file to AppsScript codes as a constant named
|
|
107
|
+
# `GCP_CONFIG`.
|
|
108
|
+
# Globals:
|
|
109
|
+
# None
|
|
110
|
+
# Arguments:
|
|
111
|
+
# The folder for Apps Script code, default value ${DEFAULT_APPS_SCRIPT_FOLDER}
|
|
112
|
+
#######################################
|
|
113
|
+
generate_config_js_for_apps_script() {
|
|
114
|
+
local apps_script_src="${1-"${DEFAULT_APPS_SCRIPT_FOLDER}"}"
|
|
115
|
+
local generated_file="${apps_script_src}/.generated_config.js"
|
|
116
|
+
if [[ -f "${CONFIG_FILE}" ]]; then
|
|
117
|
+
echo '// Copyright 2022 Google Inc.
|
|
118
|
+
//
|
|
119
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
120
|
+
// you may not use this file except in compliance with the License.
|
|
121
|
+
// You may obtain a copy of the License at
|
|
122
|
+
//
|
|
123
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
124
|
+
//
|
|
125
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
126
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
127
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
128
|
+
// See the License for the specific language governing permissions and
|
|
129
|
+
// limitations under the License.
|
|
130
|
+
|
|
131
|
+
/** @fileoverview Auto-generated configuration file for Apps Script. */
|
|
132
|
+
'>"${generated_file}"
|
|
133
|
+
echo -n "const GCP_CONFIG = " >>"${generated_file}"
|
|
134
|
+
cat "${CONFIG_FILE}" >>"${generated_file}"
|
|
135
|
+
else
|
|
136
|
+
printf '%s\n' "Couldn't find ${CONFIG_FILE}."
|
|
137
|
+
fi
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
#######################################
|
|
141
|
+
# Clasp pushes AppsScript codes.
|
|
142
|
+
# Globals:
|
|
143
|
+
# None
|
|
144
|
+
# Arguments:
|
|
145
|
+
# The folder for Apps Script code, default value ${DEFAULT_APPS_SCRIPT_FOLDER}
|
|
146
|
+
#######################################
|
|
147
|
+
clasp_push_codes() {
|
|
148
|
+
((STEP += 1))
|
|
149
|
+
printf '%s\n' "Step ${STEP}: Starting to push codes to the Google Sheets..."
|
|
150
|
+
local apps_script_src="${1-"${DEFAULT_APPS_SCRIPT_FOLDER}"}"
|
|
151
|
+
clasp status -P "${apps_script_src}" >>/dev/null
|
|
152
|
+
local project_status=$?
|
|
153
|
+
if [[ ${project_status} -gt 0 ]]; then
|
|
154
|
+
return ${project_status}
|
|
155
|
+
else
|
|
156
|
+
generate_config_js_for_apps_script "${apps_script_src}"
|
|
157
|
+
clasp push --force -P "${apps_script_src}"
|
|
158
|
+
fi
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
#######################################
|
|
162
|
+
# Ask user to update the GCP number of this AppsScript.
|
|
163
|
+
# Globals:
|
|
164
|
+
# GCP_PROJECT
|
|
165
|
+
# Arguments:
|
|
166
|
+
# The folder for Apps Script code, default value ${DEFAULT_APPS_SCRIPT_FOLDER}
|
|
167
|
+
#######################################
|
|
168
|
+
clasp_update_project_number() {
|
|
169
|
+
((STEP += 1))
|
|
170
|
+
local projectNumber=$(get_project_number)
|
|
171
|
+
local apps_script_src="${1-"${DEFAULT_APPS_SCRIPT_FOLDER}"}"
|
|
172
|
+
printf '%s\n' "Step ${STEP}: Update Google Cloud Platform (GCP) Project for \
|
|
173
|
+
Apps Script."
|
|
174
|
+
printf '%s' " "
|
|
175
|
+
clasp open -P "${apps_script_src}"
|
|
176
|
+
printf '%s\n' " On the open tab of Apps Script, use 'Project \
|
|
177
|
+
Settings' to set the Google Cloud Platform (GCP) Project as: ${projectNumber}"
|
|
178
|
+
printf '%s' "Press any key to continue after you update the GCP number..."
|
|
179
|
+
local any
|
|
180
|
+
read -n1 -s any
|
|
181
|
+
printf '\n'
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
#######################################
|
|
185
|
+
# Ask user to grant the access to CF's default service account.
|
|
186
|
+
# Note: the target GCP needs to have OAuth consent screen.
|
|
187
|
+
# Globals:
|
|
188
|
+
# SHEET_URL
|
|
189
|
+
# Arguments:
|
|
190
|
+
# The folder for Apps Script code, default value ${DEFAULT_APPS_SCRIPT_FOLDER}
|
|
191
|
+
#######################################
|
|
192
|
+
grant_access_to_service_account() {
|
|
193
|
+
((STEP += 1))
|
|
194
|
+
local apps_script_src="${1-"${DEFAULT_APPS_SCRIPT_FOLDER}"}"
|
|
195
|
+
local defaultServiceAccount=$(get_cloud_functions_service_account \
|
|
196
|
+
"${PROJECT_NAMESPACE}_main")
|
|
197
|
+
local parentId=$(get_value_from_json_file "${apps_script_src}"/.clasp.json \
|
|
198
|
+
parentId|cut -d\" -f2)
|
|
199
|
+
printf '%s\n' "Step ${STEP}: Share the Google Sheet with ${SOLUTION_NAME}."
|
|
200
|
+
|
|
201
|
+
printf '%s\n' " Open Google Sheet: \
|
|
202
|
+
https://drive.google.com/open?id=${parentId}"
|
|
203
|
+
printf '%s\n' " Click 'Share' and grant the Viewer access to: \
|
|
204
|
+
${defaultServiceAccount}"
|
|
205
|
+
printf '%s' "Press any key to continue after you grant the access..."
|
|
206
|
+
local any
|
|
207
|
+
read -n1 -s any
|
|
208
|
+
printf '\n'
|
|
209
|
+
}
|
package/bin/bigquery.sh
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2022 Google Inc.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
#######################################
|
|
18
|
+
# Checks whether the BigQuery object (table or view) exists.
|
|
19
|
+
# Globals:
|
|
20
|
+
# GCP_PROJECT
|
|
21
|
+
# DATASET
|
|
22
|
+
# BIGQUERY_LOG_TABLE
|
|
23
|
+
# Arguments:
|
|
24
|
+
# None
|
|
25
|
+
#######################################
|
|
26
|
+
check_existence_in_bigquery() {
|
|
27
|
+
bq show "${1}" >/dev/null 2>&1
|
|
28
|
+
printf '%d' $?
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
#######################################
|
|
32
|
+
# Creates or updates the BigQuery view.
|
|
33
|
+
# Globals:
|
|
34
|
+
# GCP_PROJECT
|
|
35
|
+
# DATASET
|
|
36
|
+
# Arguments:
|
|
37
|
+
# The name of view.
|
|
38
|
+
# The query of view.
|
|
39
|
+
#######################################
|
|
40
|
+
create_or_update_view() {
|
|
41
|
+
local viewName viewQuery
|
|
42
|
+
viewName="${1}"
|
|
43
|
+
viewQuery=${2}
|
|
44
|
+
local action="mk"
|
|
45
|
+
if [[ $(check_existence_in_bigquery "${DATASET}.${viewName}") -eq 0 ]]; then
|
|
46
|
+
action="update"
|
|
47
|
+
fi
|
|
48
|
+
bq "${action}" \
|
|
49
|
+
--use_legacy_sql=false \
|
|
50
|
+
--view "${viewQuery}" \
|
|
51
|
+
--project_id ${GCP_PROJECT} \
|
|
52
|
+
"${DATASET}.${viewName}"
|
|
53
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2022 Google Inc.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
# Google Ads API version
|
|
18
|
+
GOOGLE_ADS_API_VERSION=10
|
|
19
|
+
|
|
20
|
+
#######################################
|
|
21
|
+
# Verify whether the current OAuth token, CID and developer token can work.
|
|
22
|
+
# Globals:
|
|
23
|
+
# None
|
|
24
|
+
# Arguments:
|
|
25
|
+
# MCC CID
|
|
26
|
+
# Developer token
|
|
27
|
+
#######################################
|
|
28
|
+
validate_googleads_account() {
|
|
29
|
+
local cid developerToken accessToken request response
|
|
30
|
+
cid=${1}
|
|
31
|
+
developerToken=${2}
|
|
32
|
+
accessToken=$(get_oauth_access_token)
|
|
33
|
+
request=(
|
|
34
|
+
-H "Accept: application/json"
|
|
35
|
+
-H "Content-Type: application/json"
|
|
36
|
+
-H "developer-token: ${developerToken}"
|
|
37
|
+
-H "Authorization: Bearer ${accessToken}"
|
|
38
|
+
-X POST "https://googleads.googleapis.com/v${GOOGLE_ADS_API_VERSION}/customers/${cid}/googleAds:search"
|
|
39
|
+
-d '{"query": "SELECT customer.id FROM customer"}'
|
|
40
|
+
)
|
|
41
|
+
response=$(curl "${request[@]}" 2>/dev/null)
|
|
42
|
+
local errorCode errorMessage
|
|
43
|
+
errorCode=$(get_value_from_json_string "${response}" "error.code")
|
|
44
|
+
if [[ -n "${errorCode}" ]]; then
|
|
45
|
+
errorMessage=$(get_value_from_json_string "${response}" "error.message")
|
|
46
|
+
printf '%s\n' "Validate failed: ${errorMessage}" >&2
|
|
47
|
+
return 1
|
|
48
|
+
fi
|
|
49
|
+
return 0
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
#######################################
|
|
53
|
+
# Let user input MCC CID and developer token for cronjob(s).
|
|
54
|
+
# Globals:
|
|
55
|
+
# MCC_CIDS
|
|
56
|
+
# DEVELOPER_TOKEN
|
|
57
|
+
# Arguments:
|
|
58
|
+
# None
|
|
59
|
+
#######################################
|
|
60
|
+
set_google_ads_account() {
|
|
61
|
+
printf '%s\n' "Setting up Google Ads account information..."
|
|
62
|
+
local developToken mccCids
|
|
63
|
+
while :; do
|
|
64
|
+
# Developer token
|
|
65
|
+
while [[ -z ${developToken} ]]; do
|
|
66
|
+
printf '%s' " Enter the developer token[${DEVELOPER_TOKEN}]: "
|
|
67
|
+
read -r input
|
|
68
|
+
developToken="${input:-${DEVELOPER_TOKEN}}"
|
|
69
|
+
done
|
|
70
|
+
DEVELOPER_TOKEN="${developToken}"
|
|
71
|
+
mccCids=""
|
|
72
|
+
# MCC CIDs
|
|
73
|
+
while :; do
|
|
74
|
+
printf '%s' " Enter the MCC CID: "
|
|
75
|
+
read -r input
|
|
76
|
+
if [[ -z ${input} ]]; then
|
|
77
|
+
continue
|
|
78
|
+
fi
|
|
79
|
+
input="$(printf '%s' "${input}" | sed -r 's/-//g')"
|
|
80
|
+
printf '%s' " validating ${input}...... "
|
|
81
|
+
validate_googleads_account ${input} ${DEVELOPER_TOKEN}
|
|
82
|
+
if [[ $? -eq 1 ]]; then
|
|
83
|
+
printf '%s\n' "failed.
|
|
84
|
+
Press 'd' to re-enter developer token ["${developToken}"] or
|
|
85
|
+
'C' to continue with this MCC CID or
|
|
86
|
+
any other key to enter another MCC CID..."
|
|
87
|
+
local any
|
|
88
|
+
read -n1 -s any
|
|
89
|
+
if [[ "${any}" == "d" ]]; then
|
|
90
|
+
developToken=""
|
|
91
|
+
continue 2
|
|
92
|
+
elif [[ "${any}" == "C" ]]; then
|
|
93
|
+
printf '%s\n' "WARNING! Continue with FAILED MCC ${input}."
|
|
94
|
+
else
|
|
95
|
+
continue
|
|
96
|
+
fi
|
|
97
|
+
else
|
|
98
|
+
printf '%s\n' "succeeded."
|
|
99
|
+
fi
|
|
100
|
+
mccCids+=",${input}"
|
|
101
|
+
printf '%s' " Do you want to add another MCC CID? [Y/n]: "
|
|
102
|
+
read -r input
|
|
103
|
+
if [[ ${input} == 'n' || ${input} == 'N' ]]; then
|
|
104
|
+
break
|
|
105
|
+
fi
|
|
106
|
+
done
|
|
107
|
+
# Left Shift one position to remove the first comma.
|
|
108
|
+
# After shifting, MCC_CIDS would like "11111,22222".
|
|
109
|
+
MCC_CIDS="${mccCids:1}"
|
|
110
|
+
printf '%s\n' "Using Google Ads MCC CIDs: ${MCC_CIDS}."
|
|
111
|
+
break
|
|
112
|
+
done
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
printf '%s\n' "Google Ads Bash Library is loaded."
|
package/bin/install_functions.sh
CHANGED
|
@@ -573,7 +573,7 @@ API(s):"
|
|
|
573
573
|
printf '%s\n' "OK. OAuth is required by selected API(s)."
|
|
574
574
|
return 0
|
|
575
575
|
fi
|
|
576
|
-
printf '%s\n' "Selected API(s) require authentication.
|
|
576
|
+
printf '%s\n' "Selected API(s) require authentication. Choose the \
|
|
577
577
|
authentication method:"
|
|
578
578
|
local auths=("Service Account (recommended)" "OAuth")
|
|
579
579
|
select auth in "${auths[@]}"; do
|
|
@@ -604,28 +604,40 @@ check_permissions() {
|
|
|
604
604
|
(( STEP += 1 ))
|
|
605
605
|
printf '%s\n' "Step ${STEP}: Checking current user's access..."
|
|
606
606
|
local role error
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
607
|
+
while :; do
|
|
608
|
+
error=0
|
|
609
|
+
for role in "${!GOOGLE_CLOUD_PERMISSIONS[@]}"; do
|
|
610
|
+
printf '%s' " Checking permissions for ${role}... "
|
|
611
|
+
local permissions
|
|
612
|
+
permissions=(${GOOGLE_CLOUD_PERMISSIONS[${role}]})
|
|
613
|
+
local missed
|
|
614
|
+
missed=$(get_number_of_missed_permissions "${permissions[@]}")
|
|
615
|
+
if [[ $missed -gt 0 ]]; then
|
|
616
|
+
message="missed ${missed}, failed"
|
|
617
|
+
error=1
|
|
618
|
+
else
|
|
619
|
+
message='successfully'
|
|
620
|
+
fi
|
|
621
|
+
printf '%s\n' " ${message}."
|
|
622
|
+
done
|
|
623
|
+
if [[ ${error} -gt 0 ]]; then
|
|
624
|
+
printf '%s\n' "Permissions check failed."
|
|
625
|
+
printf '%s\n' "Would you like to login with another account? [Y/n]: "
|
|
626
|
+
local reLogin
|
|
627
|
+
read -r reLogin
|
|
628
|
+
reLogin=${reLogin:-"Y"}
|
|
629
|
+
if [[ ${reLogin} == "Y" || ${reLogin} == "y" ]]; then
|
|
630
|
+
gcloud auth login
|
|
631
|
+
continue
|
|
632
|
+
else
|
|
633
|
+
return 1
|
|
634
|
+
fi
|
|
616
635
|
else
|
|
617
|
-
|
|
636
|
+
echo "OK. Permissions check passed for Google Cloud project \
|
|
637
|
+
[${GCP_PROJECT}]."
|
|
638
|
+
return 0
|
|
618
639
|
fi
|
|
619
|
-
printf '%s\n' " ${message}."
|
|
620
640
|
done
|
|
621
|
-
if [[ ${error} -gt 0 ]]; then
|
|
622
|
-
printf '%s\n' "Permissions check failed."
|
|
623
|
-
return 1
|
|
624
|
-
else
|
|
625
|
-
echo "OK. Permissions check passed for Google Cloud project \
|
|
626
|
-
[${GCP_PROJECT}]."
|
|
627
|
-
return 0
|
|
628
|
-
fi
|
|
629
641
|
}
|
|
630
642
|
|
|
631
643
|
#######################################
|
|
@@ -1031,13 +1043,24 @@ refresh the list:"
|
|
|
1031
1043
|
# Globals:
|
|
1032
1044
|
# GCP_PROJECT
|
|
1033
1045
|
# Arguments:
|
|
1034
|
-
# Bucket name var,
|
|
1046
|
+
# Bucket name var with optional usage, e.g. 'GCS_BUCKET_REPORT:reports'.
|
|
1047
|
+
# The default value is 'GCS_BUCKET'.
|
|
1035
1048
|
# Location var name, default value 'REGION'
|
|
1036
1049
|
# If the second location var is unset, use this var as default value
|
|
1037
1050
|
#######################################
|
|
1038
1051
|
confirm_located_bucket() {
|
|
1039
|
-
local gcsName defaultValue defaultBucketName locationName location
|
|
1040
|
-
|
|
1052
|
+
local gcsName usage defaultValue defaultBucketName locationName location
|
|
1053
|
+
if [[ -z "${1}" ]]; then
|
|
1054
|
+
gcsName="GCS_BUCKET"
|
|
1055
|
+
elif [[ "${1}" == *":"* ]]; then
|
|
1056
|
+
gcsName=$(echo "${1}" | cut -d\: -f1)
|
|
1057
|
+
usage=$(echo "${1}" | cut -d\: -f2)
|
|
1058
|
+
if [[ -n "${usage}" ]]; then
|
|
1059
|
+
usage=" for ${usage}"
|
|
1060
|
+
fi
|
|
1061
|
+
else
|
|
1062
|
+
gcsName="${1}"
|
|
1063
|
+
fi
|
|
1041
1064
|
locationName="${2:-"REGION"}"
|
|
1042
1065
|
defaultValue="${!gcsName}"
|
|
1043
1066
|
defaultBucketName=$(get_default_bucket_name "${GCP_PROJECT}")
|
|
@@ -1052,7 +1075,8 @@ confirm_located_bucket() {
|
|
|
1052
1075
|
|
|
1053
1076
|
(( STEP += 1 ))
|
|
1054
1077
|
if [[ -z "${location}" ]]; then
|
|
1055
|
-
printf '%s\n' "Step ${STEP}: Checking or creating a Cloud Storage
|
|
1078
|
+
printf '%s\n' "Step ${STEP}: Checking or creating a Cloud Storage \
|
|
1079
|
+
Bucket${usage}..."
|
|
1056
1080
|
if [[ "${locationName}" == "REGION" ]]; then
|
|
1057
1081
|
select_functions_location ${locationName}
|
|
1058
1082
|
else
|
|
@@ -1060,8 +1084,8 @@ confirm_located_bucket() {
|
|
|
1060
1084
|
fi
|
|
1061
1085
|
location="${!locationName}"
|
|
1062
1086
|
else
|
|
1063
|
-
printf '%s\n' "Step ${STEP}: Checking or creating a Cloud Storage
|
|
1064
|
-
location [${location}] ..."
|
|
1087
|
+
printf '%s\n' "Step ${STEP}: Checking or creating a Cloud Storage \
|
|
1088
|
+
Bucket${usage} in location [${location}] ..."
|
|
1065
1089
|
fi
|
|
1066
1090
|
declare -g "${locationName}=${location}"
|
|
1067
1091
|
while :; do
|
|
@@ -1129,6 +1153,7 @@ Project. Continuing to enter another bucket..."
|
|
|
1129
1153
|
fi
|
|
1130
1154
|
done
|
|
1131
1155
|
declare -g "${gcsName}=${bucket}"
|
|
1156
|
+
confirm_bucket_lifecycle "${bucket}"
|
|
1132
1157
|
}
|
|
1133
1158
|
|
|
1134
1159
|
#######################################
|
|
@@ -1149,6 +1174,58 @@ confirm_bucket_with_location() {
|
|
|
1149
1174
|
confirm_located_bucket ${gcsName} ${locationName}
|
|
1150
1175
|
}
|
|
1151
1176
|
|
|
1177
|
+
#######################################
|
|
1178
|
+
# Manage the lifecycle of a GCS bucket: setting or removing the GCS lifecycle
|
|
1179
|
+
# rule of 'age'.
|
|
1180
|
+
# See: https://cloud.google.com/storage/docs/lifecycle#age
|
|
1181
|
+
# Arguments:
|
|
1182
|
+
# Bucket name
|
|
1183
|
+
#######################################
|
|
1184
|
+
confirm_bucket_lifecycle() {
|
|
1185
|
+
local bucket bucketMetadata lifecycle
|
|
1186
|
+
bucket="${1}"
|
|
1187
|
+
bucketMetadata="$(get_bucket_metadata "${bucket}")"
|
|
1188
|
+
lifecycle="$(get_value_from_json_string "${bucketMetadata}" "lifecycle")"
|
|
1189
|
+
if [[ -n "${lifecycle}" ]]; then
|
|
1190
|
+
printf '%s\n' "There are lifecycle rules in this bucket: ${lifecycle}."
|
|
1191
|
+
printf '%s' "Would you like to overwrite it? [N/y]:"
|
|
1192
|
+
local confirmContinue
|
|
1193
|
+
read -r confirmContinue
|
|
1194
|
+
confirmContinue=${confirmContinue:-"N"}
|
|
1195
|
+
if [[ ${confirmContinue} == "N" || ${confirmContinue} == "n" ]]; then
|
|
1196
|
+
return 0
|
|
1197
|
+
fi
|
|
1198
|
+
else
|
|
1199
|
+
printf '%s\n' "There is no lifecycle rules in this bucket."
|
|
1200
|
+
printf '%s' "Would you like to create it? [Y/n]:"
|
|
1201
|
+
local confirmContinue
|
|
1202
|
+
read -r confirmContinue
|
|
1203
|
+
confirmContinue=${confirmContinue:-"Y"}
|
|
1204
|
+
if [[ ${confirmContinue} == "N" || ${confirmContinue} == "n" ]]; then
|
|
1205
|
+
return 0
|
|
1206
|
+
fi
|
|
1207
|
+
fi
|
|
1208
|
+
while :; do
|
|
1209
|
+
printf '%s' "Enter the number of days that a file will be kept before it \
|
|
1210
|
+
is automatically removed in the bucket[${bucket}]. (enter 0 to remove all \
|
|
1211
|
+
existing lifecycle rules): "
|
|
1212
|
+
local days
|
|
1213
|
+
read -r days
|
|
1214
|
+
days=${days}
|
|
1215
|
+
if [[ "${days}" =~ ^[0-9]+$ ]]; then
|
|
1216
|
+
local lifecycle
|
|
1217
|
+
if [[ "${days}" == "0" ]]; then
|
|
1218
|
+
lifecycle="{}"
|
|
1219
|
+
else
|
|
1220
|
+
lifecycle='{"rule":[{"action":{"type":"Delete"},"condition":{"age":'\
|
|
1221
|
+
"${days}}}]}"
|
|
1222
|
+
fi
|
|
1223
|
+
gsutil lifecycle set /dev/stdin gs://${bucket} <<< ${lifecycle}
|
|
1224
|
+
return $?
|
|
1225
|
+
fi
|
|
1226
|
+
done
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1152
1229
|
#######################################
|
|
1153
1230
|
# Confirm the monitored folder.
|
|
1154
1231
|
# Globals:
|
|
@@ -1409,20 +1486,18 @@ EOF
|
|
|
1409
1486
|
"client_id=${client_id}" "scope=${scope}")
|
|
1410
1487
|
local auth_url
|
|
1411
1488
|
auth_url="${OAUTH_BASE_URL}auth?${parameters}"
|
|
1412
|
-
printf '%s\n' "3. Open the link in browser and finish authentication
|
|
1413
|
-
${auth_url}"
|
|
1489
|
+
printf '%s\n' "3. Open the link in your browser and finish authentication. \
|
|
1490
|
+
Do not close the redirected page: ${auth_url}"
|
|
1414
1491
|
cat <<EOF
|
|
1415
1492
|
Note:
|
|
1493
|
+
The succeeded OAuth flow will land the browser on an error page - \
|
|
1494
|
+
"This site can't be reached". This is expected behavior. Copy the whole URL and continue.
|
|
1416
1495
|
If the OAuth client is not for a native application, there will be an \
|
|
1417
1496
|
"Error 400: redirect_uri_mismatch" shown up on the page. In this case, press \
|
|
1418
1497
|
"Enter" to start again with a native application OAuth client ID.
|
|
1419
|
-
If there is no local web server serving at ${REDIRECT_URI}, the \
|
|
1420
|
-
succeeded OAuth flow will land the browser on an error page ("This site can't \
|
|
1421
|
-
be reached"). This is an expected behavior. Copy the whole URL and continue.
|
|
1422
1498
|
|
|
1423
1499
|
EOF
|
|
1424
|
-
printf '%s' "4. Copy the
|
|
1425
|
-
and paste here: "
|
|
1500
|
+
printf '%s' "4. Copy the complete URL from your browser and paste here: "
|
|
1426
1501
|
read -r auth_code
|
|
1427
1502
|
if [[ -z ${auth_code} ]]; then
|
|
1428
1503
|
printf '%s\n\n' "No authorization code. Starting from beginning again..."
|
|
@@ -1507,6 +1582,7 @@ set_authentication_env_for_cloud_functions() {
|
|
|
1507
1582
|
create_or_update_cloud_scheduler_for_pubsub(){
|
|
1508
1583
|
check_authentication
|
|
1509
1584
|
quit_if_failed $?
|
|
1585
|
+
local location_flag="--location=${REGION}"
|
|
1510
1586
|
local scheduler_flag=()
|
|
1511
1587
|
scheduler_flag+=(--schedule="$2")
|
|
1512
1588
|
scheduler_flag+=(--time-zone="$3")
|
|
@@ -1514,13 +1590,13 @@ create_or_update_cloud_scheduler_for_pubsub(){
|
|
|
1514
1590
|
scheduler_flag+=(--message-body="$5")
|
|
1515
1591
|
local exist_job
|
|
1516
1592
|
exist_job=($(gcloud scheduler jobs list --filter="name~${1}" \
|
|
1517
|
-
--format="value(state)"))
|
|
1593
|
+
--format="value(state)" "${location_flag}"))
|
|
1518
1594
|
local action needPause
|
|
1519
1595
|
if [[ ${#exist_job[@]} -gt 0 ]]; then
|
|
1520
1596
|
action="update"
|
|
1521
1597
|
scheduler_flag+=(--update-attributes=$6)
|
|
1522
1598
|
if [[ "${exist_job[0]}" == "PAUSED" ]]; then
|
|
1523
|
-
gcloud scheduler jobs resume "${1}"
|
|
1599
|
+
gcloud scheduler jobs resume "${1}" "${location_flag}"
|
|
1524
1600
|
if [[ $? -gt 0 ]]; then
|
|
1525
1601
|
printf '%s\n' "Failed to resume paused Cloud Scheduler job [${1}]."
|
|
1526
1602
|
return 1
|
|
@@ -1531,9 +1607,9 @@ create_or_update_cloud_scheduler_for_pubsub(){
|
|
|
1531
1607
|
action="create"
|
|
1532
1608
|
scheduler_flag+=(--attributes=$6)
|
|
1533
1609
|
fi
|
|
1534
|
-
gcloud scheduler jobs ${action} pubsub "$1" "${scheduler_flag[@]}"
|
|
1610
|
+
gcloud scheduler jobs ${action} pubsub "$1" "${scheduler_flag[@]}" "${location_flag}"
|
|
1535
1611
|
if [[ "${needPause}" == "true" ]]; then
|
|
1536
|
-
gcloud scheduler jobs pause "${1}"
|
|
1612
|
+
gcloud scheduler jobs pause "${1}" "${location_flag}"
|
|
1537
1613
|
fi
|
|
1538
1614
|
}
|
|
1539
1615
|
|
|
@@ -1613,7 +1689,9 @@ customized_install() {
|
|
|
1613
1689
|
local tasks=("$@")
|
|
1614
1690
|
local task
|
|
1615
1691
|
for task in "${tasks[@]}"; do
|
|
1616
|
-
|
|
1692
|
+
local cmd
|
|
1693
|
+
eval "cmd=(${task})"
|
|
1694
|
+
"${cmd[@]}"
|
|
1617
1695
|
quit_if_failed $?
|
|
1618
1696
|
done
|
|
1619
1697
|
}
|
|
@@ -1921,3 +1999,11 @@ join_string_array() {
|
|
|
1921
1999
|
shift
|
|
1922
2000
|
printf %s "$first" "${@/#/$separator}"
|
|
1923
2001
|
}
|
|
2002
|
+
|
|
2003
|
+
# Import other bash files.
|
|
2004
|
+
_SELF="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
2005
|
+
source "${_SELF}/google_ads.sh"
|
|
2006
|
+
source "${_SELF}/bigquery.sh"
|
|
2007
|
+
source "${_SELF}/apps_scripts.sh"
|
|
2008
|
+
|
|
2009
|
+
printf '%s\n' "Common Bash Library is loaded."
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@google-cloud/nodejs-common",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.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",
|
|
@@ -16,25 +16,25 @@
|
|
|
16
16
|
},
|
|
17
17
|
"homepage": "https://github.com/GoogleCloudPlatform/cloud-for-marketing/blob/master/marketing-analytics/activation/common-libs/nodejs-common/README.md",
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@google-cloud/aiplatform": "^1.
|
|
20
|
-
"@google-cloud/automl": "^2.5.
|
|
21
|
-
"@google-cloud/bigquery": "^
|
|
19
|
+
"@google-cloud/aiplatform": "^1.19.0",
|
|
20
|
+
"@google-cloud/automl": "^2.5.2",
|
|
21
|
+
"@google-cloud/bigquery": "^6.0.0",
|
|
22
22
|
"@google-cloud/datastore": "^6.6.2",
|
|
23
|
-
"@google-cloud/firestore": "^
|
|
24
|
-
"@google-cloud/logging-winston": "^
|
|
25
|
-
"@google-cloud/pubsub": "^
|
|
26
|
-
"@google-cloud/storage": "^
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"google-ads-
|
|
30
|
-
"google-
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"winston": "^3.
|
|
23
|
+
"@google-cloud/firestore": "^5.0.2",
|
|
24
|
+
"@google-cloud/logging-winston": "^5.1.0",
|
|
25
|
+
"@google-cloud/pubsub": "^3.0.1",
|
|
26
|
+
"@google-cloud/storage": "^6.0.1",
|
|
27
|
+
"@google-cloud/scheduler": "^3.0.0",
|
|
28
|
+
"gaxios": "^5.0.0",
|
|
29
|
+
"google-ads-api": "^10.0.1",
|
|
30
|
+
"google-ads-node":"^8.0.1",
|
|
31
|
+
"google-auth-library": "^8.0.2",
|
|
32
|
+
"googleapis": "^100.0.0",
|
|
33
|
+
"winston": "^3.7.2",
|
|
34
34
|
"lodash": "^4.17.21"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"jasmine": "^
|
|
37
|
+
"jasmine": "^4.1.0"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
40
|
"test": "node node_modules/jasmine/bin/jasmine"
|
package/src/components/automl.js
CHANGED
|
@@ -17,15 +17,9 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
'use strict';
|
|
20
|
-
const {
|
|
21
|
-
const {JWT, Compute} = require('google-auth-library');
|
|
20
|
+
const { ClientOptions } = require('google-gax');
|
|
22
21
|
const automl = require('@google-cloud/automl');
|
|
23
22
|
const google = automl.protos.google;
|
|
24
|
-
const AuthClient = require('../apis/auth_client.js');
|
|
25
|
-
|
|
26
|
-
const API_SCOPES = Object.freeze([
|
|
27
|
-
'https://www.googleapis.com/auth/cloud-platform',
|
|
28
|
-
]);
|
|
29
23
|
|
|
30
24
|
/**
|
|
31
25
|
* For version `v1beta1`, BigQuery can be the source and destination.
|
|
@@ -40,46 +34,49 @@ const API_SCOPES = Object.freeze([
|
|
|
40
34
|
* @type {string}
|
|
41
35
|
*/
|
|
42
36
|
const API_VERSION = 'v1beta1';
|
|
43
|
-
const PredictionServiceClient = automl[API_VERSION]
|
|
37
|
+
const { PredictionServiceClient } = automl[API_VERSION];
|
|
44
38
|
|
|
45
39
|
/**
|
|
46
|
-
* AutoML Tables API
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* https://
|
|
51
|
-
* 2. 'get Operations' based on REST API, see:
|
|
52
|
-
* https://cloud.google.com/automl/docs/reference/rest/v1/projects.locations.operations/get
|
|
53
|
-
* https://cloud.google.com/automl-tables/docs/long-operations#get-operation
|
|
40
|
+
* AutoML Tables API class for:
|
|
41
|
+
* 1. start a batch predict job:
|
|
42
|
+
* @see https://cloud.google.com/nodejs/docs/reference/automl/latest/automl/v1beta1.predictionserviceclient#_google_cloud_automl_v1beta1_PredictionServiceClient_batchPredict_member_3_
|
|
43
|
+
* 2. get the status of an operation:
|
|
44
|
+
* @see https://cloud.google.com/nodejs/docs/reference/automl/latest/automl/v1beta1.predictionserviceclient#_google_cloud_automl_v1beta1_PredictionServiceClient_checkBatchPredictProgress_member_1_
|
|
54
45
|
*/
|
|
55
46
|
class AutoMl {
|
|
56
|
-
|
|
57
47
|
/**
|
|
58
48
|
* Initialize an instance.
|
|
59
|
-
* @param {
|
|
49
|
+
* @param {ClientOptions=} options
|
|
60
50
|
*/
|
|
61
51
|
constructor(options = {}) {
|
|
62
|
-
this.
|
|
52
|
+
this.client = new PredictionServiceClient(options);
|
|
63
53
|
}
|
|
64
54
|
|
|
65
55
|
/**
|
|
66
56
|
* Batch predicts based on Google Cloud Client Library.
|
|
67
|
-
* See https://googleapis.dev/nodejs/automl/latest/index.html
|
|
68
57
|
* @param {string} projectId
|
|
69
58
|
* @param {string} computeRegion
|
|
70
59
|
* @param {string} modelId
|
|
71
|
-
* @param {google.cloud.automl.
|
|
72
|
-
* @param {google.cloud.automl.
|
|
60
|
+
* @param {google.cloud.automl.v1beta1.IBatchPredictInputConfig} inputConfig
|
|
61
|
+
* @param {google.cloud.automl.v1beta1.IBatchPredictOutputConfig} outputConfig
|
|
73
62
|
* @return {Promise<string>} Predict operation name.
|
|
74
63
|
*/
|
|
75
|
-
async batchPredict(
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
64
|
+
async batchPredict(
|
|
65
|
+
projectId,
|
|
66
|
+
computeRegion,
|
|
67
|
+
modelId,
|
|
68
|
+
inputConfig,
|
|
69
|
+
outputConfig
|
|
70
|
+
) {
|
|
71
|
+
const modelFullId = this.client.modelPath(
|
|
72
|
+
projectId,
|
|
73
|
+
computeRegion,
|
|
74
|
+
modelId
|
|
75
|
+
);
|
|
76
|
+
const responses = await this.client.batchPredict({
|
|
80
77
|
name: modelFullId,
|
|
81
|
-
inputConfig
|
|
82
|
-
outputConfig
|
|
78
|
+
inputConfig,
|
|
79
|
+
outputConfig,
|
|
83
80
|
});
|
|
84
81
|
const operation = responses[1];
|
|
85
82
|
console.log(`Operation name: ${operation.name}`);
|
|
@@ -87,37 +84,20 @@ class AutoMl {
|
|
|
87
84
|
}
|
|
88
85
|
|
|
89
86
|
/**
|
|
90
|
-
* Gets
|
|
91
|
-
* https://cloud.google.com/automl/docs/reference/rest/v1/projects.locations.operations/get
|
|
92
|
-
* https://cloud.google.com/automl-tables/docs/long-operations#get-operation
|
|
87
|
+
* Gets status of an operation.
|
|
93
88
|
* @param {string} operationName
|
|
94
|
-
* @return {Promise<
|
|
89
|
+
* @return {Promise<{{
|
|
90
|
+
* done: boolean,
|
|
91
|
+
* error: Error|undefined,
|
|
92
|
+
* metadata: OperationMetadata,
|
|
93
|
+
* name: string,
|
|
94
|
+
* }}>}
|
|
95
95
|
*/
|
|
96
96
|
async getOperation(operationName) {
|
|
97
|
-
const
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
method: 'GET',
|
|
101
|
-
headers,
|
|
102
|
-
url,
|
|
103
|
-
};
|
|
104
|
-
const response = await request(requestOptions);
|
|
105
|
-
return response.data;
|
|
97
|
+
const response = await this.client.checkBatchPredictProgress(operationName);
|
|
98
|
+
const { done, error, metadata, name } = response;
|
|
99
|
+
return { done, error, metadata, name };
|
|
106
100
|
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Gets authentication client of AutoML API.
|
|
110
|
-
* This API belongs to GCP, however it is not fully supported by Google Cloud
|
|
111
|
-
* Client Library. So we need to manage some functions on its REST API,
|
|
112
|
-
* in the way like we invoke other external APIs.
|
|
113
|
-
* @returns {(!JWT|!Compute)}
|
|
114
|
-
* @private
|
|
115
|
-
*/
|
|
116
|
-
getAuthClient_() {
|
|
117
|
-
/** @const {!AuthClient} */ const authClient = new AuthClient(API_SCOPES);
|
|
118
|
-
return authClient.getApplicationDefaultCredentials();
|
|
119
|
-
}
|
|
120
|
-
|
|
121
101
|
}
|
|
122
102
|
|
|
123
103
|
exports.AutoMl = AutoMl;
|
|
@@ -30,11 +30,36 @@ const API_VERSION = 'v1';
|
|
|
30
30
|
* to get/pause/resume a job.
|
|
31
31
|
*/
|
|
32
32
|
class CloudScheduler {
|
|
33
|
-
constructor(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
constructor(env = process.env, options = {}) {
|
|
34
|
+
if (!options.authClient) {
|
|
35
|
+
/** @const {!AuthClient} */
|
|
36
|
+
const authClient = new AuthClient(API_SCOPES, env);
|
|
37
|
+
/**
|
|
38
|
+
* By default, `AuthClient` (getDefaultAuth()) will return an auth client
|
|
39
|
+
* based on the settings in ENV while the OAuth is the most preferred.
|
|
40
|
+
* This works for most of the external API clients (in the '../apis'
|
|
41
|
+
* folder), however this won't work in the Cloud Functions, as those OAuth
|
|
42
|
+
* token usually won't have enough permission to invoke Google Cloud API.
|
|
43
|
+
* Using the method `getApplicationDefaultCredentials` to force
|
|
44
|
+
* `AuthClient` return an ADC auth client, which will work in the Cloud.
|
|
45
|
+
*
|
|
46
|
+
* Cloud Scheduler API Client Library is used here as Cloud Client Library
|
|
47
|
+
* is still at beta stage. (For the difference, see
|
|
48
|
+
* https://cloud.google.com/apis/docs/client-libraries-explained)
|
|
49
|
+
* Eventually, when we migrate this to the cloud client library, which
|
|
50
|
+
* automatically takes ADC as the authentication method, the 'AuthClient'
|
|
51
|
+
* is not required here and can be removed.
|
|
52
|
+
*/
|
|
53
|
+
this.auth = authClient.getApplicationDefaultCredentials();
|
|
54
|
+
} else {
|
|
55
|
+
/**
|
|
56
|
+
* `authClient` can be consumed by cloud client library as the auth
|
|
57
|
+
* client. By passing this in, we can offer more flexible auth clients in
|
|
58
|
+
* test cases for API client library and cloud client library in future.
|
|
59
|
+
*/
|
|
60
|
+
this.auth = options.authClient;
|
|
61
|
+
}
|
|
62
|
+
this.projectId = env['GCP_PROJECT'];
|
|
38
63
|
this.instance = cloudscheduler({
|
|
39
64
|
version: API_VERSION,
|
|
40
65
|
auth: this.auth,
|
|
@@ -113,8 +138,8 @@ class CloudScheduler {
|
|
|
113
138
|
*/
|
|
114
139
|
async getJobs_(name, targetLocations = undefined) {
|
|
115
140
|
const regex = new RegExp(`/jobs/${name}$`);
|
|
116
|
-
const
|
|
117
|
-
|
|
141
|
+
const allJobs = await this.listJobs_(targetLocations);
|
|
142
|
+
const jobs = allJobs.filter((job) => regex.test(job));
|
|
118
143
|
if (jobs.length === 0) console.error(`Can not find job: ${name}`);
|
|
119
144
|
return jobs;
|
|
120
145
|
}
|
|
@@ -134,23 +159,10 @@ class CloudScheduler {
|
|
|
134
159
|
const projectId = await this.getProjectId_();
|
|
135
160
|
const requestPrefix = `projects/${projectId}/locations`;
|
|
136
161
|
const jobs = locations.map(async (location) => {
|
|
137
|
-
const request = {parent: `${requestPrefix}/${location}`};
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return response.data.jobs.map((job) => job.name);
|
|
142
|
-
} catch (error) {
|
|
143
|
-
// Currently, listLocations always returns an array with one location.
|
|
144
|
-
// Not sure whether this will be changed or not in future. If one day
|
|
145
|
-
// the Cloud Scheduler let users to select a location for a job, then
|
|
146
|
-
// it may return multiple locations when listLocation, however the
|
|
147
|
-
// target job may exist in one of the locations. In this case, when we
|
|
148
|
-
// iterate the job with all possible locations to get the complete job
|
|
149
|
-
// path to operate, it will likely generate an error for those wrong
|
|
150
|
-
// location(s). So set the try-catch here to handle this situation.
|
|
151
|
-
console.error(error.message);
|
|
152
|
-
return [];
|
|
153
|
-
}
|
|
162
|
+
const request = { parent: `${requestPrefix}/${location}` };
|
|
163
|
+
const response = await this.instance.projects.locations.jobs.list(request);
|
|
164
|
+
if (!response.data.jobs) return [];
|
|
165
|
+
return response.data.jobs.map((job) => job.name);
|
|
154
166
|
});
|
|
155
167
|
// Waits for all jobs names and flattens nested job name arrays, however
|
|
156
168
|
// there is no 'flat' available in current Cloud Functions runtime.
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
'use strict';
|
|
20
20
|
|
|
21
|
+
const { ClientOptions } = require('google-gax');
|
|
21
22
|
const {JobServiceClient} = require('@google-cloud/aiplatform');
|
|
22
23
|
|
|
23
24
|
/**
|
|
@@ -29,7 +30,7 @@ class VertexAi {
|
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
32
|
* Initialize an instance.
|
|
32
|
-
* @param {
|
|
33
|
+
* @param {ClientOptions=} options
|
|
33
34
|
*/
|
|
34
35
|
constructor(options = {}) {
|
|
35
36
|
this.options = options;
|
|
@@ -105,9 +106,9 @@ class VertexAi {
|
|
|
105
106
|
* @private
|
|
106
107
|
*/
|
|
107
108
|
getJobServiceClient_(location) {
|
|
108
|
-
const clientOptions = {
|
|
109
|
+
const clientOptions = Object.assign({}, this.options, {
|
|
109
110
|
apiEndpoint: this.getServiceEndpoint_(location),
|
|
110
|
-
};
|
|
111
|
+
});
|
|
111
112
|
return new JobServiceClient(clientOptions);
|
|
112
113
|
}
|
|
113
114
|
}
|