"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 });
/*!
* @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");
/**
* @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 {
constructor(bigQuery, id, options) {
let location;
if (options && options.location) {
location = options.location;
}
const methods = {
/**
* Check if the job exists.
*
* @method Job#exists
* @param {function} [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}
*
* @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,
/**
* Get a job if it exists.
*
* @method Job#get
* @param {function} [callback] The callback function.
* @param {?error} callback.err An error returned while making this
* request.
* @param {Job} callback.job The job.
* @returns {Promise}
*
* @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,
/**
* Get the metadata of the job. This will mostly be useful for checking
* the status of a previously-run job.
*
* @see [Jobs: get API Documentation]{@link https://cloud.google.com/bigquery/docs/reference/v2/jobs/get}
*
* @method Job#getMetadata
* @param {function} [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}
*
* @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: { location },
},
},
};
super({
parent: bigQuery,
baseUrl: '/jobs',
id,
methods,
});
this.bigQuery = bigQuery;
if (options && options.location) {
this.location = options.location;
}
/**
* 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_');
}
/**
* Cancel a job. Use {@link Job#getMetadata} to see if the cancel
* completes successfully. See an example implementation below.
*
* @see [Jobs: get API Documentation]{@link https://cloud.google.com/bigquery/docs/reference/v2/jobs/cancel}
*
* @param {function} [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}
*
* @example
* const {BigQuery} = require('@google-cloud/bigquery');
* const bigquery = new BigQuery();
*
* const job = bigquery.job('job-id');
*
* job.cancel((err, apiResponse) =>{
* // Check to see if the job completes successfully.
* job.on('error', (err) => {});
* job.on('complete', (metadata) => {});
* });
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* job.cancel().then((data) => {
* const apiResponse = data[0];
* });
*/
cancel(callback) {
let qs;
if (this.location) {
qs = { location: this.location };
}
this.request({
method: 'POST',
uri: '/cancel',
qs,
}, callback);
}
/**
* Get the results of a job.
*
* @see [Jobs: getQueryResults API Documentation]{@link https://cloud.google.com/bigquery/docs/reference/v2/jobs/getQueryResults}
*
* @param {object} [options] Configuration object.
* @param {boolean} [options.autoPaginate=true] Have pagination handled
* automatically.
* @param {number} [options.maxApiCalls] Maximum number of API calls to make.
* @param {number} [options.maxResults] Maximum number of results to read.
* @param {string} [options.pageToken] Page token, returned by a previous call,
* to request the next page of results. Note: This is automatically added
* to the `nextQuery` argument of your callback.
* @param {number} [options.startIndex] Zero-based index of the starting row.
* @param {number} [options.timeoutMs] How long to wait for the query to
* complete, in milliseconds, before returning. Default is to return
* immediately. If the timeout passes before the job completes, the
* request will fail with a `TIMEOUT` error.
* @param {QueryResultsCallback|ManualQueryResultsCallback} [callback] The
* callback function. If `autoPaginate` is set to false a
* {@link ManualQueryResultsCallback} should be used.
* @returns {Promise}
*
* @example
* const {BigQuery} = require('@google-cloud/bigquery');
* const bigquery = new BigQuery();
*
* const job = bigquery.job('job-id');
*
* //-
* // Get all of the results of a query.
* //-
* job.getQueryResults((err, rows) => {
* if (!err) {
* // rows is an array of results.
* }
* });
*
* //-
* // Customize the results you want to fetch.
* //-
* job.getQueryResults({
* maxResults: 100
* }, (err, rows) => {});
*
* //-
* // To control how many API requests are made and page through the results
* // manually, set `autoPaginate` to `false`.
* //-
* function manualPaginationCallback(err, rows, nextQuery, apiResponse) {
* if (nextQuery) {
* // More results exist.
* job.getQueryResults(nextQuery, manualPaginationCallback);
* }
* }
*
* job.getQueryResults({
* autoPaginate: false
* }, manualPaginationCallback);
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* job.getQueryResults().then((data) => {
* const rows = data[0];
* });
*/
getQueryResults(optionsOrCallback, cb) {
const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : cb;
const qs = extend({
location: this.location,
}, options);
delete qs.job;
this.bigQuery.request({
uri: '/queries/' + this.id,
qs,
}, (err, resp) => {
if (err) {
callback(err, null, null, resp);
return;
}
// tslint:disable-next-line no-any
let rows = [];
if (resp.schema && resp.rows) {
rows = bigquery_1.BigQuery.mergeSchemaWithRows_(resp.schema, resp.rows);
}
let nextQuery = null;
if (resp.jobComplete === false) {
// Query is still running.
nextQuery = extend({}, options);
}
else if (resp.pageToken) {
// More results exist.
nextQuery = extend({}, options, {
pageToken: resp.pageToken,
});
}
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.
*/
promisify_1.promisifyAll(Job);
//# sourceMappingURL=job.js.map