@google-cloud/nodejs-common 0.9.4-beta2 → 0.9.5-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 +4 -0
- package/package.json +3 -2
- package/src/apis/auth_client.js +15 -0
- package/src/apis/dfa_reporting.js +67 -19
- package/src/apis/doubleclick_search.js +161 -89
- package/src/apis/google_ads.js +198 -46
- package/src/apis/index.js +10 -0
- package/src/apis/measurement_protocol.js +45 -25
- package/src/apis/measurement_protocol_ga4.js +7 -1
- package/src/apis/spreadsheets.js +13 -13
- package/src/apis/youtube.js +356 -0
- package/src/components/utils.js +47 -12
|
@@ -0,0 +1,356 @@
|
|
|
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 Youtube API Client Library.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
'use strict';
|
|
20
|
+
|
|
21
|
+
const {google} = require('googleapis');
|
|
22
|
+
const {
|
|
23
|
+
Schema$Channel,
|
|
24
|
+
Schema$Video,
|
|
25
|
+
Schema$CommentThread,
|
|
26
|
+
Schema$Playlist,
|
|
27
|
+
Schema$Search,
|
|
28
|
+
} = google.youtube;
|
|
29
|
+
const AuthClient = require('./auth_client.js');
|
|
30
|
+
const {getLogger} = require('../components/utils.js');
|
|
31
|
+
|
|
32
|
+
const API_SCOPES = Object.freeze([
|
|
33
|
+
'https://www.googleapis.com/auth/youtube.force-ssl'
|
|
34
|
+
]);
|
|
35
|
+
const API_VERSION = 'v3';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Configuration for listing youtube channels.
|
|
39
|
+
* @see https://developers.google.com/youtube/v3/docs/channels/list
|
|
40
|
+
* @typedef {{
|
|
41
|
+
* part: Array<string>,
|
|
42
|
+
* categoryId: (string|undefined),
|
|
43
|
+
* forUsername: (string|undefined),
|
|
44
|
+
* id: (string|undefined),
|
|
45
|
+
* managedByMe: (boolean|undefined),
|
|
46
|
+
* mine: (boolean|undefined),
|
|
47
|
+
* hl: (string|undefined),
|
|
48
|
+
* onBehalfOfContentOwner: (string|undefined),
|
|
49
|
+
* pageToken: (string|undefined),
|
|
50
|
+
* }}
|
|
51
|
+
*/
|
|
52
|
+
let ListChannelsConfig;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Configuration for listing youtube videos.
|
|
56
|
+
* @see https://developers.google.com/youtube/v3/docs/videos/list
|
|
57
|
+
* @typedef {{
|
|
58
|
+
* part: Array<string>,
|
|
59
|
+
* id: (string|undefined),
|
|
60
|
+
* chart: (string|undefined),
|
|
61
|
+
* hl: (string|undefined),
|
|
62
|
+
* maxHeight: (unsigned integer|undefined),
|
|
63
|
+
* maxResults: (unsigned integer|undefined),
|
|
64
|
+
* maxWidth: (unsigned integer|undefined),
|
|
65
|
+
* onBehalfOfContentOwner: (string|undefined),
|
|
66
|
+
* pageToken: (string|undefined),
|
|
67
|
+
* regionCode: (string|undefined),
|
|
68
|
+
* }}
|
|
69
|
+
*/
|
|
70
|
+
let ListVideosConfig;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Configuration for listing youtube comment threads by the given id.
|
|
74
|
+
* @see https://developers.google.com/youtube/v3/docs/commentThreads/list
|
|
75
|
+
* @typedef {{
|
|
76
|
+
* part: Array<string>,
|
|
77
|
+
* id: (string|undefined),
|
|
78
|
+
* allThreadsRelatedToChannelId: (string|undefined),
|
|
79
|
+
* channelId: (string|undefined),
|
|
80
|
+
* videoId: (string|undefined),
|
|
81
|
+
* maxResults: (unsigned integer|undefined),
|
|
82
|
+
* moderationStatus: (string|undefined),
|
|
83
|
+
* order: (string|undefined),
|
|
84
|
+
* pageToken: (string|undefined),
|
|
85
|
+
* searchTerms: (string|undefined),
|
|
86
|
+
* textFormat: (string|undefined),
|
|
87
|
+
* limit: (unsigned integer|undefined),
|
|
88
|
+
* }}
|
|
89
|
+
*/
|
|
90
|
+
let ListCommentThreadsConfig;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Configuration for listing youtube play list.
|
|
94
|
+
* @see https://developers.google.com/youtube/v3/docs/Playlists/list
|
|
95
|
+
* @typedef {{
|
|
96
|
+
* part: Array<string>,
|
|
97
|
+
* channelId: (string|undefined),
|
|
98
|
+
* id: (string|undefined),
|
|
99
|
+
* mine: (boolean|undefined),
|
|
100
|
+
* hl: (string|undefined),
|
|
101
|
+
* maxResults: (unsigned integer|undefined),
|
|
102
|
+
* onBehalfOfContentOwner: (string|undefined),
|
|
103
|
+
* onBehalfOfContentOwnerChannel: (string|undefined),
|
|
104
|
+
* pageToken: (string|undefined)
|
|
105
|
+
* }}
|
|
106
|
+
*/
|
|
107
|
+
let ListPlaylistConfig;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Configuration for listing youtube search results.
|
|
111
|
+
* @see https://developers.google.com/youtube/v3/docs/search/list
|
|
112
|
+
* @typedef {{
|
|
113
|
+
* part: Array<string>,
|
|
114
|
+
* forContentOwner: (boolean|undefined),
|
|
115
|
+
* forDeveloper: (boolean|undefined),
|
|
116
|
+
* forMine: (boolean|undefined),
|
|
117
|
+
* relatedToVideoId: (string|undefined),
|
|
118
|
+
* channelId: (string|undefined),
|
|
119
|
+
* channelType: (string|undefined),
|
|
120
|
+
* eventType: (string|undefined),
|
|
121
|
+
* location: (string|undefined),
|
|
122
|
+
* locationRadius: (string|undefined),
|
|
123
|
+
* maxResults: (unsigned integer|undefined),
|
|
124
|
+
* onBehalfOfContentOwner: (string|undefined),
|
|
125
|
+
* order: (string|undefined),
|
|
126
|
+
* pageToken: (string|undefined),
|
|
127
|
+
* publishedAfter: (datetime|undefined),
|
|
128
|
+
* publishedBefore: (datetime|undefined),
|
|
129
|
+
* q: (string|undefined),
|
|
130
|
+
* regionCode: (string|undefined),
|
|
131
|
+
* relevanceLanguage: (string|undefined),
|
|
132
|
+
* safeSearch: (string|undefined),
|
|
133
|
+
* topicId: (string|undefined),
|
|
134
|
+
* type: (string|undefined),
|
|
135
|
+
* videoCaption: (string|undefined),
|
|
136
|
+
* videoCategoryId: (string|undefined),
|
|
137
|
+
* videoDefinition: (string|undefined),
|
|
138
|
+
* videoDimension: (string|undefined),
|
|
139
|
+
* videoDuration: (string|undefined),
|
|
140
|
+
* videoEmbeddable: (string|undefined),
|
|
141
|
+
* videoLicense: (string|undefined),
|
|
142
|
+
* videoSyndicated: (string|undefined),
|
|
143
|
+
* videoType: (string|undefined)
|
|
144
|
+
* }}
|
|
145
|
+
*/
|
|
146
|
+
let ListSearchConfig;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Youtube API v3 stub.
|
|
150
|
+
* See: https://developers.google.com/youtube/v3/docs
|
|
151
|
+
* Channel list type definition, see:
|
|
152
|
+
* https://developers.google.com/youtube/v3/docs/channels/list
|
|
153
|
+
* Video list type definition, see:
|
|
154
|
+
* https://developers.google.com/youtube/v3/docs/videos/list
|
|
155
|
+
* CommentThread list type definition, see:
|
|
156
|
+
* https://developers.google.com/youtube/v3/docs/commentThreads/list
|
|
157
|
+
* Playlist list type definition, see:
|
|
158
|
+
* https://developers.google.com/youtube/v3/docs/playlists/list
|
|
159
|
+
* Search list type definition, see:
|
|
160
|
+
* https://developers.google.com/youtube/v3/docs/search/list
|
|
161
|
+
*/
|
|
162
|
+
class YouTube {
|
|
163
|
+
/**
|
|
164
|
+
* @constructor
|
|
165
|
+
* @param {!Object<string,string>=} env The environment object to hold env
|
|
166
|
+
* variables.
|
|
167
|
+
*/
|
|
168
|
+
constructor(env = process.env) {
|
|
169
|
+
const authClient = new AuthClient(API_SCOPES, env);
|
|
170
|
+
this.auth = authClient.getDefaultAuth();
|
|
171
|
+
/** @const {!google.youtube} */
|
|
172
|
+
this.instance = google.youtube({
|
|
173
|
+
version: API_VERSION,
|
|
174
|
+
});
|
|
175
|
+
/**
|
|
176
|
+
* Logger object from 'log4js' package where this type is not exported.
|
|
177
|
+
*/
|
|
178
|
+
this.logger = getLogger('API.YT');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Returns a collection of zero or more channel resources that match the
|
|
183
|
+
* request criteria.
|
|
184
|
+
* @see https://developers.google.com/youtube/v3/docs/channels/list
|
|
185
|
+
* @param {!ListChannelsConfig} config List channels configuration.
|
|
186
|
+
* @return {!Promise<Array<Schema$Channel>>}
|
|
187
|
+
*/
|
|
188
|
+
async listChannels(config) {
|
|
189
|
+
const channelListRequest = Object.assign({
|
|
190
|
+
auth: this.auth,
|
|
191
|
+
}, config);
|
|
192
|
+
channelListRequest.part = channelListRequest.part.join(',')
|
|
193
|
+
try {
|
|
194
|
+
const response = await this.instance.channels.list(channelListRequest);
|
|
195
|
+
this.logger.debug('Response: ', response);
|
|
196
|
+
return response.data.items;
|
|
197
|
+
} catch (error) {
|
|
198
|
+
const errorMsg =
|
|
199
|
+
`Fail to list channels: ${JSON.stringify(channelListRequest)}`;
|
|
200
|
+
this.logger.error('YouTube list channels failed.', error.message);
|
|
201
|
+
this.logger.debug('Errors in response:', error);
|
|
202
|
+
throw new Error(errorMsg);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Returns a list of videos that match the API request parameters.
|
|
208
|
+
* @see https://developers.google.com/youtube/v3/docs/videos/list
|
|
209
|
+
* @param {!ListVideosConfig} config List videos configuration.
|
|
210
|
+
* @return {!Promise<Array<Schema$Video>>}
|
|
211
|
+
*/
|
|
212
|
+
async listVideos(config) {
|
|
213
|
+
const videoListRequest = Object.assign({
|
|
214
|
+
auth: this.auth,
|
|
215
|
+
}, config);
|
|
216
|
+
videoListRequest.part = videoListRequest.part.join(',')
|
|
217
|
+
try {
|
|
218
|
+
const response = await this.instance.videos.list(videoListRequest);
|
|
219
|
+
this.logger.debug('Response: ', response);
|
|
220
|
+
return response.data.items;
|
|
221
|
+
} catch (error) {
|
|
222
|
+
const errorMsg =
|
|
223
|
+
`Fail to list videos: ${JSON.stringify(videoListRequest)}`;
|
|
224
|
+
this.logger.error('YouTube list videos failed.', error.message);
|
|
225
|
+
this.logger.debug('Errors in response:', error);
|
|
226
|
+
throw new Error(errorMsg);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Returns a list of comment threads that match the API request parameters.
|
|
232
|
+
* @see https://developers.google.com/youtube/v3/docs/videos/list
|
|
233
|
+
* @param {!ListCommentThreadsConfig} config List comment threads
|
|
234
|
+
* configuration.
|
|
235
|
+
* @return {!Promise<Array<Schema$CommentThread>>}
|
|
236
|
+
*/
|
|
237
|
+
async listCommentThreads(config) {
|
|
238
|
+
const commentThreadsRequest = Object.assign({
|
|
239
|
+
auth: this.auth,
|
|
240
|
+
}, config);
|
|
241
|
+
commentThreadsRequest.part = commentThreadsRequest.part.join(',')
|
|
242
|
+
try {
|
|
243
|
+
const response = await this.instance.commentThreads.list(
|
|
244
|
+
commentThreadsRequest);
|
|
245
|
+
this.logger.debug('Response: ', response.data);
|
|
246
|
+
return response.data.items;
|
|
247
|
+
} catch (error) {
|
|
248
|
+
const errorMsg =
|
|
249
|
+
`Fail to list comment threads: ${JSON.stringify(commentThreadsRequest)}`;
|
|
250
|
+
this.logger.error('YouTube list comment threads failed.', error.message);
|
|
251
|
+
this.logger.debug('Errors in response:', error);
|
|
252
|
+
throw new Error(errorMsg);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Returns a collection of playlists that match the API request parameters.
|
|
258
|
+
* @see https://developers.google.com/youtube/v3/docs/playlists/list
|
|
259
|
+
* @param {!ListPlaylistConfig} config List playlist configuration.
|
|
260
|
+
* @param {number} resultLimit The limit of the number of results.
|
|
261
|
+
* @param {string} pageToken Token to identify a specific page in the result.
|
|
262
|
+
* @return {!Promise<Array<Schema$Playlist>>}
|
|
263
|
+
*/
|
|
264
|
+
async listPlaylists(config, resultLimit = 1000, pageToken = null) {
|
|
265
|
+
if (resultLimit <= 0) return [];
|
|
266
|
+
|
|
267
|
+
const playlistsRequest = Object.assign({
|
|
268
|
+
auth: this.auth,
|
|
269
|
+
}, config, {
|
|
270
|
+
pageToken
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
if (Array.isArray(playlistsRequest.part)) {
|
|
274
|
+
playlistsRequest.part = playlistsRequest.part.join(',');
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
try {
|
|
278
|
+
const response = await this.instance.playlists.list(
|
|
279
|
+
playlistsRequest);
|
|
280
|
+
this.logger.debug('Response: ', response.data);
|
|
281
|
+
if (response.data.nextPageToken) {
|
|
282
|
+
this.logger.debug(
|
|
283
|
+
'Call youtube playlist:list API agian with Token: ',
|
|
284
|
+
response.data.nextPageToken);
|
|
285
|
+
const nextResponse = await this.listPlaylists(
|
|
286
|
+
config,
|
|
287
|
+
resultLimit - response.data.items.length,
|
|
288
|
+
response.data.nextPageToken);
|
|
289
|
+
return response.data.items.concat(nextResponse);
|
|
290
|
+
}
|
|
291
|
+
return response.data.items;
|
|
292
|
+
} catch (error) {
|
|
293
|
+
const errorMsg =
|
|
294
|
+
`Fail to list playlists: ${JSON.stringify(playlistsRequest)}`;
|
|
295
|
+
this.logger.error('YouTube list playlists failed.', error.message);
|
|
296
|
+
this.logger.debug('Errors in response:', error);
|
|
297
|
+
throw new Error(errorMsg);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Returns a collection of search results that match the query parameters
|
|
303
|
+
* specified in the API request.
|
|
304
|
+
* @see https://developers.google.com/youtube/v3/docs/search/list
|
|
305
|
+
* @param {!ListSearchConfig} config List search result configuration.
|
|
306
|
+
* @param {number} resultLimit The limit of the number of results.
|
|
307
|
+
* @param {string} pageToken Token to identify a specific page in the result.
|
|
308
|
+
* @return {!Promise<Array<Schema$Search>>}
|
|
309
|
+
*/
|
|
310
|
+
async listSearchResults(config, resultLimit = 1000, pageToken = null) {
|
|
311
|
+
if (resultLimit <= 0) return [];
|
|
312
|
+
|
|
313
|
+
const searchRequest = Object.assign({
|
|
314
|
+
auth: this.auth,
|
|
315
|
+
}, config, {
|
|
316
|
+
pageToken
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
if (Array.isArray(searchRequest.part)) {
|
|
320
|
+
searchRequest.part = searchRequest.part.join(',');
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
try {
|
|
324
|
+
const response = await this.instance.search.list(searchRequest);
|
|
325
|
+
this.logger.debug('Response: ', response.data);
|
|
326
|
+
if (response.data.nextPageToken) {
|
|
327
|
+
this.logger.debug(
|
|
328
|
+
'Call youtube search:list API agian with Token: ',
|
|
329
|
+
response.data.nextPageToken);
|
|
330
|
+
const nextResponse = await this.listSearchResults(
|
|
331
|
+
config,
|
|
332
|
+
resultLimit - response.data.items.length,
|
|
333
|
+
response.data.nextPageToken);
|
|
334
|
+
return response.data.items.concat(nextResponse);
|
|
335
|
+
}
|
|
336
|
+
return response.data.items;
|
|
337
|
+
} catch (error) {
|
|
338
|
+
const errorMsg =
|
|
339
|
+
`Fail to list search results: ${JSON.stringify(searchRequest)}`;
|
|
340
|
+
this.logger.error('YouTube list search results failed.', error.message);
|
|
341
|
+
this.logger.debug('Errors in response:', error);
|
|
342
|
+
throw new Error(errorMsg);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
module.exports = {
|
|
348
|
+
YouTube,
|
|
349
|
+
ListChannelsConfig,
|
|
350
|
+
ListVideosConfig,
|
|
351
|
+
ListCommentThreadsConfig,
|
|
352
|
+
ListPlaylistConfig,
|
|
353
|
+
ListSearchConfig,
|
|
354
|
+
API_VERSION,
|
|
355
|
+
API_SCOPES,
|
|
356
|
+
};
|
package/src/components/utils.js
CHANGED
|
@@ -26,14 +26,17 @@ const {CloudPlatformApis} = require('../apis/cloud_platform_apis.js');
|
|
|
26
26
|
/**
|
|
27
27
|
* The result of a batch of data sent to target API. The batch here means the
|
|
28
28
|
* data that will be sent out in one single request.
|
|
29
|
+
* Some APIs allows partial failure: it will take those correct data and
|
|
30
|
+
* response with reasons for those failed ones. 'groupedFailed' uses error
|
|
31
|
+
* message as the key, and tthe array of related failed lines(records) as value.
|
|
29
32
|
* Some APIs upload whole file. In this case, there will be not 'numberOfLines'
|
|
30
|
-
* or 'failedLines'.
|
|
33
|
+
* or 'failedLines', etc.
|
|
31
34
|
* @typedef {{
|
|
32
35
|
* result: boolean,
|
|
33
36
|
* numberOfLines: (number|undefined),
|
|
34
37
|
* failedLines: (Array<string>|undefined),
|
|
35
38
|
* errors: (Array<string>|undefined),
|
|
36
|
-
*
|
|
39
|
+
* groupedFailed: (Object<string,Array<string>>|undefined),
|
|
37
40
|
* }}
|
|
38
41
|
*/
|
|
39
42
|
let BatchResult;
|
|
@@ -177,6 +180,27 @@ const splitArray = (records, splitSize) => {
|
|
|
177
180
|
return results;
|
|
178
181
|
};
|
|
179
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Merges an object of 'groupedFailed into the object 'mergedResult'
|
|
185
|
+
* @param {!BatchResult} mergedResult
|
|
186
|
+
* @param {!BatchResult} groupedFailed
|
|
187
|
+
* @private
|
|
188
|
+
*/
|
|
189
|
+
const mergeGroupedFailed_ = (mergedResult, groupedFailed) => {
|
|
190
|
+
if (groupedFailed) {
|
|
191
|
+
const mergedKeys = Object.keys(mergedResult.groupedFailed);
|
|
192
|
+
mergedKeys.forEach((key) => {
|
|
193
|
+
mergedResult.groupedFailed[key] =
|
|
194
|
+
mergedResult.groupedFailed[key].concat(groupedFailed[key]);
|
|
195
|
+
});
|
|
196
|
+
Object.keys(groupedFailed)
|
|
197
|
+
.filter((key) => mergedKeys.indexOf(key) < 0)
|
|
198
|
+
.forEach((key) => {
|
|
199
|
+
mergedResult.groupedFailed[key] = groupedFailed[key];
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
180
204
|
/**
|
|
181
205
|
* Merges an array of API results (BatchResult) in to a single one.
|
|
182
206
|
*
|
|
@@ -191,11 +215,17 @@ const mergeBatchResults = (batchResults, batchPrefix) => {
|
|
|
191
215
|
numberOfLines: 0,
|
|
192
216
|
failedLines: [],
|
|
193
217
|
errors: [],
|
|
194
|
-
|
|
218
|
+
groupedFailed: {},
|
|
195
219
|
};
|
|
196
220
|
batchResults.forEach((batchResult, index) => {
|
|
197
221
|
const batchId = `${batchPrefix}-${index}`;
|
|
198
|
-
const {
|
|
222
|
+
const {
|
|
223
|
+
result,
|
|
224
|
+
numberOfLines,
|
|
225
|
+
failedLines = [],
|
|
226
|
+
errors = [],
|
|
227
|
+
groupedFailed,
|
|
228
|
+
} = batchResult;
|
|
199
229
|
if (logger.isDebugEnabled()) {
|
|
200
230
|
logger.debug(
|
|
201
231
|
` Task [${batchId}] has ${numberOfLines} lines: ${result
|
|
@@ -207,10 +237,13 @@ const mergeBatchResults = (batchResults, batchPrefix) => {
|
|
|
207
237
|
}
|
|
208
238
|
mergedResult.result = mergedResult.result && result;
|
|
209
239
|
mergedResult.numberOfLines += numberOfLines;
|
|
210
|
-
mergedResult.failedLines = mergedResult.failedLines.concat(
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
240
|
+
mergedResult.failedLines = mergedResult.failedLines.concat(failedLines);
|
|
241
|
+
errors.forEach((error) => {
|
|
242
|
+
if (mergedResult.errors.indexOf(error) === -1) {
|
|
243
|
+
mergedResult.errors.push(error);
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
mergeGroupedFailed_(mergedResult, groupedFailed);
|
|
214
247
|
});
|
|
215
248
|
return mergedResult;
|
|
216
249
|
};
|
|
@@ -289,13 +322,14 @@ const apiSpeedControl = (recordSize = 1, numberOfThreads = 1, qps = 1,
|
|
|
289
322
|
const records = Array.isArray(data) ?
|
|
290
323
|
data :
|
|
291
324
|
data.split('\n').filter((line) => line.trim() !== '');
|
|
292
|
-
if (maxRecords <
|
|
293
|
-
const error = `Predicted timeout: ${
|
|
294
|
-
`${recordSize} records
|
|
325
|
+
if (maxRecords < records.length) {
|
|
326
|
+
const error = `Predicted timeout: ${records.length} records with config: `
|
|
327
|
+
+ `${recordSize} records/request and ${qps} QPS in ${timeout} sec.`;
|
|
295
328
|
logger.error(error);
|
|
296
329
|
return {
|
|
297
330
|
result: false,
|
|
298
|
-
|
|
331
|
+
numberOfLines: records.length,
|
|
332
|
+
errors: [error],
|
|
299
333
|
};
|
|
300
334
|
}
|
|
301
335
|
const roundArray = splitArray(records, roundSize);
|
|
@@ -523,6 +557,7 @@ module.exports = {
|
|
|
523
557
|
wait,
|
|
524
558
|
BatchResult,
|
|
525
559
|
SendSingleBatch,
|
|
560
|
+
mergeBatchResults,
|
|
526
561
|
sendSingleRound,
|
|
527
562
|
apiSpeedControl,
|
|
528
563
|
splitArray,
|