1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | Object.defineProperty(exports, "__esModule", { value: true });
|
18 | exports.util = exports.Util = exports.PartialFailureError = exports.ApiError = void 0;
|
19 |
|
20 |
|
21 |
|
22 | const projectify_1 = require("@google-cloud/projectify");
|
23 | const ent = require("ent");
|
24 | const extend = require("extend");
|
25 | const google_auth_library_1 = require("google-auth-library");
|
26 | const retryRequest = require("retry-request");
|
27 | const stream_1 = require("stream");
|
28 | const teeny_request_1 = require("teeny-request");
|
29 |
|
30 | const duplexify = require('duplexify');
|
31 | const requestDefaults = {
|
32 | timeout: 60000,
|
33 | gzip: true,
|
34 | forever: true,
|
35 | pool: {
|
36 | maxSockets: Infinity,
|
37 | },
|
38 | };
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 | const AUTO_RETRY_DEFAULT = true;
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 | const MAX_RETRY_DEFAULT = 3;
|
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 | class ApiError extends Error {
|
59 | constructor(errorBodyOrMessage) {
|
60 | super();
|
61 | if (typeof errorBodyOrMessage !== 'object') {
|
62 | this.message = errorBodyOrMessage || '';
|
63 | return;
|
64 | }
|
65 | const errorBody = errorBodyOrMessage;
|
66 | this.code = errorBody.code;
|
67 | this.errors = errorBody.errors;
|
68 | this.response = errorBody.response;
|
69 | try {
|
70 | this.errors = JSON.parse(this.response.body).error.errors;
|
71 | }
|
72 | catch (e) {
|
73 | this.errors = errorBody.errors;
|
74 | }
|
75 | this.message = ApiError.createMultiErrorMessage(errorBody, this.errors);
|
76 | Error.captureStackTrace(this);
|
77 | }
|
78 | |
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 | static createMultiErrorMessage(err, errors) {
|
89 | const messages = new Set();
|
90 | if (err.message) {
|
91 | messages.add(err.message);
|
92 | }
|
93 | if (errors && errors.length) {
|
94 | errors.forEach(({ message }) => messages.add(message));
|
95 | }
|
96 | else if (err.response && err.response.body) {
|
97 | messages.add(ent.decode(err.response.body.toString()));
|
98 | }
|
99 | else if (!err.message) {
|
100 | messages.add('A failure occurred during this request.');
|
101 | }
|
102 | let messageArr = Array.from(messages);
|
103 | if (messageArr.length > 1) {
|
104 | messageArr = messageArr.map((message, i) => ` ${i + 1}. ${message}`);
|
105 | messageArr.unshift('Multiple errors occurred during the request. Please see the `errors` array for complete details.\n');
|
106 | messageArr.push('\n');
|
107 | }
|
108 | return messageArr.join('\n');
|
109 | }
|
110 | }
|
111 | exports.ApiError = ApiError;
|
112 |
|
113 |
|
114 |
|
115 |
|
116 |
|
117 | class PartialFailureError extends Error {
|
118 | constructor(b) {
|
119 | super();
|
120 | const errorObject = b;
|
121 | this.errors = errorObject.errors;
|
122 | this.name = 'PartialFailureError';
|
123 | this.response = errorObject.response;
|
124 | this.message = ApiError.createMultiErrorMessage(errorObject, this.errors);
|
125 | }
|
126 | }
|
127 | exports.PartialFailureError = PartialFailureError;
|
128 | class Util {
|
129 | constructor() {
|
130 | this.ApiError = ApiError;
|
131 | this.PartialFailureError = PartialFailureError;
|
132 | }
|
133 | |
134 |
|
135 |
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 | noop() { }
|
142 | |
143 |
|
144 |
|
145 |
|
146 |
|
147 |
|
148 |
|
149 |
|
150 | handleResp(err, resp, body, callback) {
|
151 | callback = callback || util.noop;
|
152 | const parsedResp = extend(true, { err: err || null }, resp && util.parseHttpRespMessage(resp), body && util.parseHttpRespBody(body));
|
153 |
|
154 |
|
155 |
|
156 | if (!parsedResp.err && resp && typeof parsedResp.body === 'object') {
|
157 | parsedResp.resp.body = parsedResp.body;
|
158 | }
|
159 | if (parsedResp.err && resp) {
|
160 | parsedResp.err.response = resp;
|
161 | }
|
162 | callback(parsedResp.err, parsedResp.body, parsedResp.resp);
|
163 | }
|
164 | |
165 |
|
166 |
|
167 |
|
168 |
|
169 |
|
170 |
|
171 |
|
172 | parseHttpRespMessage(httpRespMessage) {
|
173 | const parsedHttpRespMessage = {
|
174 | resp: httpRespMessage,
|
175 | };
|
176 | if (httpRespMessage.statusCode < 200 || httpRespMessage.statusCode > 299) {
|
177 |
|
178 | parsedHttpRespMessage.err = new ApiError({
|
179 | errors: new Array(),
|
180 | code: httpRespMessage.statusCode,
|
181 | message: httpRespMessage.statusMessage,
|
182 | response: httpRespMessage,
|
183 | });
|
184 | }
|
185 | return parsedHttpRespMessage;
|
186 | }
|
187 | |
188 |
|
189 |
|
190 |
|
191 |
|
192 |
|
193 |
|
194 |
|
195 |
|
196 |
|
197 | parseHttpRespBody(body) {
|
198 | const parsedHttpRespBody = {
|
199 | body,
|
200 | };
|
201 | if (typeof body === 'string') {
|
202 | try {
|
203 | parsedHttpRespBody.body = JSON.parse(body);
|
204 | }
|
205 | catch (err) {
|
206 | parsedHttpRespBody.body = body;
|
207 | }
|
208 | }
|
209 | if (parsedHttpRespBody.body && parsedHttpRespBody.body.error) {
|
210 |
|
211 | parsedHttpRespBody.err = new ApiError(parsedHttpRespBody.body.error);
|
212 | }
|
213 | return parsedHttpRespBody;
|
214 | }
|
215 | |
216 |
|
217 |
|
218 |
|
219 |
|
220 |
|
221 |
|
222 |
|
223 |
|
224 |
|
225 |
|
226 |
|
227 |
|
228 |
|
229 | makeWritableStream(dup, options, onComplete) {
|
230 | onComplete = onComplete || util.noop;
|
231 | const writeStream = new ProgressStream();
|
232 | writeStream.on('progress', evt => dup.emit('progress', evt));
|
233 | dup.setWritable(writeStream);
|
234 | const defaultReqOpts = {
|
235 | method: 'POST',
|
236 | qs: {
|
237 | uploadType: 'multipart',
|
238 | },
|
239 | timeout: 0,
|
240 | maxRetries: 0,
|
241 | };
|
242 | const metadata = options.metadata || {};
|
243 | const reqOpts = extend(true, defaultReqOpts, options.request, {
|
244 | multipart: [
|
245 | {
|
246 | 'Content-Type': 'application/json',
|
247 | body: JSON.stringify(metadata),
|
248 | },
|
249 | {
|
250 | 'Content-Type': metadata.contentType || 'application/octet-stream',
|
251 | body: writeStream,
|
252 | },
|
253 | ],
|
254 | });
|
255 | options.makeAuthenticatedRequest(reqOpts, {
|
256 | onAuthenticated(err, authenticatedReqOpts) {
|
257 | if (err) {
|
258 | dup.destroy(err);
|
259 | return;
|
260 | }
|
261 | const request = teeny_request_1.teenyRequest.defaults(requestDefaults);
|
262 | request(authenticatedReqOpts, (err, resp, body) => {
|
263 | util.handleResp(err, resp, body, (err, data) => {
|
264 | if (err) {
|
265 | dup.destroy(err);
|
266 | return;
|
267 | }
|
268 | dup.emit('response', resp);
|
269 | onComplete(data);
|
270 | });
|
271 | });
|
272 | },
|
273 | });
|
274 | }
|
275 | |
276 |
|
277 |
|
278 |
|
279 |
|
280 |
|
281 |
|
282 |
|
283 | shouldRetryRequest(err) {
|
284 | if (err) {
|
285 | if ([408, 429, 500, 502, 503, 504].indexOf(err.code) !== -1) {
|
286 | return true;
|
287 | }
|
288 | if (err.errors) {
|
289 | for (const e of err.errors) {
|
290 | const reason = e.reason;
|
291 | if (reason === 'rateLimitExceeded') {
|
292 | return true;
|
293 | }
|
294 | if (reason === 'userRateLimitExceeded') {
|
295 | return true;
|
296 | }
|
297 | if (reason && reason.includes('EAI_AGAIN')) {
|
298 | return true;
|
299 | }
|
300 | }
|
301 | }
|
302 | }
|
303 | return false;
|
304 | }
|
305 | |
306 |
|
307 |
|
308 |
|
309 |
|
310 |
|
311 |
|
312 |
|
313 |
|
314 |
|
315 |
|
316 |
|
317 |
|
318 |
|
319 |
|
320 |
|
321 | makeAuthenticatedRequestFactory(config) {
|
322 | const googleAutoAuthConfig = extend({}, config);
|
323 | if (googleAutoAuthConfig.projectId === '{{projectId}}') {
|
324 | delete googleAutoAuthConfig.projectId;
|
325 | }
|
326 | let authClient;
|
327 | if (googleAutoAuthConfig.authClient instanceof google_auth_library_1.GoogleAuth) {
|
328 |
|
329 | authClient = googleAutoAuthConfig.authClient;
|
330 | }
|
331 | else {
|
332 |
|
333 | const config = {
|
334 | ...googleAutoAuthConfig,
|
335 | authClient: googleAutoAuthConfig.authClient,
|
336 | };
|
337 | authClient = new google_auth_library_1.GoogleAuth(config);
|
338 | }
|
339 | function makeAuthenticatedRequest(reqOpts, optionsOrCallback) {
|
340 | let stream;
|
341 | let projectId;
|
342 | const reqConfig = extend({}, config);
|
343 | let activeRequest_;
|
344 | if (!optionsOrCallback) {
|
345 | stream = duplexify();
|
346 | reqConfig.stream = stream;
|
347 | }
|
348 | const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : undefined;
|
349 | const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : undefined;
|
350 | const onAuthenticated = (err, authenticatedReqOpts) => {
|
351 | const authLibraryError = err;
|
352 | const autoAuthFailed = err &&
|
353 | err.message.indexOf('Could not load the default credentials') > -1;
|
354 | if (autoAuthFailed) {
|
355 |
|
356 |
|
357 | authenticatedReqOpts = reqOpts;
|
358 | }
|
359 | if (!err || autoAuthFailed) {
|
360 | try {
|
361 | authenticatedReqOpts = util.decorateRequest(authenticatedReqOpts, projectId);
|
362 | err = null;
|
363 | }
|
364 | catch (e) {
|
365 |
|
366 |
|
367 |
|
368 | err = err || e;
|
369 | }
|
370 | }
|
371 | if (err) {
|
372 | if (stream) {
|
373 | stream.destroy(err);
|
374 | }
|
375 | else {
|
376 | const fn = options && options.onAuthenticated
|
377 | ? options.onAuthenticated
|
378 | : callback;
|
379 | fn(err);
|
380 | }
|
381 | return;
|
382 | }
|
383 | if (options && options.onAuthenticated) {
|
384 | options.onAuthenticated(null, authenticatedReqOpts);
|
385 | }
|
386 | else {
|
387 | activeRequest_ = util.makeRequest(authenticatedReqOpts, reqConfig, (apiResponseError, ...params) => {
|
388 | if (apiResponseError &&
|
389 | apiResponseError.code === 401 &&
|
390 | authLibraryError) {
|
391 |
|
392 |
|
393 | apiResponseError = authLibraryError;
|
394 | }
|
395 | callback(apiResponseError, ...params);
|
396 | });
|
397 | }
|
398 | };
|
399 | Promise.all([
|
400 | config.projectId && config.projectId !== '{{projectId}}'
|
401 | ?
|
402 |
|
403 | new Promise(resolve => resolve(config.projectId))
|
404 | : authClient.getProjectId(),
|
405 | reqConfig.customEndpoint && reqConfig.useAuthWithCustomEndpoint !== true
|
406 | ?
|
407 |
|
408 | new Promise(resolve => resolve(reqOpts))
|
409 | : authClient.authorizeRequest(reqOpts),
|
410 | ])
|
411 | .then(([_projectId, authorizedReqOpts]) => {
|
412 | projectId = _projectId;
|
413 | onAuthenticated(null, authorizedReqOpts);
|
414 | })
|
415 | .catch(onAuthenticated);
|
416 | if (stream) {
|
417 | return stream;
|
418 | }
|
419 | return {
|
420 | abort() {
|
421 | setImmediate(() => {
|
422 | if (activeRequest_) {
|
423 | activeRequest_.abort();
|
424 | activeRequest_ = null;
|
425 | }
|
426 | });
|
427 | },
|
428 | };
|
429 | }
|
430 | const mar = makeAuthenticatedRequest;
|
431 | mar.getCredentials = authClient.getCredentials.bind(authClient);
|
432 | mar.authClient = authClient;
|
433 | return mar;
|
434 | }
|
435 | |
436 |
|
437 |
|
438 |
|
439 |
|
440 |
|
441 |
|
442 |
|
443 |
|
444 |
|
445 |
|
446 |
|
447 |
|
448 |
|
449 |
|
450 | makeRequest(reqOpts, config, callback) {
|
451 | var _a, _b, _c, _d, _e, _f, _g;
|
452 | let autoRetryValue = AUTO_RETRY_DEFAULT;
|
453 | if (config.autoRetry !== undefined &&
|
454 | ((_a = config.retryOptions) === null || _a === void 0 ? void 0 : _a.autoRetry) !== undefined) {
|
455 | throw new ApiError('autoRetry is deprecated. Use retryOptions.autoRetry instead.');
|
456 | }
|
457 | else if (config.autoRetry !== undefined) {
|
458 | autoRetryValue = config.autoRetry;
|
459 | }
|
460 | else if (((_b = config.retryOptions) === null || _b === void 0 ? void 0 : _b.autoRetry) !== undefined) {
|
461 | autoRetryValue = config.retryOptions.autoRetry;
|
462 | }
|
463 | let maxRetryValue = MAX_RETRY_DEFAULT;
|
464 | if (config.maxRetries && ((_c = config.retryOptions) === null || _c === void 0 ? void 0 : _c.maxRetries)) {
|
465 | throw new ApiError('maxRetries is deprecated. Use retryOptions.maxRetries instead.');
|
466 | }
|
467 | else if (config.maxRetries) {
|
468 | maxRetryValue = config.maxRetries;
|
469 | }
|
470 | else if ((_d = config.retryOptions) === null || _d === void 0 ? void 0 : _d.maxRetries) {
|
471 | maxRetryValue = config.retryOptions.maxRetries;
|
472 | }
|
473 | const options = {
|
474 | request: teeny_request_1.teenyRequest.defaults(requestDefaults),
|
475 | retries: autoRetryValue !== false ? maxRetryValue : 0,
|
476 | noResponseRetries: autoRetryValue !== false ? maxRetryValue : 0,
|
477 | shouldRetryFn(httpRespMessage) {
|
478 | var _a, _b;
|
479 | const err = util.parseHttpRespMessage(httpRespMessage).err;
|
480 | if ((_a = config.retryOptions) === null || _a === void 0 ? void 0 : _a.retryableErrorFn) {
|
481 | return err && ((_b = config.retryOptions) === null || _b === void 0 ? void 0 : _b.retryableErrorFn(err));
|
482 | }
|
483 | return err && util.shouldRetryRequest(err);
|
484 | },
|
485 | maxRetryDelay: (_e = config.retryOptions) === null || _e === void 0 ? void 0 : _e.maxRetryDelay,
|
486 | retryDelayMultiplier: (_f = config.retryOptions) === null || _f === void 0 ? void 0 : _f.retryDelayMultiplier,
|
487 | totalTimeout: (_g = config.retryOptions) === null || _g === void 0 ? void 0 : _g.totalTimeout,
|
488 | };
|
489 | if (typeof reqOpts.maxRetries === 'number') {
|
490 | options.retries = reqOpts.maxRetries;
|
491 | }
|
492 | if (!config.stream) {
|
493 | return retryRequest(reqOpts, options,
|
494 |
|
495 | (err, response, body) => {
|
496 | util.handleResp(err, response, body, callback);
|
497 | });
|
498 | }
|
499 | const dup = config.stream;
|
500 |
|
501 | let requestStream;
|
502 | const isGetRequest = (reqOpts.method || 'GET').toUpperCase() === 'GET';
|
503 | if (isGetRequest) {
|
504 | requestStream = retryRequest(reqOpts, options);
|
505 | dup.setReadable(requestStream);
|
506 | }
|
507 | else {
|
508 |
|
509 | requestStream = options.request(reqOpts);
|
510 | dup.setWritable(requestStream);
|
511 | }
|
512 |
|
513 | requestStream
|
514 | .on('error', dup.destroy.bind(dup))
|
515 | .on('response', dup.emit.bind(dup, 'response'))
|
516 | .on('complete', dup.emit.bind(dup, 'complete'));
|
517 | dup.abort = requestStream.abort;
|
518 | return dup;
|
519 | }
|
520 | |
521 |
|
522 |
|
523 |
|
524 |
|
525 |
|
526 |
|
527 | decorateRequest(reqOpts, projectId) {
|
528 | delete reqOpts.autoPaginate;
|
529 | delete reqOpts.autoPaginateVal;
|
530 | delete reqOpts.objectMode;
|
531 | if (reqOpts.qs !== null && typeof reqOpts.qs === 'object') {
|
532 | delete reqOpts.qs.autoPaginate;
|
533 | delete reqOpts.qs.autoPaginateVal;
|
534 | reqOpts.qs = projectify_1.replaceProjectIdToken(reqOpts.qs, projectId);
|
535 | }
|
536 | if (Array.isArray(reqOpts.multipart)) {
|
537 | reqOpts.multipart = reqOpts.multipart.map(part => {
|
538 | return projectify_1.replaceProjectIdToken(part, projectId);
|
539 | });
|
540 | }
|
541 | if (reqOpts.json !== null && typeof reqOpts.json === 'object') {
|
542 | delete reqOpts.json.autoPaginate;
|
543 | delete reqOpts.json.autoPaginateVal;
|
544 | reqOpts.json = projectify_1.replaceProjectIdToken(reqOpts.json, projectId);
|
545 | }
|
546 | reqOpts.uri = projectify_1.replaceProjectIdToken(reqOpts.uri, projectId);
|
547 | return reqOpts;
|
548 | }
|
549 |
|
550 | isCustomType(unknown, module) {
|
551 | function getConstructorName(obj) {
|
552 | return obj.constructor && obj.constructor.name.toLowerCase();
|
553 | }
|
554 | const moduleNameParts = module.split('/');
|
555 | const parentModuleName = moduleNameParts[0] && moduleNameParts[0].toLowerCase();
|
556 | const subModuleName = moduleNameParts[1] && moduleNameParts[1].toLowerCase();
|
557 | if (subModuleName && getConstructorName(unknown) !== subModuleName) {
|
558 | return false;
|
559 | }
|
560 | let walkingModule = unknown;
|
561 |
|
562 | while (true) {
|
563 | if (getConstructorName(walkingModule) === parentModuleName) {
|
564 | return true;
|
565 | }
|
566 | walkingModule = walkingModule.parent;
|
567 | if (!walkingModule) {
|
568 | return false;
|
569 | }
|
570 | }
|
571 | }
|
572 | |
573 |
|
574 |
|
575 |
|
576 |
|
577 |
|
578 | getUserAgentFromPackageJson(packageJson) {
|
579 | const hyphenatedPackageName = packageJson.name
|
580 | .replace('@google-cloud', 'gcloud-node')
|
581 | .replace('/', '-');
|
582 | return hyphenatedPackageName + '/' + packageJson.version;
|
583 | }
|
584 | |
585 |
|
586 |
|
587 |
|
588 |
|
589 |
|
590 |
|
591 | maybeOptionsOrCallback(optionsOrCallback, cb) {
|
592 | return typeof optionsOrCallback === 'function'
|
593 | ? [{}, optionsOrCallback]
|
594 | : [optionsOrCallback, cb];
|
595 | }
|
596 | }
|
597 | exports.Util = Util;
|
598 |
|
599 |
|
600 |
|
601 |
|
602 | class ProgressStream extends stream_1.Transform {
|
603 | constructor() {
|
604 | super(...arguments);
|
605 | this.bytesRead = 0;
|
606 | }
|
607 |
|
608 | _transform(chunk, encoding, callback) {
|
609 | this.bytesRead += chunk.length;
|
610 | this.emit('progress', { bytesWritten: this.bytesRead, contentLength: '*' });
|
611 | this.push(chunk);
|
612 | callback();
|
613 | }
|
614 | }
|
615 | const util = new Util();
|
616 | exports.util = util;
|
617 |
|
\ | No newline at end of file |