UNPKG

15.5 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 constructor(bigQuery, id, options) {
98 let location;
99 const methods = {
100 /**
101 * @callback DeleteJobCallback
102 * @param {?Error} err Request error, if any.
103 * @param {object} apiResponse The full API response.
104 */
105 /**
106 * @typedef {array} DeleteJobResponse
107 * @property {object} 0 The full API response.
108 */
109 /**
110 * Delete the job.
111 *
112 * @see [Jobs: delete API Documentation]{@link https://cloud.google.com/bigquery/docs/reference/rest/v2/jobs/delete}
113 *
114 * @method Job#delete
115 * @param {DeleteJobCallback} [callback] The callback function.
116 * @param {?error} callback.err An error returned while making this
117 * request.
118 * @param {object} callback.apiResponse The full API response.
119 * @returns {Promise<DeleteJobResponse>}
120 *
121 * @example
122 * const {BigQuery} = require('@google-cloud/bigquery');
123 * const bigquery = new BigQuery();
124 *
125 * const job = bigquery.job(jobId);
126 * job.delete((err, apiResponse) => {
127 * if (!err) {
128 * // The job was deleted successfully.
129 * }
130 * });
131 *
132 * @example If the callback is omitted a Promise will be returned
133 * const [apiResponse] = await job.delete();
134 */
135 delete: {
136 reqOpts: {
137 method: 'DELETE',
138 uri: '/delete',
139 qs: {
140 get location() {
141 return location;
142 },
143 },
144 },
145 },
146 /**
147 * @callback JobExistsCallback
148 * @param {?Error} err Request error, if any.
149 * @param {boolean} exists Indicates if the job exists.
150 */
151 /**
152 * @typedef {array} JobExistsResponse
153 * @property {boolean} 0 Indicates if the job exists.
154 */
155 /**
156 * Check if the job exists.
157 *
158 * @method Job#exists
159 * @param {JobExistsCallback} [callback] The callback function.
160 * @param {?error} callback.err An error returned while making this
161 * request.
162 * @param {boolean} callback.exists Whether the job exists or not.
163 * @returns {Promise<JobExistsResponse>}
164 *
165 * @example
166 * ```
167 * const {BigQuery} = require('@google-cloud/bigquery');
168 * const bigquery = new BigQuery();
169 *
170 * const job = bigquery.job('job-id');
171 *
172 * job.exists((err, exists) => {});
173 *
174 * //-
175 * // If the callback is omitted, we'll return a Promise.
176 * //-
177 * job.exists().then((data) => {
178 * const exists = data[0];
179 * });
180 * ```
181 */
182 exists: true,
183 /**
184 * @callback GetJobCallback
185 * @param {?Error} err Request error, if any.
186 * @param {Model} model The job.
187 * @param {object} apiResponse The full API response body.
188 */
189 /**
190 * @typedef {array} GetJobResponse
191 * @property {Model} 0 The job.
192 * @property {object} 1 The full API response body.
193 */
194 /**
195 * Get a job if it exists.
196 *
197 * @method Job#get
198 * @param {object} [options] Configuration object.
199 * @param {string} [options.location] The geographic location of the job.
200 * Required except for US and EU.
201 * @param {GetJobCallback} [callback] The callback function.
202 * @param {?error} callback.err An error returned while making this
203 * request.
204 * @param {Job} callback.job The job.
205 * @returns {Promise<GetJobResponse>}
206 *
207 * @example
208 * ```
209 * const {BigQuery} = require('@google-cloud/bigquery');
210 * const bigquery = new BigQuery();
211 *
212 * const job = bigquery.job('job-id');
213 *
214 * job.get((err, job, apiResponse) => {
215 * if (!err) {
216 * // `job.metadata` has been populated.
217 * }
218 * });
219 *
220 * //-
221 * // If the callback is omitted, we'll return a Promise.
222 * //-
223 * job.get().then((data) => {
224 * const job = data[0];
225 * const apiResponse = data[1];
226 * });
227 * ```
228 */
229 get: true,
230 /**
231 * @callback GetJobMetadataCallback
232 * @param {?Error} err Request error, if any.
233 * @param {object} metadata The job metadata.
234 * @param {object} apiResponse The full API response.
235 */
236 /**
237 * @typedef {array} GetJobMetadataResponse
238 * @property {object} 0 The job metadata.
239 * @property {object} 1 The full API response.
240 */
241 /**
242 * Get the metadata of the job. This will mostly be useful for checking
243 * the status of a previously-run job.
244 *
245 * See {@link https://cloud.google.com/bigquery/docs/reference/v2/jobs/get| Jobs: get API Documentation}
246 *
247 * @method Job#getMetadata
248 * @param {GetJobMetadataCallback} [callback] The callback function.
249 * @param {?error} callback.err An error returned while making this
250 * request.
251 * @param {object} callback.metadata The metadata of the job.
252 * @param {object} callback.apiResponse The full API response.
253 * @returns {Promise<GetJobMetadataResponse>}
254 *
255 * @example
256 * ```
257 * const {BigQuery} = require('@google-cloud/bigquery');
258 * const bigquery = new BigQuery();
259 *
260 * const job = bigquery.job('id');
261 * job.getMetadata((err, metadata, apiResponse) => {});
262 *
263 * //-
264 * // If the callback is omitted, we'll return a Promise.
265 * //-
266 * job.getMetadata().then((data) => {
267 * const metadata = data[0];
268 * const apiResponse = data[1];
269 * });
270 * ```
271 */
272 getMetadata: {
273 reqOpts: {
274 qs: {
275 get location() {
276 return location;
277 },
278 },
279 },
280 },
281 };
282 super({
283 parent: bigQuery,
284 baseUrl: '/jobs',
285 id,
286 methods,
287 });
288 Object.defineProperty(this, 'location', {
289 get() {
290 return location;
291 },
292 set(_location) {
293 location = _location;
294 },
295 });
296 this.bigQuery = bigQuery;
297 if (options && options.location) {
298 this.location = options.location;
299 }
300 if (options === null || options === void 0 ? void 0 : options.projectId) {
301 this.projectId = options.projectId;
302 }
303 /**
304 * Get the results of a job as a readable object stream.
305 *
306 * @param {object} options Configuration object. See
307 * {@link Job#getQueryResults} for a complete list of options.
308 * @return {stream}
309 *
310 * @example
311 * ```
312 * const through2 = require('through2');
313 * const fs = require('fs');
314 * const {BigQuery} = require('@google-cloud/bigquery');
315 * const bigquery = new BigQuery();
316 *
317 * const job = bigquery.job('job-id');
318 *
319 * job.getQueryResultsStream()
320 * .pipe(through2.obj(function (row, enc, next) {
321 * this.push(JSON.stringify(row) + '\n');
322 * next();
323 * }))
324 * .pipe(fs.createWriteStream('./test/testdata/testfile.json'));
325 * ```
326 */
327 this.getQueryResultsStream = paginator_1.paginator.streamify('getQueryResultsAsStream_');
328 }
329 getQueryResultsStream(options) {
330 // placeholder body, overwritten in constructor
331 return new paginator_1.ResourceStream({}, () => { });
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 }
384 callback(null, rows, nextQuery, resp);
385 });
386 }
387 /**
388 * This method will be called by `getQueryResultsStream()`. It is required to
389 * properly set the `autoPaginate` option value.
390 *
391 * @private
392 */
393 getQueryResultsAsStream_(options, callback) {
394 options = extend({ autoPaginate: false }, options);
395 this.getQueryResults(options, callback);
396 }
397 /**
398 * Poll for a status update. Execute the callback:
399 *
400 * - callback(err): Job failed
401 * - callback(): Job incomplete
402 * - callback(null, metadata): Job complete
403 *
404 * @private
405 *
406 * @param {function} callback
407 */
408 poll_(callback) {
409 this.getMetadata((err, metadata) => {
410 if (!err && metadata.status && metadata.status.errorResult) {
411 err = new common_1.util.ApiError(metadata.status);
412 }
413 if (err) {
414 callback(err);
415 return;
416 }
417 if (metadata.status.state !== 'DONE') {
418 callback(null);
419 return;
420 }
421 callback(null, metadata);
422 });
423 }
424}
425exports.Job = Job;
426/*! Developer Documentation
427 *
428 * These methods can be auto-paginated.
429 */
430paginator_1.paginator.extend(Job, ['getQueryResults']);
431/*! Developer Documentation
432 *
433 * All async methods (except for streams) will return a Promise in the event
434 * that a callback is omitted.
435 */
436promisify_1.promisifyAll(Job);
437//# sourceMappingURL=job.js.map
\No newline at end of file