@google-cloud/bigtable
Version:
Cloud Bigtable Client Library for Node.js
758 lines • 29.8 kB
JavaScript
;
// Copyright 2016 Google LLC
//
// 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
//
// https://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.Table = exports.PartialFailureError = void 0;
const promisify_1 = require("@google-cloud/promisify");
const arrify = require("arrify");
const family_1 = require("./family");
const mutation_1 = require("./mutation");
const table_1 = require("./utils/table");
const is = require("is");
const tabular_api_surface_1 = require("./tabular-api-surface");
Object.defineProperty(exports, "PartialFailureError", { enumerable: true, get: function () { return tabular_api_surface_1.PartialFailureError; } });
/**
* Create a Table object to interact with a Cloud Bigtable table.
*
* @class
* @param {Instance} instance Instance Object.
* @param {string} id Unique identifier of the table.
*
* @example
* ```
* const {Bigtable} = require('@google-cloud/bigtable');
* const bigtable = new Bigtable();
* const instance = bigtable.instance('my-instance');
* const table = instance.table('prezzy');
* ```
*/
class Table extends tabular_api_surface_1.TabularApiSurface {
constructor(instance, id) {
super(instance, id);
}
/**
* Formats the decodes policy etag value to string.
*
* @private
*
* @param {object} policy
*/
static decodePolicyEtag(policy) {
policy.etag = policy.etag.toString('ascii');
return policy;
}
/**
* Formats the table name to include the Bigtable cluster.
*
* @private
*
* @param {string} instanceName The formatted instance name.
* @param {string} name The table name.
*
* @example
* ```
* Table.formatName_(
* 'projects/my-project/zones/my-zone/instances/my-instance',
* 'my-table'
* );
* //
* 'projects/my-project/zones/my-zone/instances/my-instance/tables/my-table'
* ```
*/
static formatName_(instanceName, id) {
if (id.includes('/')) {
return id;
}
return `${instanceName}/tables/${id}`;
}
/**
* Creates a range based off of a key prefix.
*
* @private
*
* @param {string} start The key prefix/starting bound.
* @returns {object} range
*
* @example
* ```
* const {Bigtable} = require('@google-cloud/bigtable');
* const bigtable = new Bigtable();
* const instance = bigtable.instance('my-instance');
* const table = instance.table('prezzy');
* table.createPrefixRange('start');
* // => {
* // start: 'start',
* // end: {
* // value: 'staru',
* // inclusive: false
* // }
* // }
* ```
*/
static createPrefixRange(start) {
return table_1.TableUtils.createPrefixRange(start);
}
/**
* Create a table.
*
* @param {object} [options] See {@link Instance#createTable}.
* @param {object} [options.gaxOptions] Request configuration options, outlined
* here: https://googleapis.github.io/gax-nodejs/global.html#CallOptions.
* @param {function} callback The callback function.
* @param {?error} callback.err An error returned while making this request.
* @param {Table} callback.table The newly created table.
* @param {object} callback.apiResponse The full API response.
*
* @example <caption>include:samples/api-reference-doc-snippets/table.js</caption>
* region_tag:bigtable_api_create_table
*/
create(optionsOrCallback, cb) {
const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : cb;
const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
this.instance.createTable(this.id, options, callback);
}
/**
* Backup a table with cluster auto selection.
*
* Backups of tables originate from a specific cluster. This is a helper
* around `Cluster.createBackup` that automatically selects the first ready
* cluster from which a backup can be performed.
*
* NOTE: This will make two API requests to first determine the most
* appropriate cluster, then create the backup. This could lead to a race
* condition if other requests are simultaneously sent or if the cluster
* availability state changes between each call.
*
* @param {string} id A unique ID for the backup.
* @param {CreateBackupConfig} config Metadata to set on the Backup.
* @param {BackupTimestamp} config.expireTime When the backup will be
* automatically deleted.
* @param {CallOptions} [config.gaxOptions] Request configuration options,
* outlined here:
* https://googleapis.github.io/gax-nodejs/CallSettings.html.
* @param {CreateBackupCallback} [callback] The callback function.
* @param {?error} callback.err An error returned while making this request.
* @param {Backup} callback.backup The newly created Backup.
* @param {Operation} callback.operation An operation object that can be used
* to check the status of the request.
* @param {object} callback.apiResponse The full API response.
* @return {void | Promise<CreateBackupResponse>}
*/
createBackup(id, config, callback) {
if (!id) {
throw new TypeError('An id is required to create a backup.');
}
if (!config) {
throw new TypeError('A configuration object is required.');
}
this.getReplicationStates(config.gaxOptions, (err, stateMap) => {
if (err) {
callback(err);
return;
}
const [clusterId] = [...stateMap.entries()].find(([, clusterState]) => {
return (clusterState.replicationState === 'READY' ||
clusterState.replicationState === 'READY_OPTIMIZING');
}) || [];
if (!clusterId) {
callback(new Error('No ready clusters eligible for backup.'));
return;
}
this.instance.cluster(clusterId).createBackup(id, {
table: this.name,
...config,
}, callback);
});
}
/**
* Create a column family.
*
* Optionally you can send garbage collection rules and when creating a
* family. Garbage collection executes opportunistically in the background, so
* it's possible for reads to return a cell even if it matches the active
* expression for its family.
*
* @see [Garbage Collection Proto Docs]{@link https://github.com/googleapis/googleapis/blob/3b236df084cf9222c529a2890f90e3a4ff0f2dfd/google/bigtable/admin/v2/table.proto#L184}
*
* @throws {error} If a name is not provided.
*
* @param {string} id The unique identifier of column family.
* @param {object} [options] Configuration object.
* @param {object} [options.gaxOptions] Request configuration options, outlined
* here: https://googleapis.github.io/gax-nodejs/global.html#CallOptions.
* @param {object} [options.rule] Garbage collection rule
* @param {object} [options.rule.age] Delete cells in a column older than the
* given age. Values must be at least 1 millisecond.
* @param {number} [options.rule.versions] Maximum number of versions to delete
* cells in a column, except for the most recent.
* @param {boolean} [options.rule.intersect] Cells to delete should match all
* rules.
* @param {boolean} [options.rule.union] Cells to delete should match any of the
* rules.
* @param {function} callback The callback function.
* @param {?error} callback.err An error returned while making this request.
* @param {Family} callback.family The newly created Family.
* @param {object} callback.apiResponse The full API response.
*
* @example <caption>include:samples/api-reference-doc-snippets/table.js</caption>
* region_tag:bigtable_api_create_family
*/
createFamily(id, optionsOrCallback, cb) {
const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : cb;
const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
if (!id) {
throw new Error('An id is required to create a family.');
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const mod = {
id,
create: {},
};
if (options.rule) {
mod.create.gcRule = family_1.Family.formatRule_(options.rule);
}
const reqOpts = {
name: this.name,
modifications: [mod],
};
this.bigtable.request({
client: 'BigtableTableAdminClient',
method: 'modifyColumnFamilies',
reqOpts,
gaxOpts: options.gaxOptions,
}, (err, resp) => {
if (err) {
callback(err, null, resp);
return;
}
const family = this.family(id);
family.metadata = resp;
callback(null, family, resp);
});
}
/**
* Delete the table.
*
* @param {object} [gaxOptions] Request configuration options, outlined
* here: https://googleapis.github.io/gax-nodejs/CallSettings.html.
* @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.
*
* @example <caption>include:samples/api-reference-doc-snippets/table.js</caption>
* region_tag:bigtable_api_del_table
*/
delete(optionsOrCallback, cb) {
const gaxOptions = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : cb;
this.bigtable.request({
client: 'BigtableTableAdminClient',
method: 'deleteTable',
reqOpts: {
name: this.name,
},
gaxOpts: gaxOptions,
}, callback);
}
/**
* Delete all rows in the table, optionally corresponding to a particular
* prefix.
*
* @throws {error} If a prefix is not provided.
*
* @param {string} prefix Row key prefix.
* @param {object} [gaxOptions] Request configuration options, outlined
* here: https://googleapis.github.io/gax-nodejs/CallSettings.html.
* @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.
*
* @example <caption>include:samples/api-reference-doc-snippets/table.js</caption>
* region_tag:bigtable_api_del_rows
*/
deleteRows(prefix, optionsOrCallback, cb) {
const gaxOptions = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : cb;
if (!prefix || is.fn(prefix)) {
throw new Error('A prefix is required for deleteRows.');
}
const reqOpts = {
name: this.name,
rowKeyPrefix: mutation_1.Mutation.convertToBytes(prefix),
};
this.bigtable.request({
client: 'BigtableTableAdminClient',
method: 'dropRowRange',
reqOpts,
gaxOpts: gaxOptions,
}, callback);
}
/**
* Check if a table exists.
*
* @param {object} [gaxOptions] Request configuration options, outlined
* here: https://googleapis.github.io/gax-nodejs/CallSettings.html.
* @param {function} callback The callback function.
* @param {?error} callback.err An error returned while making this
* request.
* @param {boolean} callback.exists Whether the table exists or not.
*
* @example <caption>include:samples/api-reference-doc-snippets/table.js</caption>
* region_tag:bigtable_api_exists_table
*/
exists(optionsOrCallback, cb) {
const gaxOptions = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : cb;
const reqOpts = {
view: 'name',
gaxOptions,
};
this.getMetadata(reqOpts, err => {
if (err) {
if (err.code === 5) {
callback(null, false);
return;
}
callback(err);
return;
}
callback(null, true);
});
}
/**
* Get a reference to a Table Family.
*
* @throws {error} If a name is not provided.
*
* @param {string} id The family unique identifier.
* @returns {Family}
*
* @example
* ```
* const family = table.family('my-family');
* ```
*/
family(id) {
if (!id) {
throw new Error('A family id must be provided.');
}
return new family_1.Family(this, id);
}
/**
* Get a table if it exists.
*
* You may optionally use this to "get or create" an object by providing an
* object with `autoCreate` set to `true`. Any extra configuration that is
* normally required for the `create` method must be contained within this
* object as well.
*
* @param {object} [options] Configuration object.
* @param {boolean} [options.autoCreate=false] Automatically create the
* instance if it does not already exist.
* @param {object} [options.gaxOptions] Request configuration options, outlined
* here: https://googleapis.github.io/gax-nodejs/CallSettings.html.
* @param {?error} callback.error An error returned while making this request.
* @param {Table} callback.table The Table object.
* @param {object} callback.apiResponse The resource as it exists in the API.
*
* @example <caption>include:samples/api-reference-doc-snippets/table.js</caption>
* region_tag:bigtable_api_get_table
*/
get(optionsOrCallback, cb) {
const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : cb;
const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
const autoCreate = !!options.autoCreate;
const gaxOptions = options.gaxOptions;
this.getMetadata({ gaxOptions }, (err, metadata) => {
if (err) {
if (err.code === 5 && autoCreate) {
this.create({ gaxOptions }, callback);
return;
}
callback(err);
return;
}
callback(null, this, metadata);
});
}
/**
* @param {object} [options] Configuration object.
* @param {object} [options.gaxOptions] Request configuration options, outlined
* here: https://googleapis.github.io/gax-nodejs/CallSettings.html.
* @param {number} [options.requestedPolicyVersion] The policy format version
* to be returned. Valid values are 0, 1, and 3. Requests specifying an
* invalid value will be rejected. Requests for policies with any
* conditional bindings must specify version 3. Policies without any
* conditional bindings may specify any valid value or leave the field unset.
* @param {function} [callback] The callback function.
* @param {?error} callback.error An error returned while making this request.
* @param {Policy} policy The policy.
*
* @example <caption>include:samples/api-reference-doc-snippets/instance.js</caption>
* region_tag:bigtable_api_get_table_Iam_policy
*/
getIamPolicy(optionsOrCallback, callback) {
const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
callback =
typeof optionsOrCallback === 'function' ? optionsOrCallback : callback;
const reqOpts = {
resource: this.name,
};
if (options.requestedPolicyVersion !== null &&
options.requestedPolicyVersion !== undefined) {
reqOpts.options = {
requestedPolicyVersion: options.requestedPolicyVersion,
};
}
this.bigtable.request({
client: 'BigtableTableAdminClient',
method: 'getIamPolicy',
reqOpts,
gaxOpts: options.gaxOptions,
}, (err, resp) => {
if (err) {
callback(err);
return;
}
callback(null, Table.decodePolicyEtag(resp));
});
}
/**
* Get Family objects for all the column families in your table.
*
* @param {object} [gaxOptions] Request configuration options, outlined here:
* https://googleapis.github.io/gax-nodejs/CallSettings.html.
* @param {function} callback The callback function.
* @param {?error} callback.err An error returned while making this request.
* @param {Family[]} callback.families The list of families.
* @param {object} callback.apiResponse The full API response.
*
* @example <caption>include:samples/api-reference-doc-snippets/table.js</caption>
* region_tag:bigtable_api_get_families
*/
getFamilies(optionsOrCallback, cb) {
const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : cb;
const gaxOptions = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
this.getMetadata({ gaxOptions }, (err, metadata) => {
if (err) {
callback(err);
return;
}
const families = Object.keys(metadata.columnFamilies).map(familyId => {
const family = this.family(familyId);
family.metadata = metadata.columnFamilies[familyId];
return family;
});
callback(null, families, metadata.columnFamilies);
});
}
/**
* Get replication states of the clusters for this table.
*
* @param {object} [gaxOptions] Request configuration options, outlined here:
* https://googleapis.github.io/gax-nodejs/CallSettings.html.
* @param {function} callback The callback function.
* @param {?error} callback.err An error returned while making this request.
* @param {Family[]} callback.clusterStates The map of clusterId and its replication state.
* @param {object} callback.apiResponse The full API response.
*
* @example
* ```
* const {Bigtable} = require('@google-cloud/bigtable');
* const bigtable = new Bigtable();
* const instance = bigtable.instance('my-instance');
* const table = instance.table('prezzy');
*
* table.getReplicationStates(function(err, clusterStates, apiResponse) {
* // `clusterStates` is an map of clusterId and its replication state.
* });
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* table.getReplicationStates().then(function(data) {
* const clusterStates = data[0];
* const apiResponse = data[1];
* });
* ```
*/
getReplicationStates(optionsOrCallback, cb) {
const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : cb;
const gaxOptions = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
const reqOpts = {
view: 'replication',
gaxOptions,
};
this.getMetadata(reqOpts, (err, metadata) => {
if (err) {
callback(err);
return;
}
const clusterStates = new Map();
Object.keys(metadata.clusterStates).map(clusterId => clusterStates.set(clusterId, metadata.clusterStates[clusterId]));
callback(null, clusterStates, metadata);
});
}
/**
* Get the table's metadata.
*
* @param {object} [options] Table request options.
* @param {object} [options.gaxOptions] Request configuration options, outlined
* here: https://googleapis.github.io/gax-nodejs/CallSettings.html.
* @param {string} [options.view] The view to be applied to the table fields.
* @param {function} [callback] The callback function.
* @param {?error} callback.err An error returned while making this
* request.
* @param {object} callback.metadata The table's metadata.
*
* @example <caption>include:samples/api-reference-doc-snippets/table.js</caption>
* region_tag:bigtable_api_get_table_meta
*/
getMetadata(optionsOrCallback, cb) {
const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : cb;
const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
const reqOpts = {
name: this.name,
view: Table.VIEWS[options.view || 'unspecified'],
};
this.bigtable.request({
client: 'BigtableTableAdminClient',
method: 'getTable',
reqOpts,
gaxOpts: options.gaxOptions,
}, (...args) => {
if (args[1]) {
this.metadata = args[1];
}
callback(...args);
});
}
/**
* @param {object} [gaxOptions] Request configuration options, outlined
* here: https://googleapis.github.io/gax-nodejs/CallSettings.html.
* @param {function} [callback] The callback function.
* @param {?error} callback.error An error returned while making this request.
* @param {Policy} policy The policy.
*
* @example <caption>include:samples/api-reference-doc-snippets/instance.js</caption>
* region_tag:bigtable_api_set_table_Iam_policy
*/
setIamPolicy(policy, gaxOptionsOrCallback, callback) {
const gaxOptions = typeof gaxOptionsOrCallback === 'object' ? gaxOptionsOrCallback : {};
callback =
typeof gaxOptionsOrCallback === 'function'
? gaxOptionsOrCallback
: callback;
if (policy.etag !== null && policy.etag !== undefined) {
policy.etag = Buffer.from(policy.etag);
}
const reqOpts = {
resource: this.name,
policy,
};
this.bigtable.request({
client: 'BigtableTableAdminClient',
method: 'setIamPolicy',
reqOpts,
gaxOpts: gaxOptions,
}, (err, resp) => {
if (err) {
callback(err);
}
callback(null, Table.decodePolicyEtag(resp));
});
}
/**
*
* @param {string | string[]} permissions The permission(s) to test for.
* @param {object} [gaxOptions] Request configuration options, outlined
* here: https://googleapis.github.io/gax-nodejs/CallSettings.html.
* @param {function} [callback] The callback function.
* @param {?error} callback.error An error returned while making this request.
* @param {string[]} permissions A subset of permissions that the caller is
* allowed.
*
* @example <caption>include:samples/api-reference-doc-snippets/instance.js</caption>
* region_tag:bigtable_api_test_table_Iam_permissions
*/
testIamPermissions(permissions, gaxOptionsOrCallback, callback) {
const gaxOptions = typeof gaxOptionsOrCallback === 'object' ? gaxOptionsOrCallback : {};
callback =
typeof gaxOptionsOrCallback === 'function'
? gaxOptionsOrCallback
: callback;
const reqOpts = {
resource: this.name,
permissions: arrify(permissions),
};
this.bigtable.request({
client: 'BigtableTableAdminClient',
method: 'testIamPermissions',
reqOpts,
gaxOpts: gaxOptions,
}, (err, resp) => {
if (err) {
callback(err);
return;
}
callback(null, resp.permissions);
});
}
/**
* Truncate the table.
*
* @param {object} [gaxOptions] Request configuration options, outlined
* here: https://googleapis.github.io/gax-nodejs/CallSettings.html.
* @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.
*
* @example
* ```
* table.truncate(function(err, apiResponse) {});
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* table.truncate().then(function(data) {
* const apiResponse = data[0];
* });
* ```
*/
truncate(optionsOrCallback, cb) {
const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : cb;
const gaxOptions = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
const reqOpts = {
name: this.name,
deleteAllDataFromTable: true,
};
this.bigtable.request({
client: 'BigtableTableAdminClient',
method: 'dropRowRange',
reqOpts,
gaxOpts: gaxOptions,
}, callback);
}
/**
* Generates Consistency-Token and check consistency for generated token
* In-case consistency check returns false, retrial is done in interval
* of 5 seconds till 10 minutes, after that it returns false.
*
* @param {function(?error, ?boolean)} callback The callback function.
* @param {?Error} callback.err An error returned while making this request.
* @param {?Boolean} callback.resp Boolean value.
*/
waitForReplication(callback) {
// handler for generated consistency-token
const tokenHandler = (err, token) => {
if (err) {
return callback(err);
}
// set timeout for 10 minutes
const timeoutAfterTenMinutes = setTimeout(() => {
callback(null, false);
}, 10 * 60 * 1000);
// method checks if retrial is required & init retrial with 5 sec
// delay
const retryIfNecessary = (err, res) => {
if (err) {
clearTimeout(timeoutAfterTenMinutes);
return callback(err);
}
if (res === true) {
clearTimeout(timeoutAfterTenMinutes);
return callback(null, true);
}
setTimeout(launchCheck, 5000);
};
// method to launch token consistency check
const launchCheck = () => {
this.checkConsistency(token, retryIfNecessary);
};
launchCheck();
};
// generate consistency-token
this.generateConsistencyToken(tokenHandler);
}
/**
* Generates Consistency-Token
* @param {function(?error, ?boolean)} callback The callback function.
* @param {?Error} callback.err An error returned while making this request.
* @param {?String} callback.token The generated consistency token.
*/
generateConsistencyToken(callback) {
const reqOpts = {
name: this.name,
};
this.bigtable.request({
client: 'BigtableTableAdminClient',
method: 'generateConsistencyToken',
reqOpts,
}, (err, res) => {
if (err) {
callback(err);
return;
}
callback(null, res.consistencyToken);
});
}
/**
* Checks consistency for given ConsistencyToken
* @param {string} token consistency token
* @param {function(?error, ?boolean)} callback The callback function.
* @param {?Error} callback.err An error returned while making this request.
* @param {?Boolean} callback.consistent Boolean value.
*/
checkConsistency(token, callback) {
const reqOpts = {
name: this.name,
consistencyToken: token,
};
this.bigtable.request({
client: 'BigtableTableAdminClient',
method: 'checkConsistency',
reqOpts,
}, (err, res) => {
if (err) {
callback(err);
return;
}
callback(null, res.consistent);
});
}
/**
* The view to be applied to the returned table's fields.
* Defaults to schema if unspecified.
*
* @private
*/
static VIEWS = {
unspecified: 0,
name: 1,
schema: 2,
replication: 3,
full: 4,
};
}
exports.Table = Table;
/*! Developer Documentation
*
* All async methods (except for streams) will return a Promise in the event
* that a callback is omitted.
*/
(0, promisify_1.promisifyAll)(Table, {
exclude: ['family', 'row'],
});
//# sourceMappingURL=table.js.map