UNPKG

@google-cloud/bigquery

Version:

Google BigQuery Client Library for Node.js

462 lines 16.6 kB
"use strict"; /*! * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Job = void 0; /*! * @module bigquery/job */ const common_1 = require("@google-cloud/common"); const paginator_1 = require("@google-cloud/paginator"); const promisify_1 = require("@google-cloud/promisify"); const extend = require("extend"); const bigquery_1 = require("./bigquery"); const logger_1 = require("./logger"); /** * @callback QueryResultsCallback * @param {?Error} err An error returned while making this request. * @param {array} rows The results of the job. */ /** * @callback ManualQueryResultsCallback * @param {?Error} err An error returned while making this request. * @param {array} rows The results of the job. * @param {?object} nextQuery A pre-made configuration object for your next * request. This will be `null` if no additional results are available. * If the query is not yet complete, you may get empty `rows` and * non-`null` `nextQuery` that you should use for your next request. * @param {object} apiResponse The full API response. */ /** * Job objects are returned from various places in the BigQuery API: * * - {@link BigQuery#getJobs} * - {@link BigQuery#job} * - {@link BigQuery#query} * - {@link BigQuery#createJob} * - {@link Table#copy} * - {@link Table#createWriteStream} * - {@link Table#extract} * - {@link Table#load} * * They can be used to check the status of a running job or fetching the results * of a previously-executed one. * * @class * @param {BigQuery} bigQuery {@link BigQuery} instance. * @param {string} id The ID of the job. * @param {object} [options] Configuration object. * @param {string} [options.location] The geographic location of the job. * Required except for US and EU. * * @example * ``` * const {BigQuery} = require('@google-cloud/bigquery'); * const bigquery = new BigQuery(); * * const job = bigquery.job('job-id'); * * //- * // All jobs are event emitters. The status of each job is polled * // continuously, starting only after you register a "complete" listener. * //- * job.on('complete', (metadata) => { * // The job is complete. * }); * * //- * // Be sure to register an error handler as well to catch any issues which * // impeded the job. * //- * job.on('error', (err) => { * // An error occurred during the job. * }); * * //- * // To force the Job object to stop polling for updates, simply remove any * // "complete" listeners you've registered. * // * // The easiest way to do this is with `removeAllListeners()`. * //- * job.removeAllListeners(); * ``` */ class Job extends common_1.Operation { getQueryResultsStream(options) { // placeholder body, overwritten in constructor return new paginator_1.ResourceStream({}, () => { }); } constructor(bigQuery, id, options) { let location; const methods = { /** * @callback DeleteJobCallback * @param {?Error} err Request error, if any. * @param {object} apiResponse The full API response. */ /** * @typedef {array} DeleteJobResponse * @property {object} 0 The full API response. */ /** * Delete the job. * * @see [Jobs: delete API Documentation]{@link https://cloud.google.com/bigquery/docs/reference/rest/v2/jobs/delete} * * @method Job#delete * @param {DeleteJobCallback} [callback] The callback function. * @param {?error} callback.err An error returned while making this * request. * @param {object} callback.apiResponse The full API response. * @returns {Promise<DeleteJobResponse>} * * @example * const {BigQuery} = require('@google-cloud/bigquery'); * const bigquery = new BigQuery(); * * const job = bigquery.job(jobId); * job.delete((err, apiResponse) => { * if (!err) { * // The job was deleted successfully. * } * }); * * @example If the callback is omitted a Promise will be returned * const [apiResponse] = await job.delete(); */ delete: { reqOpts: { method: 'DELETE', uri: '/delete', qs: { get location() { return location; }, }, }, }, /** * @callback JobExistsCallback * @param {?Error} err Request error, if any. * @param {boolean} exists Indicates if the job exists. */ /** * @typedef {array} JobExistsResponse * @property {boolean} 0 Indicates if the job exists. */ /** * Check if the job exists. * * @method Job#exists * @param {JobExistsCallback} [callback] The callback function. * @param {?error} callback.err An error returned while making this * request. * @param {boolean} callback.exists Whether the job exists or not. * @returns {Promise<JobExistsResponse>} * * @example * ``` * const {BigQuery} = require('@google-cloud/bigquery'); * const bigquery = new BigQuery(); * * const job = bigquery.job('job-id'); * * job.exists((err, exists) => {}); * * //- * // If the callback is omitted, we'll return a Promise. * //- * job.exists().then((data) => { * const exists = data[0]; * }); * ``` */ exists: true, /** * @callback GetJobCallback * @param {?Error} err Request error, if any. * @param {Model} model The job. * @param {object} apiResponse The full API response body. */ /** * @typedef {array} GetJobResponse * @property {Model} 0 The job. * @property {object} 1 The full API response body. */ /** * Get a job if it exists. * * @method Job#get * @param {object} [options] Configuration object. * @param {string} [options.location] The geographic location of the job. * Required except for US and EU. * @param {GetJobCallback} [callback] The callback function. * @param {?error} callback.err An error returned while making this * request. * @param {Job} callback.job The job. * @returns {Promise<GetJobResponse>} * * @example * ``` * const {BigQuery} = require('@google-cloud/bigquery'); * const bigquery = new BigQuery(); * * const job = bigquery.job('job-id'); * * job.get((err, job, apiResponse) => { * if (!err) { * // `job.metadata` has been populated. * } * }); * * //- * // If the callback is omitted, we'll return a Promise. * //- * job.get().then((data) => { * const job = data[0]; * const apiResponse = data[1]; * }); * ``` */ get: true, /** * @callback GetJobMetadataCallback * @param {?Error} err Request error, if any. * @param {object} metadata The job metadata. * @param {object} apiResponse The full API response. */ /** * @typedef {array} GetJobMetadataResponse * @property {object} 0 The job metadata. * @property {object} 1 The full API response. */ /** * Get the metadata of the job. This will mostly be useful for checking * the status of a previously-run job. * * See {@link https://cloud.google.com/bigquery/docs/reference/v2/jobs/get| Jobs: get API Documentation} * * @method Job#getMetadata * @param {GetJobMetadataCallback} [callback] The callback function. * @param {?error} callback.err An error returned while making this * request. * @param {object} callback.metadata The metadata of the job. * @param {object} callback.apiResponse The full API response. * @returns {Promise<GetJobMetadataResponse>} * * @example * ``` * const {BigQuery} = require('@google-cloud/bigquery'); * const bigquery = new BigQuery(); * * const job = bigquery.job('id'); * job.getMetadata((err, metadata, apiResponse) => {}); * * //- * // If the callback is omitted, we'll return a Promise. * //- * job.getMetadata().then((data) => { * const metadata = data[0]; * const apiResponse = data[1]; * }); * ``` */ getMetadata: { reqOpts: { qs: { get location() { return location; }, }, }, }, }; super({ parent: bigQuery, baseUrl: '/jobs', id, methods, }); Object.defineProperty(this, 'location', { get() { return location; }, set(_location) { location = _location; }, }); this.bigQuery = bigQuery; if (options && options.location) { this.location = options.location; } if (options === null || options === void 0 ? void 0 : options.projectId) { this.projectId = options.projectId; } /** * Get the results of a job as a readable object stream. * * @param {object} options Configuration object. See * {@link Job#getQueryResults} for a complete list of options. * @return {stream} * * @example * ``` * const through2 = require('through2'); * const fs = require('fs'); * const {BigQuery} = require('@google-cloud/bigquery'); * const bigquery = new BigQuery(); * * const job = bigquery.job('job-id'); * * job.getQueryResultsStream() * .pipe(through2.obj(function (row, enc, next) { * this.push(JSON.stringify(row) + '\n'); * next(); * })) * .pipe(fs.createWriteStream('./test/testdata/testfile.json')); * ``` */ this.getQueryResultsStream = paginator_1.paginator.streamify('getQueryResultsAsStream_'); } // eslint-disable-next-line @typescript-eslint/no-explicit-any trace_(msg, ...otherArgs) { (0, logger_1.logger)(`[job][${this.id}]`, msg, ...otherArgs); } cancel(callback) { let qs; if (this.location) { qs = { location: this.location }; } this.request({ method: 'POST', uri: '/cancel', qs, }, callback); } getQueryResults(optionsOrCallback, cb) { const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {}; const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : cb; const qs = extend({ location: this.location, 'formatOptions.useInt64Timestamp': true, }, options); this.trace_('[getQueryResults]', this.id, options.pageToken, options.startIndex); const wrapIntegers = qs.wrapIntegers ? qs.wrapIntegers : false; delete qs.wrapIntegers; const parseJSON = qs.parseJSON ? qs.parseJSON : false; delete qs.parseJSON; delete qs.job; const timeoutOverride = typeof qs.timeoutMs === 'number' ? qs.timeoutMs : false; if (options._cachedRows) { let nextQuery = null; if (options.pageToken) { nextQuery = Object.assign({}, options, { pageToken: options.pageToken, }); delete nextQuery._cachedRows; } callback(null, options._cachedRows, nextQuery); return; } this.bigQuery.request({ uri: '/queries/' + this.id, qs, }, (err, resp) => { if (err) { callback(err, null, null, resp); return; } // eslint-disable-next-line @typescript-eslint/no-explicit-any let rows = []; if (resp.schema && resp.rows) { rows = bigquery_1.BigQuery.mergeSchemaWithRows_(resp.schema, resp.rows, { wrapIntegers, parseJSON, }); } let nextQuery = null; if (resp.jobComplete === false) { // Query is still running. nextQuery = Object.assign({}, options); // If timeout override was provided, return error. if (timeoutOverride) { const err = new Error(`The query did not complete before ${timeoutOverride}ms`); callback(err, null, nextQuery, resp); return; } } else if (resp.pageToken) { this.trace_('[getQueryResults] has more pages', resp.pageToken); // More results exist. nextQuery = Object.assign({}, options, { pageToken: resp.pageToken, }); delete nextQuery.startIndex; } callback(null, rows, nextQuery, resp); }); } /** * This method will be called by `getQueryResultsStream()`. It is required to * properly set the `autoPaginate` option value. * * @private */ getQueryResultsAsStream_(options, callback) { options = extend({ autoPaginate: false }, options); this.getQueryResults(options, callback); } /** * Poll for a status update. Execute the callback: * * - callback(err): Job failed * - callback(): Job incomplete * - callback(null, metadata): Job complete * * @private * * @param {function} callback */ poll_(callback) { this.getMetadata((err, metadata) => { if (!err && metadata.status && metadata.status.errorResult) { err = new common_1.util.ApiError(metadata.status); } if (err) { callback(err); return; } if (metadata.status.state !== 'DONE') { callback(null); return; } callback(null, metadata); }); } } exports.Job = Job; /*! Developer Documentation * * These methods can be auto-paginated. */ paginator_1.paginator.extend(Job, ['getQueryResults']); /*! Developer Documentation * * All async methods (except for streams) will return a Promise in the event * that a callback is omitted. */ (0, promisify_1.promisifyAll)(Job); //# sourceMappingURL=job.js.map