1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 | Object.defineProperty(exports, "__esModule", { value: true });
|
15 | exports.createAPIRequest = void 0;
|
16 | const google_auth_library_1 = require("google-auth-library");
|
17 | const qs = require("qs");
|
18 | const stream = require("stream");
|
19 | const urlTemplate = require("url-template");
|
20 | const uuid = require("uuid");
|
21 | const extend = require("extend");
|
22 | const isbrowser_1 = require("./isbrowser");
|
23 | const h2 = require("./http2");
|
24 |
|
25 | const pkg = require('../../package.json');
|
26 |
|
27 | function isReadableStream(obj) {
|
28 | return (obj !== null &&
|
29 | typeof obj === 'object' &&
|
30 | typeof obj.pipe === 'function' &&
|
31 | obj.readable !== false &&
|
32 | typeof obj._read === 'function' &&
|
33 | typeof obj._readableState === 'object');
|
34 | }
|
35 | function getMissingParams(params, required) {
|
36 | const missing = new Array();
|
37 | required.forEach(param => {
|
38 |
|
39 | if (params[param] === undefined) {
|
40 | missing.push(param);
|
41 | }
|
42 | });
|
43 |
|
44 |
|
45 | return missing.length > 0 ? missing : null;
|
46 | }
|
47 | function createAPIRequest(parameters, callback) {
|
48 | if (callback) {
|
49 | createAPIRequestAsync(parameters).then(r => callback(null, r), callback);
|
50 | }
|
51 | else {
|
52 | return createAPIRequestAsync(parameters);
|
53 | }
|
54 | }
|
55 | exports.createAPIRequest = createAPIRequest;
|
56 | async function createAPIRequestAsync(parameters) {
|
57 | var _a, _b, _c, _d;
|
58 |
|
59 |
|
60 |
|
61 | const options = extend(true, {},
|
62 | ((_a = parameters.context.google) === null || _a === void 0 ? void 0 : _a._options) || {},
|
63 | parameters.context._options || {},
|
64 | parameters.options
|
65 | );
|
66 | const params = extend(true, {},
|
67 | options.params,
|
68 | parameters.params
|
69 | );
|
70 | options.userAgentDirectives = options.userAgentDirectives || [];
|
71 | const media = params.media || {};
|
72 | |
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 | let resource = params.requestBody;
|
87 | if (!params.requestBody &&
|
88 | params.resource &&
|
89 | (!parameters.requiredParams.includes('resource') ||
|
90 | typeof params.resource !== 'string')) {
|
91 | resource = params.resource;
|
92 | delete params.resource;
|
93 | }
|
94 | delete params.requestBody;
|
95 | let authClient = params.auth || options.auth;
|
96 | const defaultMime = typeof media.body === 'string' ? 'text/plain' : 'application/octet-stream';
|
97 | delete params.media;
|
98 | delete params.auth;
|
99 |
|
100 | const headers = params.headers || {};
|
101 | populateAPIHeader(headers);
|
102 | delete params.headers;
|
103 |
|
104 | Object.keys(params).forEach(key => {
|
105 | if (key.slice(-1) === '_') {
|
106 | const newKey = key.slice(0, -1);
|
107 | params[newKey] = params[key];
|
108 | delete params[key];
|
109 | }
|
110 | });
|
111 |
|
112 | const missingParams = getMissingParams(params, parameters.requiredParams);
|
113 | if (missingParams) {
|
114 |
|
115 |
|
116 | throw new Error('Missing required parameters: ' + missingParams.join(', '));
|
117 | }
|
118 |
|
119 | if (options.url) {
|
120 | let url = options.url;
|
121 | if (typeof url === 'object') {
|
122 | url = url.toString();
|
123 | }
|
124 | options.url = urlTemplate.parse(url).expand(params);
|
125 | }
|
126 | if (parameters.mediaUrl) {
|
127 | parameters.mediaUrl = urlTemplate.parse(parameters.mediaUrl).expand(params);
|
128 | }
|
129 |
|
130 | if (parameters.context._options.rootUrl !== undefined &&
|
131 | options.url !== undefined) {
|
132 | const originalUrl = new URL(options.url);
|
133 | const path = originalUrl.href.substr(originalUrl.origin.length);
|
134 | options.url = new URL(path, parameters.context._options.rootUrl).href;
|
135 | }
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 | options.paramsSerializer = params => {
|
142 | return qs.stringify(params, { arrayFormat: 'repeat' });
|
143 | };
|
144 |
|
145 | parameters.pathParams.forEach(param => delete params[param]);
|
146 |
|
147 | if (typeof authClient === 'string') {
|
148 | params.key = params.key || authClient;
|
149 | authClient = undefined;
|
150 | }
|
151 | function multipartUpload(multipart) {
|
152 | const boundary = uuid.v4();
|
153 | const finale = `--${boundary}--`;
|
154 | const rStream = new stream.PassThrough({
|
155 | flush(callback) {
|
156 | this.push('\r\n');
|
157 | this.push(finale);
|
158 | callback();
|
159 | },
|
160 | });
|
161 | const pStream = new ProgressStream();
|
162 | const isStream = isReadableStream(multipart[1].body);
|
163 | headers['content-type'] = `multipart/related; boundary=${boundary}`;
|
164 | for (const part of multipart) {
|
165 | const preamble = `--${boundary}\r\ncontent-type: ${part['content-type']}\r\n\r\n`;
|
166 | rStream.push(preamble);
|
167 | if (typeof part.body === 'string') {
|
168 | rStream.push(part.body);
|
169 | rStream.push('\r\n');
|
170 | }
|
171 | else {
|
172 |
|
173 |
|
174 |
|
175 | pStream.on('progress', bytesRead => {
|
176 | if (options.onUploadProgress) {
|
177 | options.onUploadProgress({ bytesRead });
|
178 | }
|
179 | });
|
180 | part.body.pipe(pStream).pipe(rStream);
|
181 | }
|
182 | }
|
183 | if (!isStream) {
|
184 | rStream.push(finale);
|
185 | rStream.push(null);
|
186 | }
|
187 | options.data = rStream;
|
188 | }
|
189 | function browserMultipartUpload(multipart) {
|
190 | const boundary = uuid.v4();
|
191 | const finale = `--${boundary}--`;
|
192 | headers['content-type'] = `multipart/related; boundary=${boundary}`;
|
193 | let content = '';
|
194 | for (const part of multipart) {
|
195 | const preamble = `--${boundary}\r\ncontent-type: ${part['content-type']}\r\n\r\n`;
|
196 | content += preamble;
|
197 | if (typeof part.body === 'string') {
|
198 | content += part.body;
|
199 | content += '\r\n';
|
200 | }
|
201 | }
|
202 | content += finale;
|
203 | options.data = content;
|
204 | }
|
205 | if (parameters.mediaUrl && media.body) {
|
206 | options.url = parameters.mediaUrl;
|
207 | if (resource) {
|
208 | params.uploadType = 'multipart';
|
209 | const multipart = [
|
210 | { 'content-type': 'application/json', body: JSON.stringify(resource) },
|
211 | {
|
212 | 'content-type': media.mimeType || (resource && resource.mimeType) || defaultMime,
|
213 | body: media.body,
|
214 | },
|
215 | ];
|
216 | if (!(0, isbrowser_1.isBrowser)()) {
|
217 |
|
218 |
|
219 | multipartUpload(multipart);
|
220 | }
|
221 | else {
|
222 | browserMultipartUpload(multipart);
|
223 | }
|
224 | }
|
225 | else {
|
226 | params.uploadType = 'media';
|
227 | Object.assign(headers, { 'content-type': media.mimeType || defaultMime });
|
228 | options.data = media.body;
|
229 | }
|
230 | }
|
231 | else {
|
232 | options.data = resource || undefined;
|
233 | }
|
234 | options.headers = extend(true, options.headers || {}, headers);
|
235 | options.params = params;
|
236 | if (!(0, isbrowser_1.isBrowser)()) {
|
237 | options.headers['Accept-Encoding'] = 'gzip';
|
238 | options.userAgentDirectives.push({
|
239 | product: 'google-api-nodejs-client',
|
240 | version: pkg.version,
|
241 | comment: 'gzip',
|
242 | });
|
243 | const userAgent = options.userAgentDirectives
|
244 | .map(d => {
|
245 | let line = `${d.product}/${d.version}`;
|
246 | if (d.comment) {
|
247 | line += ` (${d.comment})`;
|
248 | }
|
249 | return line;
|
250 | })
|
251 | .join(' ');
|
252 | options.headers['User-Agent'] = userAgent;
|
253 | }
|
254 |
|
255 |
|
256 |
|
257 | if (!options.validateStatus) {
|
258 | options.validateStatus = status => {
|
259 | return (status >= 200 && status < 300) || status === 304;
|
260 | };
|
261 | }
|
262 |
|
263 | options.retry = options.retry === undefined ? true : options.retry;
|
264 | delete options.auth;
|
265 |
|
266 | if (options.universeDomain &&
|
267 | options.universe_domain &&
|
268 | options.universeDomain !== options.universe_domain) {
|
269 | throw new Error('Please set either universe_domain or universeDomain, but not both.');
|
270 | }
|
271 | const universeDomainEnvVar = typeof process === 'object' && typeof process.env === 'object'
|
272 | ? process.env['GOOGLE_CLOUD_UNIVERSE_DOMAIN']
|
273 | : undefined;
|
274 | const universeDomain = (_d = (_c = (_b = options.universeDomain) !== null && _b !== void 0 ? _b : options.universe_domain) !== null && _c !== void 0 ? _c : universeDomainEnvVar) !== null && _d !== void 0 ? _d : 'googleapis.com';
|
275 |
|
276 | if (universeDomain !== 'googleapis.com' && options.url) {
|
277 | const url = new URL(options.url);
|
278 | if (url.hostname.endsWith('.googleapis.com')) {
|
279 | url.hostname = url.hostname.replace(/googleapis\.com$/, universeDomain);
|
280 | options.url = url.toString();
|
281 | }
|
282 | }
|
283 |
|
284 |
|
285 |
|
286 |
|
287 | if (authClient && typeof authClient === 'object') {
|
288 |
|
289 | const universeFromAuth = typeof authClient.getUniverseDomain === 'function'
|
290 | ? await authClient.getUniverseDomain()
|
291 | : undefined;
|
292 | if (universeFromAuth && universeDomain !== universeFromAuth) {
|
293 | throw new Error(`The configured universe domain (${universeDomain}) does not match the universe domain found in the credentials (${universeFromAuth}). ` +
|
294 | "If you haven't configured the universe domain explicitly, googleapis.com is the default.");
|
295 | }
|
296 | if (options.http2) {
|
297 | const authHeaders = await authClient.getRequestHeaders(options.url);
|
298 | const mooOpts = Object.assign({}, options);
|
299 | mooOpts.headers = Object.assign(mooOpts.headers, authHeaders);
|
300 | return h2.request(mooOpts);
|
301 | }
|
302 | else {
|
303 | return authClient.request(options);
|
304 | }
|
305 | }
|
306 | else {
|
307 | return new google_auth_library_1.DefaultTransporter().request(options);
|
308 | }
|
309 | }
|
310 |
|
311 |
|
312 |
|
313 |
|
314 | class ProgressStream extends stream.Transform {
|
315 | constructor() {
|
316 | super(...arguments);
|
317 | this.bytesRead = 0;
|
318 | }
|
319 |
|
320 | _transform(chunk, encoding, callback) {
|
321 | this.bytesRead += chunk.length;
|
322 | this.emit('progress', this.bytesRead);
|
323 | this.push(chunk);
|
324 | callback();
|
325 | }
|
326 | }
|
327 | function populateAPIHeader(headers) {
|
328 |
|
329 |
|
330 |
|
331 | if (!(0, isbrowser_1.isBrowser)()) {
|
332 | headers['x-goog-api-client'] = `gdcl/${pkg.version} gl-node/${process.versions.node}`;
|
333 | }
|
334 | }
|
335 |
|
\ | No newline at end of file |