@google-cloud/nodejs-common 2.0.16-beta → 2.1.1
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 +4 -3
- package/bin/google_ads.sh +1 -1
- package/bin/install_functions.sh +1 -0
- package/package.json +1 -1
- package/src/apis/analytics.js +17 -21
- package/src/apis/base/ads_api_common.js +99 -0
- package/src/apis/base/google_api_client.js +55 -0
- package/src/apis/dfa_reporting.js +17 -33
- package/src/apis/display_video.js +13 -44
- package/src/apis/doubleclick_bidmanager.js +34 -42
- package/src/apis/doubleclick_search.js +17 -33
- package/src/apis/google_ads_api.js +94 -45
- package/src/apis/index.js +0 -1
- package/src/apis/search_ads.js +114 -52
- package/src/apis/spreadsheets.js +16 -23
- package/src/apis/youtube.js +18 -26
- package/src/components/firestore/datastore_mode_access.js +4 -3
- package/src/components/utils.js +23 -5
package/src/apis/search_ads.js
CHANGED
|
@@ -18,20 +18,24 @@
|
|
|
18
18
|
|
|
19
19
|
'use strict';
|
|
20
20
|
|
|
21
|
+
const { request: gaxiosRequest } = require('gaxios');
|
|
21
22
|
const { google } = require('googleapis');
|
|
22
|
-
const
|
|
23
|
+
const { GoogleApiClient } = require('./base/google_api_client.js');
|
|
23
24
|
const { getLogger } = require('../components/utils.js');
|
|
25
|
+
const { getCleanCid, RestSearchStreamTransform }
|
|
26
|
+
= require('./base/ads_api_common.js');
|
|
24
27
|
|
|
25
28
|
const API_SCOPES = Object.freeze([
|
|
26
29
|
'https://www.googleapis.com/auth/doubleclicksearch',
|
|
27
30
|
]);
|
|
31
|
+
const API_ENDPOINT = 'https://searchads360.googleapis.com';
|
|
28
32
|
const API_VERSION = 'v0';
|
|
29
33
|
|
|
30
34
|
/**
|
|
31
35
|
* Search Ads 360 Reporting API stub.
|
|
32
36
|
* See: https://developers.google.com/search-ads/reporting/api/reference/release-notes
|
|
33
37
|
*/
|
|
34
|
-
class SearchAds {
|
|
38
|
+
class SearchAds extends GoogleApiClient {
|
|
35
39
|
|
|
36
40
|
/**
|
|
37
41
|
* @constructor
|
|
@@ -39,10 +43,21 @@ class SearchAds {
|
|
|
39
43
|
* variables.
|
|
40
44
|
*/
|
|
41
45
|
constructor(env = process.env) {
|
|
42
|
-
|
|
46
|
+
super(env);
|
|
47
|
+
this.googleApi = 'searchads360';
|
|
43
48
|
this.logger = getLogger('API.SA');
|
|
44
49
|
}
|
|
45
50
|
|
|
51
|
+
/** @override */
|
|
52
|
+
getScope() {
|
|
53
|
+
return API_SCOPES;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** @override */
|
|
57
|
+
getVersion() {
|
|
58
|
+
return API_VERSION;
|
|
59
|
+
}
|
|
60
|
+
|
|
46
61
|
/**
|
|
47
62
|
* Prepares the Search Ads 360 Reporting API instance.
|
|
48
63
|
* OAuth 2.0 application credentials is required for calling this API.
|
|
@@ -55,83 +70,130 @@ class SearchAds {
|
|
|
55
70
|
* @return {!google.searchads360}
|
|
56
71
|
* @private
|
|
57
72
|
*/
|
|
58
|
-
async
|
|
59
|
-
this.logger.debug(`Initialized
|
|
60
|
-
|
|
61
|
-
version:
|
|
62
|
-
auth: await this.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
73
|
+
async getApiClient(loginCustomerId) {
|
|
74
|
+
this.logger.debug(`Initialized SA reporting for ${loginCustomerId}`);
|
|
75
|
+
const options = {
|
|
76
|
+
version: this.getVersion(),
|
|
77
|
+
auth: await this.getAuth(),
|
|
78
|
+
};
|
|
79
|
+
if (loginCustomerId) {
|
|
80
|
+
options.headers = { 'login-customer-id': getCleanCid(loginCustomerId) };
|
|
81
|
+
}
|
|
82
|
+
return google.searchads360(options);
|
|
66
83
|
}
|
|
67
84
|
|
|
68
85
|
/**
|
|
69
|
-
* Gets
|
|
70
|
-
*
|
|
86
|
+
* Gets a report synchronously from a given Customer account.
|
|
87
|
+
* If there is a `nextPageToken` in the response, it means the report is not
|
|
88
|
+
* finished and there are more pages.
|
|
89
|
+
* @see https://developers.google.com/search-ads/reporting/api/reference/rpc/google.ads.searchads360.v0.services#searchads360service
|
|
90
|
+
* @param {string} customerId
|
|
91
|
+
* @param {string} loginCustomerId Login customer account ID (Mcc Account id).
|
|
92
|
+
* @param {string} query
|
|
93
|
+
* @param {object=} options Options for `SearchSearchAds360Request`.
|
|
94
|
+
* @see https://developers.google.com/search-ads/reporting/api/reference/rpc/google.ads.searchads360.v0.services#searchsearchads360request
|
|
95
|
+
* @return {!SearchAds360Field}
|
|
96
|
+
* @see https://developers.google.com/search-ads/reporting/api/reference/rpc/google.ads.searchads360.v0.services#searchsearchads360response
|
|
71
97
|
*/
|
|
72
|
-
async
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
98
|
+
async getPaginatedReport(customerId, loginCustomerId, query, options = {}) {
|
|
99
|
+
const searchads = await this.getApiClient(loginCustomerId);
|
|
100
|
+
const requestBody = Object.assign({
|
|
101
|
+
query,
|
|
102
|
+
pageSize: 10000,
|
|
103
|
+
}, options);
|
|
104
|
+
const response = await searchads.customers.searchAds360.search({
|
|
105
|
+
customerId: getCleanCid(customerId),
|
|
106
|
+
requestBody,
|
|
107
|
+
});
|
|
108
|
+
return response.data;
|
|
77
109
|
}
|
|
78
110
|
|
|
79
111
|
/**
|
|
80
|
-
*
|
|
81
|
-
* The streamed content is not NDJSON format, but
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* is too large to be handled in this way, a possible solution is to parse
|
|
85
|
-
* the string directly to get the content of `results`.
|
|
86
|
-
* @see https://developers.google.com/search-ads/reporting/api/reference/rest/v0/customers.searchAds360/searchStream
|
|
112
|
+
* Gets a report stream from a Search Ads 360 reporting API.
|
|
113
|
+
* The streamed content is not NDJSON format, but an array of JSON objects
|
|
114
|
+
* with each element has a property `results`.
|
|
115
|
+
* `data` support `batchSize` to set how many rows in one result element.
|
|
87
116
|
* @param {string} customerId
|
|
88
117
|
* @param {string} loginCustomerId Login customer account ID (Mcc Account id).
|
|
89
118
|
* @param {string} query
|
|
90
|
-
* @return {!
|
|
119
|
+
* @return {!Promise<stream>}
|
|
120
|
+
* @see https://developers.google.com/search-ads/reporting/api/reference/rest/search
|
|
91
121
|
*/
|
|
92
|
-
async
|
|
93
|
-
const
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
122
|
+
async restStreamReport(customerId, loginCustomerId, query) {
|
|
123
|
+
const auth = await this.getAuth();
|
|
124
|
+
const headers = Object.assign(
|
|
125
|
+
await auth.getRequestHeaders(), {
|
|
126
|
+
'login-customer-id': getCleanCid(loginCustomerId),
|
|
127
|
+
});
|
|
128
|
+
const options = {
|
|
129
|
+
baseURL: `${API_ENDPOINT}/${API_VERSION}/`,
|
|
130
|
+
url: `customers/${getCleanCid(customerId)}/searchAds360:searchStream`,
|
|
131
|
+
headers,
|
|
132
|
+
data: { query },
|
|
133
|
+
method: 'POST',
|
|
134
|
+
responseType: 'stream',
|
|
135
|
+
};
|
|
136
|
+
const response = await gaxiosRequest(options);
|
|
98
137
|
return response.data;
|
|
99
138
|
}
|
|
100
139
|
|
|
101
140
|
/**
|
|
102
|
-
* Gets
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
141
|
+
* Gets the report stream through REST interface.
|
|
142
|
+
* Based on the `fieldMask` in the response to filter out
|
|
143
|
+
* selected fields of the report and returns an array of JSON format strings
|
|
144
|
+
* with the delimit of a line breaker.
|
|
106
145
|
* @param {string} customerId
|
|
107
146
|
* @param {string} loginCustomerId Login customer account ID (Mcc Account id).
|
|
108
|
-
* @param {string} query
|
|
109
|
-
* @
|
|
110
|
-
* @
|
|
147
|
+
* @param {string} query A Google Ads Query string.
|
|
148
|
+
* @param {boolean=} snakeCase Output JSON objects in snake_case.
|
|
149
|
+
* @return {!Promise<stream>}
|
|
111
150
|
*/
|
|
112
|
-
async
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
151
|
+
async cleanedRestStreamReport(customerId, loginCustomerId, query,
|
|
152
|
+
snakeCase = false) {
|
|
153
|
+
const transform = new RestSearchStreamTransform(snakeCase);
|
|
154
|
+
const stream =
|
|
155
|
+
await this.restStreamReport(customerId, loginCustomerId, query);
|
|
156
|
+
return stream.on('error', (error) => transform.emit('error', error))
|
|
157
|
+
.pipe(transform);
|
|
119
158
|
}
|
|
120
159
|
|
|
121
160
|
/**
|
|
122
161
|
* Returns the requested field or resource (artifact) used by SearchAds360Service.
|
|
123
162
|
* This service doesn't require `login-customer-id` HTTP header.
|
|
124
163
|
* @see https://developers.google.com/search-ads/reporting/api/reference/rest/v0/searchAds360Fields/get
|
|
125
|
-
* @param {string}
|
|
164
|
+
* @param {string} fieldName
|
|
126
165
|
* @return {!SearchAds360Field}
|
|
127
166
|
* @see https://developers.google.com/search-ads/reporting/api/reference/rest/v0/searchAds360Fields#SearchAds360Field
|
|
128
167
|
*/
|
|
129
|
-
async getReportField(
|
|
130
|
-
const searchads = await this.
|
|
131
|
-
const
|
|
168
|
+
async getReportField(fieldName) {
|
|
169
|
+
const searchads = await this.getApiClient();
|
|
170
|
+
const resourceName = `searchAds360Fields/${fieldName}`;
|
|
171
|
+
const response =
|
|
172
|
+
await searchads.searchAds360Fields.get({ resourceName });
|
|
132
173
|
return response.data;
|
|
133
174
|
}
|
|
134
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Returns resources information from Search Ads API.
|
|
178
|
+
* @see: https://developers.google.com/search-ads/reporting/api/reference/rest/v0/searchAds360Fields
|
|
179
|
+
* Note, it looks like this function doesn't check the CID, just using OAuth.
|
|
180
|
+
* @param {Array<string>} adFields Array of Ad fields.
|
|
181
|
+
* @param {Array<string>} metadata Select fields, default values are:
|
|
182
|
+
* name, data_type, is_repeated, type_url.
|
|
183
|
+
* @return {!Promise<!Array<GoogleAdsField>>}
|
|
184
|
+
* @see GoogleAdsApi.searchReportField
|
|
185
|
+
*/
|
|
186
|
+
async searchReportField(adFields,
|
|
187
|
+
metadata = ['name', 'data_type', 'is_repeated', 'type_url',]) {
|
|
188
|
+
const searchads = await this.getApiClient();
|
|
189
|
+
const selectClause = metadata.join(',');
|
|
190
|
+
const fields = adFields.join('","');
|
|
191
|
+
const query = `SELECT ${selectClause} WHERE name IN ("${fields}")`;
|
|
192
|
+
const response =
|
|
193
|
+
await searchads.searchAds360Fields.search({ query, pageSize: 10000 });
|
|
194
|
+
return response.data.results;
|
|
195
|
+
}
|
|
196
|
+
|
|
135
197
|
/**
|
|
136
198
|
* Returns all the custom columns associated with the customer in full detail.
|
|
137
199
|
* @see https://developers.google.com/search-ads/reporting/api/reference/rest/v0/customers.customColumns/list
|
|
@@ -141,7 +203,7 @@ class SearchAds {
|
|
|
141
203
|
* @see https://developers.google.com/search-ads/reporting/api/reference/rest/v0/customers.customColumns#CustomColumn
|
|
142
204
|
*/
|
|
143
205
|
async listCustomColumns(customerId, loginCustomerId) {
|
|
144
|
-
const searchads = await this.
|
|
206
|
+
const searchads = await this.getApiClient(loginCustomerId);
|
|
145
207
|
const response = await searchads.customers.customColumns.list({ customerId });
|
|
146
208
|
return response.data.customColumns;
|
|
147
209
|
}
|
|
@@ -157,7 +219,7 @@ class SearchAds {
|
|
|
157
219
|
*/
|
|
158
220
|
async getCustomColumn(columnId, customerId, loginCustomerId) {
|
|
159
221
|
const resourceName = `customers/${customerId}/customColumns/${columnId}`;
|
|
160
|
-
const searchads = await this.
|
|
222
|
+
const searchads = await this.getApiClient(loginCustomerId);
|
|
161
223
|
const response = await searchads.customers.customColumns.get({ resourceName });
|
|
162
224
|
return response.data;
|
|
163
225
|
}
|
package/src/apis/spreadsheets.js
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
'use strict';
|
|
20
20
|
|
|
21
21
|
const {google} = require('googleapis');
|
|
22
|
+
const { GoogleApiClient } = require('./base/google_api_client.js');
|
|
22
23
|
const {Params$Resource$Spreadsheets$Get} = google.sheets;
|
|
23
24
|
const AuthClient = require('./auth_client.js');
|
|
24
25
|
const {getLogger, BatchResult} = require('../components/utils.js');
|
|
@@ -63,7 +64,7 @@ let DimensionRange;
|
|
|
63
64
|
/**
|
|
64
65
|
* Google Spreadsheets API v4 stub.
|
|
65
66
|
*/
|
|
66
|
-
class Spreadsheets {
|
|
67
|
+
class Spreadsheets extends GoogleApiClient {
|
|
67
68
|
/**
|
|
68
69
|
* Init Spreadsheets API client.
|
|
69
70
|
* @param {string} spreadsheetId
|
|
@@ -71,29 +72,21 @@ class Spreadsheets {
|
|
|
71
72
|
* variables.
|
|
72
73
|
*/
|
|
73
74
|
constructor(spreadsheetId, env = process.env) {
|
|
75
|
+
super(env);
|
|
76
|
+
this.googleApi = 'sheets';
|
|
74
77
|
/** @const {string} */
|
|
75
78
|
this.spreadsheetId = spreadsheetId;
|
|
76
|
-
this.authClient = new AuthClient(API_SCOPES, env);
|
|
77
|
-
/**
|
|
78
|
-
* Logger object from 'log4js' package where this type is not exported.
|
|
79
|
-
*/
|
|
80
79
|
this.logger = getLogger('API.GS');
|
|
81
80
|
}
|
|
82
81
|
|
|
83
|
-
/**
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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;
|
|
82
|
+
/** @override */
|
|
83
|
+
getScope() {
|
|
84
|
+
return API_SCOPES;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** @override */
|
|
88
|
+
getVersion() {
|
|
89
|
+
return API_VERSION;
|
|
97
90
|
}
|
|
98
91
|
|
|
99
92
|
/**
|
|
@@ -108,7 +101,7 @@ class Spreadsheets {
|
|
|
108
101
|
spreadsheetId: this.spreadsheetId,
|
|
109
102
|
ranges: sheetName,
|
|
110
103
|
};
|
|
111
|
-
const sheets = await this.
|
|
104
|
+
const sheets = await this.getApiClient();
|
|
112
105
|
const response = await sheets.spreadsheets.get(request);
|
|
113
106
|
const sheet = response.data.sheets[0];
|
|
114
107
|
this.logger.debug(`Get sheet[${sheetName}]: `, sheet);
|
|
@@ -127,7 +120,7 @@ class Spreadsheets {
|
|
|
127
120
|
range: sheetName,
|
|
128
121
|
};
|
|
129
122
|
try {
|
|
130
|
-
const sheets = await this.
|
|
123
|
+
const sheets = await this.getApiClient();
|
|
131
124
|
const response = await sheets.spreadsheets.values.clear(request);
|
|
132
125
|
const data = response.data;
|
|
133
126
|
this.logger.debug(`Clear sheet[${sheetName}}]: `, data);
|
|
@@ -180,7 +173,7 @@ class Spreadsheets {
|
|
|
180
173
|
ranges: sheetName,
|
|
181
174
|
};
|
|
182
175
|
try {
|
|
183
|
-
const sheets = await this.
|
|
176
|
+
const sheets = await this.getApiClient();
|
|
184
177
|
const response = await sheets.spreadsheets.get(request);
|
|
185
178
|
const sheet = response.data.sheets[0];
|
|
186
179
|
const sheetId = sheet.properties.sheetId;
|
|
@@ -232,7 +225,7 @@ class Spreadsheets {
|
|
|
232
225
|
numberOfLines: data.trim().split('\n').length,
|
|
233
226
|
};
|
|
234
227
|
try {
|
|
235
|
-
const sheets = await this.
|
|
228
|
+
const sheets = await this.getApiClient();
|
|
236
229
|
const response = await sheets.spreadsheets.batchUpdate(request);
|
|
237
230
|
const data = response.data;
|
|
238
231
|
this.logger.debug(`Batch[${batchId}] uploaded: `, data);
|
package/src/apis/youtube.js
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
'use strict';
|
|
20
20
|
|
|
21
21
|
const {google} = require('googleapis');
|
|
22
|
+
const { GoogleApiClient } = require('./base/google_api_client.js');
|
|
22
23
|
const {
|
|
23
24
|
Schema$Channel,
|
|
24
25
|
Schema$Video,
|
|
@@ -26,8 +27,7 @@ const {
|
|
|
26
27
|
Schema$Playlist,
|
|
27
28
|
Schema$Search,
|
|
28
29
|
} = google.youtube;
|
|
29
|
-
const
|
|
30
|
-
const {getLogger} = require('../components/utils.js');
|
|
30
|
+
const { getLogger } = require('../components/utils.js');
|
|
31
31
|
|
|
32
32
|
const API_SCOPES = Object.freeze([
|
|
33
33
|
'https://www.googleapis.com/auth/youtube.force-ssl'
|
|
@@ -159,34 +159,26 @@ let ListSearchConfig;
|
|
|
159
159
|
* Search list type definition, see:
|
|
160
160
|
* https://developers.google.com/youtube/v3/docs/search/list
|
|
161
161
|
*/
|
|
162
|
-
class YouTube {
|
|
162
|
+
class YouTube extends GoogleApiClient {
|
|
163
163
|
/**
|
|
164
164
|
* @constructor
|
|
165
165
|
* @param {!Object<string,string>=} env The environment object to hold env
|
|
166
166
|
* variables.
|
|
167
167
|
*/
|
|
168
168
|
constructor(env = process.env) {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
* Logger object from 'log4js' package where this type is not exported.
|
|
172
|
-
*/
|
|
169
|
+
super(env);
|
|
170
|
+
this.googleApi = 'youtube';
|
|
173
171
|
this.logger = getLogger('API.YT');
|
|
174
172
|
}
|
|
175
173
|
|
|
176
|
-
/**
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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;
|
|
174
|
+
/** @override */
|
|
175
|
+
getScope() {
|
|
176
|
+
return API_SCOPES;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** @override */
|
|
180
|
+
getVersion() {
|
|
181
|
+
return API_VERSION;
|
|
190
182
|
}
|
|
191
183
|
|
|
192
184
|
/**
|
|
@@ -200,7 +192,7 @@ class YouTube {
|
|
|
200
192
|
const channelListRequest = Object.assign({}, config);
|
|
201
193
|
channelListRequest.part = channelListRequest.part.join(',')
|
|
202
194
|
try {
|
|
203
|
-
const youtube = await this.
|
|
195
|
+
const youtube = await this.getApiClient();
|
|
204
196
|
const response = await youtube.channels.list(channelListRequest);
|
|
205
197
|
this.logger.debug('Response: ', response);
|
|
206
198
|
return response.data.items;
|
|
@@ -223,7 +215,7 @@ class YouTube {
|
|
|
223
215
|
const videoListRequest = Object.assign({}, config);
|
|
224
216
|
videoListRequest.part = videoListRequest.part.join(',')
|
|
225
217
|
try {
|
|
226
|
-
const youtube = await this.
|
|
218
|
+
const youtube = await this.getApiClient();
|
|
227
219
|
const response = await youtube.videos.list(videoListRequest);
|
|
228
220
|
this.logger.debug('Response: ', response);
|
|
229
221
|
return response.data.items;
|
|
@@ -247,7 +239,7 @@ class YouTube {
|
|
|
247
239
|
const commentThreadsRequest = Object.assign({}, config);
|
|
248
240
|
commentThreadsRequest.part = commentThreadsRequest.part.join(',')
|
|
249
241
|
try {
|
|
250
|
-
const youtube = await this.
|
|
242
|
+
const youtube = await this.getApiClient();
|
|
251
243
|
const response = await youtube.commentThreads.list(commentThreadsRequest);
|
|
252
244
|
this.logger.debug('Response: ', response.data);
|
|
253
245
|
return response.data.items;
|
|
@@ -282,7 +274,7 @@ class YouTube {
|
|
|
282
274
|
}
|
|
283
275
|
|
|
284
276
|
try {
|
|
285
|
-
const youtube = await this.
|
|
277
|
+
const youtube = await this.getApiClient();
|
|
286
278
|
const response = await youtube.playlists.list(playlistsRequest);
|
|
287
279
|
this.logger.debug('Response: ', response.data);
|
|
288
280
|
if (response.data.nextPageToken) {
|
|
@@ -324,7 +316,7 @@ class YouTube {
|
|
|
324
316
|
}
|
|
325
317
|
|
|
326
318
|
try {
|
|
327
|
-
const youtube = await this.
|
|
319
|
+
const youtube = await this.getApiClient();
|
|
328
320
|
const response = await youtube.search.list(searchRequest);
|
|
329
321
|
this.logger.debug('Response: ', response.data);
|
|
330
322
|
if (response.data.nextPageToken) {
|
|
@@ -75,7 +75,7 @@ class DatastoreModeAccess {
|
|
|
75
75
|
*/
|
|
76
76
|
getKey(id) {
|
|
77
77
|
const keyPath = [this.kind];
|
|
78
|
-
if (id) keyPath.push(isNaN(id) ? id :
|
|
78
|
+
if (id) keyPath.push(isNaN(id) ? id : this.datastore.int(id));
|
|
79
79
|
return this.datastore.key({
|
|
80
80
|
namespace: this.namespace,
|
|
81
81
|
path: keyPath,
|
|
@@ -116,10 +116,11 @@ class DatastoreModeAccess {
|
|
|
116
116
|
const key = this.getKey(id);
|
|
117
117
|
const apiResponse =
|
|
118
118
|
await this.datastore.save({ key, data, excludeLargeProperties: true });
|
|
119
|
-
// Default key in Datastore is
|
|
119
|
+
// Default key in Datastore is an int as string in response. It could be
|
|
120
|
+
// larger than JavaScript max safe integer, so keep it as string here.
|
|
120
121
|
// With a given id, the key in response is null.
|
|
121
122
|
const updatedId = id !== undefined ? id
|
|
122
|
-
:
|
|
123
|
+
: apiResponse[0].mutationResults[0].key.path[0].id;
|
|
123
124
|
this.logger.debug(`Result of saving ${updatedId}@${this.kind}: `,
|
|
124
125
|
JSON.stringify(apiResponse));
|
|
125
126
|
// Datastore has a delay to write entity. This method only returns id
|
package/src/components/utils.js
CHANGED
|
@@ -487,9 +487,9 @@ const extractObject = (paths) => {
|
|
|
487
487
|
return (sourceObject) => {
|
|
488
488
|
const output = {};
|
|
489
489
|
paths.forEach((path) => {
|
|
490
|
-
const [value, owner, property] = path.split('.')
|
|
490
|
+
const [value, owner, property] = path.trim().split('.')
|
|
491
491
|
.reduce(transcribe, [sourceObject, output, undefined]);
|
|
492
|
-
if (value) {
|
|
492
|
+
if (typeof value !== 'undefined') {
|
|
493
493
|
owner[property] = value;
|
|
494
494
|
}
|
|
495
495
|
});
|
|
@@ -509,7 +509,7 @@ const getObjectByPath = (obj, paths) => {
|
|
|
509
509
|
paths.split('.').filter((key) => !!key).forEach((key) => {
|
|
510
510
|
instance = instance[key];
|
|
511
511
|
if (!instance) {
|
|
512
|
-
console.error('Fail to get
|
|
512
|
+
console.error('Fail to get element from path:', paths);
|
|
513
513
|
return instance;
|
|
514
514
|
}
|
|
515
515
|
});
|
|
@@ -591,7 +591,24 @@ const changeObjectNamingFromLowerCamelToSnake = (obj) => {
|
|
|
591
591
|
} else {
|
|
592
592
|
return obj;
|
|
593
593
|
}
|
|
594
|
-
}
|
|
594
|
+
};
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Generates a function that can convert a given JSON object to a JSON string
|
|
598
|
+
* with only specified fields(fieldMask), in specified naming convention.
|
|
599
|
+
* @param {string} fieldMask The 'fieldMask' string from response.
|
|
600
|
+
* @param {boolean=} snakeCase Whether or not output JSON in snake naming.
|
|
601
|
+
*/
|
|
602
|
+
const getFilterAndStringifyFn = (fieldMask, snakeCase = false) => {
|
|
603
|
+
const extractor = extractObject(
|
|
604
|
+
Array.isArray(fieldMask) ? fieldMask : fieldMask.split(','));
|
|
605
|
+
return (originalObject) => {
|
|
606
|
+
const extracted = extractor(originalObject);
|
|
607
|
+
const generatedObject = snakeCase
|
|
608
|
+
? changeObjectNamingFromLowerCamelToSnake(extracted) : extracted;
|
|
609
|
+
return JSON.stringify(generatedObject);
|
|
610
|
+
};
|
|
611
|
+
};
|
|
595
612
|
|
|
596
613
|
/**
|
|
597
614
|
* Returns the response data for a HTTP request. It will retry the specific
|
|
@@ -619,7 +636,7 @@ const requestWithRetry = async (options, logger = console, retryTimes = 3) => {
|
|
|
619
636
|
logger.error(`Request ${JSON.stringify(options)}`, error);
|
|
620
637
|
}
|
|
621
638
|
} while (processedTimes <= retryTimes)
|
|
622
|
-
}
|
|
639
|
+
};
|
|
623
640
|
|
|
624
641
|
// noinspection JSUnusedAssignment
|
|
625
642
|
module.exports = {
|
|
@@ -641,5 +658,6 @@ module.exports = {
|
|
|
641
658
|
changeNamingFromLowerCamelToSnake,
|
|
642
659
|
changeObjectNamingFromSnakeToLowerCamel,
|
|
643
660
|
changeObjectNamingFromLowerCamelToSnake,
|
|
661
|
+
getFilterAndStringifyFn,
|
|
644
662
|
requestWithRetry,
|
|
645
663
|
};
|