UNPKG

15.6 kBJavaScriptView Raw
1"use strict";
2/*!
3 * Copyright 2014 Google Inc. All Rights Reserved.
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 */
17Object.defineProperty(exports, "__esModule", { value: true });
18exports.Job = void 0;
19/*!
20 * @module bigquery/job
21 */
22const common_1 = require("@google-cloud/common");
23const paginator_1 = require("@google-cloud/paginator");
24const promisify_1 = require("@google-cloud/promisify");
25const extend = require("extend");
26const bigquery_1 = require("./bigquery");
27/**
28 * @callback QueryResultsCallback
29 * @param {?Error} err An error returned while making this request.
30 * @param {array} rows The results of the job.
31 */
32/**
33 * @callback ManualQueryResultsCallback
34 * @param {?Error} err An error returned while making this request.
35 * @param {array} rows The results of the job.
36 * @param {?object} nextQuery A pre-made configuration object for your next
37 * request. This will be `null` if no additional results are available.
38 * If the query is not yet complete, you may get empty `rows` and
39 * non-`null` `nextQuery` that you should use for your next request.
40 * @param {object} apiResponse The full API response.
41 */
42/**
43 * Job objects are returned from various places in the BigQuery API:
44 *
45 * - {@link BigQuery#getJobs}
46 * - {@link BigQuery#job}
47 * - {@link BigQuery#query}
48 * - {@link BigQuery#createJob}
49 * - {@link Table#copy}
50 * - {@link Table#createWriteStream}
51 * - {@link Table#extract}
52 * - {@link Table#load}
53 *
54 * They can be used to check the status of a running job or fetching the results
55 * of a previously-executed one.
56 *
57 * @class
58 * @param {BigQuery} bigQuery {@link BigQuery} instance.
59 * @param {string} id The ID of the job.
60 * @param {object} [options] Configuration object.
61 * @param {string} [options.location] The geographic location of the job.
62 * Required except for US and EU.
63 *
64 * @example
65 * ```
66 * const {BigQuery} = require('@google-cloud/bigquery');
67 * const bigquery = new BigQuery();
68 *
69 * const job = bigquery.job('job-id');
70 *
71 * //-
72 * // All jobs are event emitters. The status of each job is polled
73 * // continuously, starting only after you register a "complete" listener.
74 * //-
75 * job.on('complete', (metadata) => {
76 * // The job is complete.
77 * });
78 *
79 * //-
80 * // Be sure to register an error handler as well to catch any issues which
81 * // impeded the job.
82 * //-
83 * job.on('error', (err) => {
84 * // An error occurred during the job.
85 * });
86 *
87 * //-
88 * // To force the Job object to stop polling for updates, simply remove any
89 * // "complete" listeners you've registered.
90 * //
91 * // The easiest way to do this is with `removeAllListeners()`.
92 * //-
93 * job.removeAllListeners();
94 * ```
95 */
96class Job extends common_1.Operation {
97 getQueryResultsStream(options) {
98 // placeholder body, overwritten in constructor
99 return new paginator_1.ResourceStream({}, () => { });
100 }
101 constructor(bigQuery, id, options) {
102 let location;
103 const methods = {
104 /**
105 * @callback DeleteJobCallback
106 * @param {?Error} err Request error, if any.
107 * @param {object} apiResponse The full API response.
108 */
109 /**
110 * @typedef {array} DeleteJobResponse
111 * @property {object} 0 The full API response.
112 */
113 /**
114 * Delete the job.
115 *
116 * @see [Jobs: delete API Documentation]{@link https://cloud.google.com/bigquery/docs/reference/rest/v2/jobs/delete}
117 *
118 * @method Job#delete
119 * @param {DeleteJobCallback} [callback] The callback function.
120 * @param {?error} callback.err An error returned while making this
121 * request.
122 * @param {object} callback.apiResponse The full API response.
123 * @returns {Promise<DeleteJobResponse>}
124 *
125 * @example
126 * const {BigQuery} = require('@google-cloud/bigquery');
127 * const bigquery = new BigQuery();
128 *
129 * const job = bigquery.job(jobId);
130 * job.delete((err, apiResponse) => {
131 * if (!err) {
132 * // The job was deleted successfully.
133 * }
134 * });
135 *
136 * @example If the callback is omitted a Promise will be returned
137 * const [apiResponse] = await job.delete();
138 */
139 delete: {
140 reqOpts: {
141 method: 'DELETE',
142 uri: '/delete',
143 qs: {
144 get location() {
145 return location;
146 },
147 },
148 },
149 },
150 /**
151 * @callback JobExistsCallback
152 * @param {?Error} err Request error, if any.
153 * @param {boolean} exists Indicates if the job exists.
154 */
155 /**
156 * @typedef {array} JobExistsResponse
157 * @property {boolean} 0 Indicates if the job exists.
158 */
159 /**
160 * Check if the job exists.
161 *
162 * @method Job#exists
163 * @param {JobExistsCallback} [callback] The callback function.
164 * @param {?error} callback.err An error returned while making this
165 * request.
166 * @param {boolean} callback.exists Whether the job exists or not.
167 * @returns {Promise<JobExistsResponse>}
168 *
169 * @example
170 * ```
171 * const {BigQuery} = require('@google-cloud/bigquery');
172 * const bigquery = new BigQuery();
173 *
174 * const job = bigquery.job('job-id');
175 *
176 * job.exists((err, exists) => {});
177 *
178 * //-
179 * // If the callback is omitted, we'll return a Promise.
180 * //-
181 * job.exists().then((data) => {
182 * const exists = data[0];
183 * });
184 * ```
185 */
186 exists: true,
187 /**
188 * @callback GetJobCallback
189 * @param {?Error} err Request error, if any.
190 * @param {Model} model The job.
191 * @param {object} apiResponse The full API response body.
192 */
193 /**
194 * @typedef {array} GetJobResponse
195 * @property {Model} 0 The job.
196 * @property {object} 1 The full API response body.
197 */
198 /**
199 * Get a job if it exists.
200 *
201 * @method Job#get
202 * @param {object} [options] Configuration object.
203 * @param {string} [options.location] The geographic location of the job.
204 * Required except for US and EU.
205 * @param {GetJobCallback} [callback] The callback function.
206 * @param {?error} callback.err An error returned while making this
207 * request.
208 * @param {Job} callback.job The job.
209 * @returns {Promise<GetJobResponse>}
210 *
211 * @example
212 * ```
213 * const {BigQuery} = require('@google-cloud/bigquery');
214 * const bigquery = new BigQuery();
215 *
216 * const job = bigquery.job('job-id');
217 *
218 * job.get((err, job, apiResponse) => {
219 * if (!err) {
220 * // `job.metadata` has been populated.
221 * }
222 * });
223 *
224 * //-
225 * // If the callback is omitted, we'll return a Promise.
226 * //-
227 * job.get().then((data) => {
228 * const job = data[0];
229 * const apiResponse = data[1];
230 * });
231 * ```
232 */
233 get: true,
234 /**
235 * @callback GetJobMetadataCallback
236 * @param {?Error} err Request error, if any.
237 * @param {object} metadata The job metadata.
238 * @param {object} apiResponse The full API response.
239 */
240 /**
241 * @typedef {array} GetJobMetadataResponse
242 * @property {object} 0 The job metadata.
243 * @property {object} 1 The full API response.
244 */
245 /**
246 * Get the metadata of the job. This will mostly be useful for checking
247 * the status of a previously-run job.
248 *
249 * See {@link https://cloud.google.com/bigquery/docs/reference/v2/jobs/get| Jobs: get API Documentation}
250 *
251 * @method Job#getMetadata
252 * @param {GetJobMetadataCallback} [callback] The callback function.
253 * @param {?error} callback.err An error returned while making this
254 * request.
255 * @param {object} callback.metadata The metadata of the job.
256 * @param {object} callback.apiResponse The full API response.
257 * @returns {Promise<GetJobMetadataResponse>}
258 *
259 * @example
260 * ```
261 * const {BigQuery} = require('@google-cloud/bigquery');
262 * const bigquery = new BigQuery();
263 *
264 * const job = bigquery.job('id');
265 * job.getMetadata((err, metadata, apiResponse) => {});
266 *
267 * //-
268 * // If the callback is omitted, we'll return a Promise.
269 * //-
270 * job.getMetadata().then((data) => {
271 * const metadata = data[0];
272 * const apiResponse = data[1];
273 * });
274 * ```
275 */
276 getMetadata: {
277 reqOpts: {
278 qs: {
279 get location() {
280 return location;
281 },
282 },
283 },
284 },
285 };
286 super({
287 parent: bigQuery,
288 baseUrl: '/jobs',
289 id,
290 methods,
291 });
292 Object.defineProperty(this, 'location', {
293 get() {
294 return location;
295 },
296 set(_location) {
297 location = _location;
298 },
299 });
300 this.bigQuery = bigQuery;
301 if (options && options.location) {
302 this.location = options.location;
303 }
304 if (options === null || options === void 0 ? void 0 : options.projectId) {
305 this.projectId = options.projectId;
306 }
307 /**
308 * Get the results of a job as a readable object stream.
309 *
310 * @param {object} options Configuration object. See
311 * {@link Job#getQueryResults} for a complete list of options.
312 * @return {stream}
313 *
314 * @example
315 * ```
316 * const through2 = require('through2');
317 * const fs = require('fs');
318 * const {BigQuery} = require('@google-cloud/bigquery');
319 * const bigquery = new BigQuery();
320 *
321 * const job = bigquery.job('job-id');
322 *
323 * job.getQueryResultsStream()
324 * .pipe(through2.obj(function (row, enc, next) {
325 * this.push(JSON.stringify(row) + '\n');
326 * next();
327 * }))
328 * .pipe(fs.createWriteStream('./test/testdata/testfile.json'));
329 * ```
330 */
331 this.getQueryResultsStream = paginator_1.paginator.streamify('getQueryResultsAsStream_');
332 }
333 cancel(callback) {
334 let qs;
335 if (this.location) {
336 qs = { location: this.location };
337 }
338 this.request({
339 method: 'POST',
340 uri: '/cancel',
341 qs,
342 }, callback);
343 }
344 getQueryResults(optionsOrCallback, cb) {
345 const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
346 const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : cb;
347 const qs = extend({
348 location: this.location,
349 }, options);
350 const wrapIntegers = qs.wrapIntegers ? qs.wrapIntegers : false;
351 delete qs.wrapIntegers;
352 delete qs.job;
353 const timeoutOverride = typeof qs.timeoutMs === 'number' ? qs.timeoutMs : false;
354 this.bigQuery.request({
355 uri: '/queries/' + this.id,
356 qs,
357 }, (err, resp) => {
358 if (err) {
359 callback(err, null, null, resp);
360 return;
361 }
362 // eslint-disable-next-line @typescript-eslint/no-explicit-any
363 let rows = [];
364 if (resp.schema && resp.rows) {
365 rows = bigquery_1.BigQuery.mergeSchemaWithRows_(resp.schema, resp.rows, wrapIntegers);
366 }
367 let nextQuery = null;
368 if (resp.jobComplete === false) {
369 // Query is still running.
370 nextQuery = Object.assign({}, options);
371 // If timeout override was provided, return error.
372 if (timeoutOverride) {
373 const err = new Error(`The query did not complete before ${timeoutOverride}ms`);
374 callback(err, null, nextQuery, resp);
375 return;
376 }
377 }
378 else if (resp.pageToken) {
379 // More results exist.
380 nextQuery = Object.assign({}, options, {
381 pageToken: resp.pageToken,
382 });
383 delete nextQuery.startIndex;
384 }
385 callback(null, rows, nextQuery, resp);
386 });
387 }
388 /**
389 * This method will be called by `getQueryResultsStream()`. It is required to
390 * properly set the `autoPaginate` option value.
391 *
392 * @private
393 */
394 getQueryResultsAsStream_(options, callback) {
395 options = extend({ autoPaginate: false }, options);
396 this.getQueryResults(options, callback);
397 }
398 /**
399 * Poll for a status update. Execute the callback:
400 *
401 * - callback(err): Job failed
402 * - callback(): Job incomplete
403 * - callback(null, metadata): Job complete
404 *
405 * @private
406 *
407 * @param {function} callback
408 */
409 poll_(callback) {
410 this.getMetadata((err, metadata) => {
411 if (!err && metadata.status && metadata.status.errorResult) {
412 err = new common_1.util.ApiError(metadata.status);
413 }
414 if (err) {
415 callback(err);
416 return;
417 }
418 if (metadata.status.state !== 'DONE') {
419 callback(null);
420 return;
421 }
422 callback(null, metadata);
423 });
424 }
425}
426exports.Job = Job;
427/*! Developer Documentation
428 *
429 * These methods can be auto-paginated.
430 */
431paginator_1.paginator.extend(Job, ['getQueryResults']);
432/*! Developer Documentation
433 *
434 * All async methods (except for streams) will return a Promise in the event
435 * that a callback is omitted.
436 */
437(0, promisify_1.promisifyAll)(Job);
438//# sourceMappingURL=job.js.map
\No newline at end of file