UNPKG

@google-cloud/spanner

Version:
1,142 lines 45.7 kB
"use strict"; /*! * Copyright 2016 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.Interval = exports.SpannerDate = exports.PGNumeric = exports.Numeric = exports.Struct = exports.Int = exports.Float = exports.Float32 = exports.protos = exports.v1 = exports.MutationSet = exports.MutationGroup = exports.Transaction = exports.Snapshot = exports.PartitionedDml = exports.Table = exports.SessionPool = exports.Session = exports.Backup = exports.Database = exports.InstanceConfig = exports.Instance = exports.Spanner = void 0; const service_1 = require("./common-grpc/service"); const precise_date_1 = require("@google-cloud/precise-date"); const projectify_1 = require("@google-cloud/projectify"); const promisify_1 = require("@google-cloud/promisify"); const extend = require("extend"); const google_auth_library_1 = require("google-auth-library"); const path = require("path"); const streamEvents = require("stream-events"); const through = require("through2"); const codec_1 = require("./codec"); Object.defineProperty(exports, "Float32", { enumerable: true, get: function () { return codec_1.Float32; } }); Object.defineProperty(exports, "Float", { enumerable: true, get: function () { return codec_1.Float; } }); Object.defineProperty(exports, "Int", { enumerable: true, get: function () { return codec_1.Int; } }); Object.defineProperty(exports, "Numeric", { enumerable: true, get: function () { return codec_1.Numeric; } }); Object.defineProperty(exports, "PGNumeric", { enumerable: true, get: function () { return codec_1.PGNumeric; } }); Object.defineProperty(exports, "SpannerDate", { enumerable: true, get: function () { return codec_1.SpannerDate; } }); Object.defineProperty(exports, "Interval", { enumerable: true, get: function () { return codec_1.Interval; } }); Object.defineProperty(exports, "Struct", { enumerable: true, get: function () { return codec_1.Struct; } }); const api_1 = require("@opentelemetry/api"); const backup_1 = require("./backup"); Object.defineProperty(exports, "Backup", { enumerable: true, get: function () { return backup_1.Backup; } }); const database_1 = require("./database"); Object.defineProperty(exports, "Database", { enumerable: true, get: function () { return database_1.Database; } }); const instance_1 = require("./instance"); Object.defineProperty(exports, "Instance", { enumerable: true, get: function () { return instance_1.Instance; } }); const instance_config_1 = require("./instance-config"); Object.defineProperty(exports, "InstanceConfig", { enumerable: true, get: function () { return instance_config_1.InstanceConfig; } }); const google_gax_1 = require("google-gax"); const protos_1 = require("../protos/protos"); var IsolationLevel = protos_1.google.spanner.v1.TransactionOptions.IsolationLevel; const common_1 = require("./common"); const session_1 = require("./session"); Object.defineProperty(exports, "Session", { enumerable: true, get: function () { return session_1.Session; } }); const session_pool_1 = require("./session-pool"); Object.defineProperty(exports, "SessionPool", { enumerable: true, get: function () { return session_pool_1.SessionPool; } }); const table_1 = require("./table"); Object.defineProperty(exports, "Table", { enumerable: true, get: function () { return table_1.Table; } }); const transaction_1 = require("./transaction"); Object.defineProperty(exports, "MutationGroup", { enumerable: true, get: function () { return transaction_1.MutationGroup; } }); Object.defineProperty(exports, "MutationSet", { enumerable: true, get: function () { return transaction_1.MutationSet; } }); Object.defineProperty(exports, "PartitionedDml", { enumerable: true, get: function () { return transaction_1.PartitionedDml; } }); Object.defineProperty(exports, "Snapshot", { enumerable: true, get: function () { return transaction_1.Snapshot; } }); Object.defineProperty(exports, "Transaction", { enumerable: true, get: function () { return transaction_1.Transaction; } }); const grpcGcpModule = require("grpc-gcp"); const grpcGcp = grpcGcpModule(google_gax_1.grpc); const v1 = require("./v1"); exports.v1 = v1; const instrument_1 = require("./instrument"); const request_id_header_1 = require("./request_id_header"); // eslint-disable-next-line @typescript-eslint/no-var-requires const gcpApiConfig = require('./spanner_grpc_config.json'); /** * [Cloud Spanner](https://cloud.google.com/spanner) is a highly scalable, * transactional, managed, NewSQL database service. Cloud Spanner solves the * need for a horizontally-scaling database with consistent global transaction * and SQL semantics. With Cloud Spanner you don't need to choose between * consistency and horizontal scaling — you get both. * * @class * * @see [Cloud Spanner Documentation](https://cloud.google.com/spanner/docs) * @see [Cloud Spanner Concepts](https://cloud.google.com/spanner/docs/concepts) * * @example Install the client library with <a * href="https://www.npmjs.com/">npm</a>: * ``` * npm install --save @google-cloud/spanner * ``` * * @example Create a client that uses <a * href="https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application">Application * Default Credentials (ADC)</a>: * ``` * const client = new Spanner(); * ``` * * @example Create a client with <a * href="https://cloud.google.com/docs/authentication/production#obtaining_and_providing_service_account_credentials_manually">explicit * credentials</a>: * ``` * const client = new Spanner({ projectId: * 'your-project-id', keyFilename: '/path/to/keyfile.json' * }); * ``` * * @example <caption>include:samples/quickstart.js</caption> * region_tag:spanner_quickstart * Full quickstart example: * * @param {ClientConfig} [options] Configuration options. */ class Spanner extends service_1.GrpcService { options; auth; clients_; instances_; instanceConfigs_; projectIdReplaced_; projectFormattedName_; commonHeaders_; routeToLeaderEnabled = true; directedReadOptions; defaultTransactionOptions; _observabilityOptions; _nthClientId; /** * Placeholder used to auto populate a column with the commit timestamp. * This can only be used for timestamp columns that have set the option * "(allow_commit_timestamp=true)" in the schema. * * @type {string} */ static COMMIT_TIMESTAMP = 'spanner.commit_timestamp()'; static POSTGRESQL = protos_1.google.spanner.admin.database.v1.DatabaseDialect.POSTGRESQL; static GOOGLE_STANDARD_SQL = protos_1.google.spanner.admin.database.v1.DatabaseDialect.GOOGLE_STANDARD_SQL; /** * Gets the configured Spanner emulator host from an environment variable. */ static getSpannerEmulatorHost() { const endpointWithPort = process.env.SPANNER_EMULATOR_HOST; if (endpointWithPort) { if (endpointWithPort.startsWith('http:') || endpointWithPort.startsWith('https:')) { throw new google_gax_1.GoogleError('SPANNER_EMULATOR_HOST must not start with a protocol specification (http/https)'); } const index = endpointWithPort.indexOf(':'); if (index > -1) { const portName = endpointWithPort.substring(index + 1); const port = +portName; if (!port || port < 1 || port > 65535) { throw new google_gax_1.GoogleError(`Invalid port number: ${portName}`); } return { endpoint: endpointWithPort.substring(0, index), port: +endpointWithPort.substring(index + 1), }; } return { endpoint: endpointWithPort }; } return undefined; } constructor(options) { const scopes = []; const clientClasses = [ v1.DatabaseAdminClient, v1.InstanceAdminClient, v1.SpannerClient, ]; for (const clientClass of clientClasses) { for (const scope of clientClass.scopes) { if (scopes.indexOf(scope) === -1) { scopes.push(scope); } } } options = Object.assign({ libName: 'gccl', libVersion: require('../../package.json').version, scopes, // Add grpc keep alive setting 'grpc.keepalive_time_ms': 30000, 'grpc.keepalive_timeout_ms': 10000, // Enable grpc-gcp support 'grpc.callInvocationTransformer': grpcGcp.gcpCallInvocationTransformer, 'grpc.channelFactoryOverride': grpcGcp.gcpChannelFactoryOverride, 'grpc.gcpApiConfig': grpcGcp.createGcpApiConfig(gcpApiConfig), grpc: google_gax_1.grpc, }, options || {}); const directedReadOptions = options.directedReadOptions ? options.directedReadOptions : null; delete options.directedReadOptions; const defaultTransactionOptions = options.defaultTransactionOptions ? options.defaultTransactionOptions : { isolationLevel: IsolationLevel.ISOLATION_LEVEL_UNSPECIFIED, }; delete options.defaultTransactionOptions; const emulatorHost = Spanner.getSpannerEmulatorHost(); if (emulatorHost && emulatorHost.endpoint && emulatorHost.endpoint.length > 0) { options.servicePath = emulatorHost.endpoint; options.port = emulatorHost.port; options.sslCreds = google_gax_1.grpc.credentials.createInsecure(); } const config = { baseUrl: options.apiEndpoint || options.servicePath || // TODO: for TPC, this needs to support universeDomain 'spanner.googleapis.com', protosDir: path.resolve(__dirname, '../protos'), protoServices: { Operations: { path: 'google/longrunning/operations.proto', service: 'longrunning', }, }, scopes: ['https://www.googleapis.com/auth/cloud-platform'], packageJson: require('../../package.json'), }; super(config, options); if (options.routeToLeaderEnabled === false) { this.routeToLeaderEnabled = false; } this.options = options; this.auth = new google_auth_library_1.GoogleAuth(this.options); this.clients_ = new Map(); this.instances_ = new Map(); this.instanceConfigs_ = new Map(); this.projectIdReplaced_ = false; this.projectFormattedName_ = 'projects/' + this.projectId; this.directedReadOptions = directedReadOptions; this.defaultTransactionOptions = defaultTransactionOptions; this._observabilityOptions = options.observabilityOptions; this.commonHeaders_ = (0, common_1.getCommonHeaders)(this.projectFormattedName_, this._observabilityOptions?.enableEndToEndTracing); (0, instrument_1.ensureInitialContextManagerSet)(); this._nthClientId = (0, request_id_header_1.nextSpannerClientId)(); } /** * Gets the InstanceAdminClient object. * The returned InstanceAdminClient object is a shared, managed instance and should not be manually closed. * @returns {v1.InstanceAdminClient} The InstanceAdminClient object * @example * ``` * const {Spanner} = require('@google-cloud/spanner'); * const spanner = new Spanner({ * projectId: projectId, * }); * const instanceAdminClient = spanner.getInstanceAdminClient(); * ``` */ getInstanceAdminClient() { const clientName = 'InstanceAdminClient'; if (!this.clients_.has(clientName)) { this.clients_.set(clientName, new v1[clientName](this.options)); } return this.clients_.get(clientName); } /** * Gets the DatabaseAdminClient object. * The returned DatabaseAdminClient object is a managed, shared instance and should not be manually closed. * @returns {v1.DatabaseAdminClient} The DatabaseAdminClient object. * @example * ``` * const {Spanner} = require('@google-cloud/spanner'); * const spanner = new Spanner({ * projectId: projectId, * }); * const databaseAdminClient = spanner.getDatabaseAdminClient(); * ``` */ getDatabaseAdminClient() { const clientName = 'DatabaseAdminClient'; if (!this.clients_.has(clientName)) { this.clients_.set(clientName, new v1[clientName](this.options)); } return this.clients_.get(clientName); } /** Closes this Spanner client and cleans up all resources used by it. */ close() { this.clients_.forEach(c => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const client = c; if (client.operationsClient && client.operationsClient.close) { client.operationsClient.close(); } client.close(); }); } createInstance(name, config, callback) { if (!name) { throw new google_gax_1.GoogleError('A name is required to create an instance.'); } if (!config) { throw new google_gax_1.GoogleError(['A configuration object is required to create an instance.'].join('')); } const formattedName = instance_1.Instance.formatName_(this.projectId, name); const displayName = config.displayName || formattedName.split('/').pop(); const reqOpts = { parent: this.projectFormattedName_, instanceId: formattedName.split('/').pop(), instance: extend({ name: formattedName, displayName, nodeCount: config.nodes, processingUnits: config.processingUnits, }, config), }; if (reqOpts.instance.nodeCount && reqOpts.instance.processingUnits) { throw new google_gax_1.GoogleError(['Only one of nodeCount or processingUnits can be specified.'].join('')); } if (!reqOpts.instance.nodeCount && !reqOpts.instance.processingUnits) { // If neither nodes nor processingUnits are specified, default to a // nodeCount of 1. reqOpts.instance.nodeCount = 1; } delete reqOpts.instance.nodes; delete reqOpts.instance.gaxOptions; if (config.config.indexOf('/') === -1) { reqOpts.instance.config = `projects/${this.projectId}/instanceConfigs/${config.config}`; } this.request({ client: 'InstanceAdminClient', method: 'createInstance', reqOpts, gaxOpts: config.gaxOptions, headers: this.commonHeaders_, }, (err, operation, resp) => { if (err) { callback(err, null, null, resp); return; } const instance = this.instance(formattedName); instance._observabilityOptions = this._observabilityOptions; callback(null, instance, operation, resp); }); } getInstances(optionsOrCallback, cb) { // eslint-disable-next-line @typescript-eslint/no-this-alias const self = this; const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {}; const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : cb; const gaxOpts = extend(true, {}, options.gaxOptions); let reqOpts = extend({}, options, { parent: 'projects/' + this.projectId, }); delete reqOpts.gaxOptions; // Copy over pageSize and pageToken values from gaxOptions. // However values set on options take precedence. if (gaxOpts) { reqOpts = extend({}, { pageSize: gaxOpts.pageSize, pageToken: gaxOpts.pageToken, }, reqOpts); delete gaxOpts.pageToken; delete gaxOpts.pageSize; } this.request({ client: 'InstanceAdminClient', method: 'listInstances', reqOpts, gaxOpts, headers: this.commonHeaders_, }, (err, instances, nextPageRequest, ...args) => { let instanceInstances = null; if (instances) { instanceInstances = instances.map(instance => { const instanceInstance = self.instance(instance.name); instanceInstance.metadata = instance; return instanceInstance; }); } const nextQuery = nextPageRequest ? extend({}, options, nextPageRequest) : null; callback(err, instanceInstances, nextQuery, ...args); }); } /** * Get a list of {@link Instance} objects as a readable object stream. * * Wrapper around {@link v1.InstanceAdminClient#listInstances}. * * @see {@link v1.InstanceAdminClient#listInstances} * @see [ListInstances API Documentation](https://cloud.google.com/spanner/docs/reference/rpc/google.spanner.admin.instance.v1#google.spanner.admin.instance.v1.InstanceAdmin.ListInstances) * * @method Spanner#getInstancesStream * @param {GetInstancesOptions} [options] Query object for listing instances. * @returns {ReadableStream} A readable stream that emits {@link Instance} * instances. * * @example * ``` * const {Spanner} = require('@google-cloud/spanner'); * const spanner = new Spanner(); * * spanner.getInstancesStream() * .on('error', console.error) * .on('data', function(instance) { * // `instance` is an `Instance` object. * }) * .on('end', function() { * // All instances retrieved. * }); * * //- * // If you anticipate many results, you can end a stream early to prevent * // unnecessary processing and API requests. * //- * spanner.getInstancesStream() * .on('data', function(instance) { * this.end(); * }); * ``` */ getInstancesStream(options = {}) { const gaxOpts = extend(true, {}, options.gaxOptions); let reqOpts = extend({}, options, { parent: 'projects/' + this.projectId, }); delete reqOpts.gaxOptions; // Copy over pageSize and pageToken values from gaxOptions. // However values set on options take precedence. if (gaxOpts) { reqOpts = extend({}, { pageSize: gaxOpts.pageSize, pageToken: gaxOpts.pageToken, }, reqOpts); delete gaxOpts.pageSize; delete gaxOpts.pageToken; } return this.requestStream({ client: 'InstanceAdminClient', method: 'listInstancesStream', reqOpts, gaxOpts, headers: this.commonHeaders_, }); } createInstanceConfig(name, config, callback) { if (!name) { throw new google_gax_1.GoogleError('A name is required to create an instance config.'); } if (!config) { throw new google_gax_1.GoogleError([ 'A configuration object is required to create an instance config.', ].join('')); } if (!config.baseConfig) { throw new google_gax_1.GoogleError(['Base instance config is required to create an instance config.'].join('')); } const formattedName = instance_config_1.InstanceConfig.formatName_(this.projectId, name); const displayName = config.displayName || formattedName.split('/').pop(); const reqOpts = { parent: this.projectFormattedName_, instanceConfigId: formattedName.split('/').pop(), instanceConfig: extend({ name: formattedName, displayName, }, config), validateOnly: config.validateOnly, }; if (config.baseConfig.indexOf('/') === -1) { reqOpts.instanceConfig.baseConfig = `projects/${this.projectId}/instanceConfigs/${config.baseConfig}`; } // validateOnly need not be passed in if it is null. if (reqOpts.validateOnly === null || reqOpts.validateOnly === undefined) delete reqOpts.validateOnly; // validateOnly and gaxOptions are not fields in InstanceConfig. delete reqOpts.instanceConfig.validateOnly; delete reqOpts.instanceConfig.gaxOptions; this.request({ client: 'InstanceAdminClient', method: 'createInstanceConfig', reqOpts, gaxOpts: config.gaxOptions, headers: this.commonHeaders_, }, (err, operation, resp) => { if (err) { callback(err, null, null, resp); return; } const instanceConfig = this.instanceConfig(formattedName); callback(null, instanceConfig, operation, resp); }); } getInstanceConfigs(optionsOrCallback, cb) { const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : cb; const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {}; const gaxOpts = extend(true, {}, options.gaxOptions); let reqOpts = extend({}, options, { parent: 'projects/' + this.projectId, }); delete reqOpts.gaxOptions; // Copy over pageSize and pageToken values from gaxOptions. // However values set on options take precedence. if (gaxOpts) { reqOpts = extend({}, { pageSize: gaxOpts.pageSize, pageToken: gaxOpts.pageToken, }, reqOpts); delete gaxOpts.pageSize; delete gaxOpts.pageToken; } return this.request({ client: 'InstanceAdminClient', method: 'listInstanceConfigs', reqOpts, gaxOpts, headers: this.commonHeaders_, }, (err, instanceConfigs, nextPageRequest, ...args) => { const nextQuery = nextPageRequest ? extend({}, options, nextPageRequest) : null; callback(err, instanceConfigs, nextQuery, ...args); }); } /** * Get a list of instance configs as a readable object stream. * * Wrapper around {@link v1.InstanceAdminClient#listInstanceConfigsStream}. * * @see {@link v1.InstanceAdminClient#listInstanceConfigsStream} * @see [ListInstanceConfigs API Documentation](https://cloud.google.com/spanner/docs/reference/rpc/google.spanner.admin.instance.v1#google.spanner.admin.instance.v1.InstanceAdmin.ListInstanceConfigs) * * @method Spanner#getInstanceConfigsStream * @param {GetInstanceConfigsOptions} [options] Query object for listing instance * configs. * @returns {ReadableStream} A readable stream that emits instance configs. * * @example * ``` * const {Spanner} = require('@google-cloud/spanner'); * const spanner = new Spanner(); * * spanner.getInstanceConfigsStream() * .on('error', console.error) * .on('data', function(instanceConfig) {}) * .on('end', function() { * // All instances retrieved. * }); * * //- * // If you anticipate many results, you can end a stream early to prevent * // unnecessary processing and API requests. * //- * spanner.getInstanceConfigsStream() * .on('data', function(instanceConfig) { * this.end(); * }); * ``` */ getInstanceConfigsStream(options = {}) { const gaxOpts = extend(true, {}, options.gaxOptions); let reqOpts = extend({}, options, { parent: 'projects/' + this.projectId, }); // Copy over pageSize and pageToken values from gaxOptions. // However values set on options take precedence. if (gaxOpts) { reqOpts = extend({}, { pageSize: gaxOpts.pageSize, pageToken: gaxOpts.pageToken, }, reqOpts); delete gaxOpts.pageSize; delete gaxOpts.pageToken; } delete reqOpts.gaxOptions; return this.requestStream({ client: 'InstanceAdminClient', method: 'listInstanceConfigsStream', reqOpts, gaxOpts, headers: this.commonHeaders_, }); } getInstanceConfig(name, optionsOrCallback, cb) { const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : cb; const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {}; const reqOpts = extend({}, { name: 'projects/' + this.projectId + '/instanceConfigs/' + name, }); const gaxOpts = extend({}, options.gaxOptions); return this.request({ client: 'InstanceAdminClient', method: 'getInstanceConfig', reqOpts, gaxOpts, headers: this.commonHeaders_, }, (err, instanceConfig) => { callback(err, instanceConfig); }); } getInstanceConfigOperations(optionsOrCallback, cb) { const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : cb; const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {}; const gaxOpts = extend(true, {}, options.gaxOptions); let reqOpts = extend({}, options, { parent: this.projectFormattedName_, }); delete reqOpts.gaxOptions; // Copy over pageSize and pageToken values from gaxOptions. // However, values set on options take precedence. if (gaxOpts) { reqOpts = extend({}, { pageSize: gaxOpts.pageSize, pageToken: gaxOpts.pageToken, }, reqOpts); delete gaxOpts.pageSize; delete gaxOpts.pageToken; } this.request({ client: 'InstanceAdminClient', method: 'listInstanceConfigOperations', reqOpts, gaxOpts, headers: this.commonHeaders_, }, (err, operations, nextPageRequest, ...args) => { const nextQuery = nextPageRequest ? extend({}, options, nextPageRequest) : null; callback(err, operations, nextQuery, ...args); }); } /** * Get a reference to an Instance object. * * @throws {GoogleError} If a name is not provided. * * @param {string} name The name of the instance. * @returns {Instance} An Instance object. * * @example * ``` * const {Spanner} = require('@google-cloud/spanner'); * const spanner = new Spanner(); * const instance = spanner.instance('my-instance'); * ``` */ instance(name) { if (!name) { throw new google_gax_1.GoogleError('A name is required to access an Instance object.'); } const key = name.split('/').pop(); if (!this.instances_.has(key)) { this.instances_.set(key, new instance_1.Instance(this, name)); } return this.instances_.get(key); } /** * Get a reference to an InstanceConfig object. * * @throws {GoogleError} If a name is not provided. * * @param {string} name The name of the instance config. * @returns {InstanceConfig} An InstanceConfig object. * * @example * ``` * const {Spanner} = require('@google-cloud/spanner'); * const spanner = new Spanner(); * const instanceConfig = spanner.instanceConfig('my-instance-config'); * ``` */ instanceConfig(name) { if (!name) { throw new google_gax_1.GoogleError('A name is required to access an InstanceConfig object.'); } const key = name.split('/').pop(); if (!this.instanceConfigs_.has(key)) { this.instanceConfigs_.set(key, new instance_config_1.InstanceConfig(this, name)); } return this.instanceConfigs_.get(key); } /** * Prepare a gapic request. This will cache the GAX client and replace * {{projectId}} placeholders, if necessary. * * @private * * @param {object} config Request config * @param {function} callback Callback function */ prepareGapicRequest_(config, callback) { this.auth.getProjectId((err, projectId) => { if (err) { callback(err); return; } const clientName = config.client; if (!this.clients_.has(clientName)) { this.clients_.set(clientName, new v1[clientName](this.options)); } const gaxClient = this.clients_.get(clientName); let reqOpts = extend(true, {}, config.reqOpts); reqOpts = (0, projectify_1.replaceProjectIdToken)(reqOpts, projectId); // It would have been preferable to replace the projectId already in the // constructor of Spanner, but that is not possible as auth.getProjectId // is an async method. This is therefore the first place where we have // access to the value that should be used instead of the placeholder. if (!this.projectIdReplaced_) { this.projectId = (0, projectify_1.replaceProjectIdToken)(this.projectId, projectId); this.projectFormattedName_ = (0, projectify_1.replaceProjectIdToken)(this.projectFormattedName_, projectId); this.instances_.forEach(instance => { instance.formattedName_ = (0, projectify_1.replaceProjectIdToken)(instance.formattedName_, projectId); instance.databases_.forEach(database => { database.formattedName_ = (0, projectify_1.replaceProjectIdToken)(database.formattedName_, projectId); }); }); this.projectIdReplaced_ = true; } config.headers[common_1.CLOUD_RESOURCE_HEADER] = (0, projectify_1.replaceProjectIdToken)(config.headers[common_1.CLOUD_RESOURCE_HEADER], projectId); // Do context propagation api_1.propagation.inject(api_1.context.active(), config.headers, { set: (carrier, key, value) => { carrier[key] = value; // Set the span context (trace and span ID) }, }); // Attach the x-goog-spanner-request-id to the currently active span. (0, request_id_header_1.attributeXGoogSpannerRequestIdToActiveSpan)(config); const requestFn = gaxClient[config.method].bind(gaxClient, reqOpts, // Add headers to `gaxOpts` extend(true, {}, config.gaxOpts, { otherArgs: { headers: config.headers, }, })); // Wrap requestFn to inject the spanner request id into every returned error. const wrappedRequestFn = (...args) => { const hasCallback = args && args.length > 0 && typeof args[args.length - 1] === 'function'; switch (hasCallback) { case true: { const cb = args[args.length - 1]; const priorArgs = args.slice(0, args.length - 1); requestFn(...priorArgs, (...results) => { if (results && results.length > 0) { const err = results[0]; (0, request_id_header_1.injectRequestIDIntoError)(config, err); } cb(...results); }); return; } case false: { const res = requestFn(...args); const stream = res; if (stream) { stream.on('error', err => { (0, request_id_header_1.injectRequestIDIntoError)(config, err); }); } const originallyPromise = res instanceof Promise; if (!originallyPromise) { return res; } return new Promise((resolve, reject) => { requestFn(...args) .then(resolve) .catch(err => { (0, request_id_header_1.injectRequestIDIntoError)(config, err); reject(err); }); }); } } }; callback(null, wrappedRequestFn); }); } /** * Funnel all API requests through this method to be sure we have a project * ID. * * @param {object} config Configuration object. * @param {object} config.gaxOpts GAX options. * @param {function} config.method The gax method to call. * @param {object} config.reqOpts Request options. * @param {function} [callback] Callback function. * @returns {Promise} */ // eslint-disable-next-line @typescript-eslint/no-explicit-any request(config, callback) { if (typeof callback === 'function') { this.prepareGapicRequest_(config, (err, requestFn) => { if (err) { callback(err); } else { requestFn(callback); } }); } else { return new Promise((resolve, reject) => { this.prepareGapicRequest_(config, (err, requestFn) => { if (err) { reject(err); } else { resolve(requestFn()); } }); }); } } /** * Funnel all streaming API requests through this method to be sure we have a * project ID. * * @param {object} config Configuration object. * @param {object} config.gaxOpts GAX options. * @param {function} config.method The gax method to call. * @param {object} config.reqOpts Request options. * @param {function} [callback] Callback function. * @returns {Stream} */ // eslint-disable-next-line @typescript-eslint/no-explicit-any requestStream(config) { const stream = streamEvents(through.obj()); stream.once('reading', () => { this.prepareGapicRequest_(config, (err, requestFn) => { if (err) { stream.destroy(err); return; } requestFn() .on('error', err => { stream.destroy(err); }) .pipe(stream); }); }); return stream; } /** * Helper function to get a Cloud Spanner Date object. * * DATE types represent a logical calendar date, independent of time zone. * DATE values do not represent a specific 24-hour period. Rather, a given * DATE value represents a different 24-hour period when interpreted in a * different time zone. Because of this, all values passed to * {@link Spanner.date} will be interpreted as local time. * * To represent an absolute point in time, use {@link Spanner.timestamp}. * * @param {string|number} [date] String representing the date or number * representing the year. * @param {number} [month] Number representing the month. * @param {number} [date] Number representing the date. * @returns {SpannerDate} * * @example * ``` * const {Spanner} = require('@google-cloud/spanner'); * const date = Spanner.date('08-20-1969'); * ``` */ static date(dateStringOrYear, month, date) { if (typeof dateStringOrYear === 'number') { return new codec_1.codec.SpannerDate(dateStringOrYear, month, date); } return new codec_1.codec.SpannerDate(dateStringOrYear); } /** * Date object with nanosecond precision. Supports all standard Date arguments * in addition to several custom types. * @external PreciseDate * @see {@link https://github.com/googleapis/nodejs-precise-date|PreciseDate} */ /** * Helper function to get a Cloud Spanner Timestamp object. * * String timestamps should have a canonical format of * `YYYY-[M]M-[D]D[( |T)[H]H:[M]M:[S]S[.DDDDDDDDD]]Z` * * **Timestamp values must be expressed in Zulu time and cannot include a UTC * offset.** * * @see https://cloud.google.com/spanner/docs/data-types#timestamp-type * * @param {string|number|google.protobuf.Timestamp|external:PreciseDate} * [timestamp] Either a RFC 3339 timestamp formatted string or a * {@link google.protobuf.Timestamp} object. If a PreciseDate is given, it * will return that timestamp as is. * @returns {external:PreciseDate} * * @example * ``` * const timestamp = Spanner.timestamp('2019-02-08T10:34:29.481145231Z'); * * ``` * @example With a `google.protobuf.Timestamp` object * ``` * const [seconds, nanos] = process.hrtime(); * const timestamp = Spanner.timestamp({seconds, nanos}); * ``` * * @example With a Date timestamp * ``` * const timestamp = Spanner.timestamp(Date.now()); * ``` */ static timestamp(value) { value = value || Date.now(); if (value instanceof precise_date_1.PreciseDate) { return value; } return new precise_date_1.PreciseDate(value); } /** * Helper function to get a Cloud Spanner Float32 object. * * @param {string|number} value The float as a number or string. * @returns {Float32} * * @example * ``` * const {Spanner} = require('@google-cloud/spanner'); * const float = Spanner.float32(10); * ``` */ static float32(value) { return new codec_1.codec.Float32(value); } /** * Helper function to get a Cloud Spanner Float64 object. * * @param {string|number} value The float as a number or string. * @returns {Float} * * @example * ``` * const {Spanner} = require('@google-cloud/spanner'); * const float = Spanner.float(10); * ``` */ static float(value) { return new codec_1.codec.Float(value); } /** * Helper function to get a Cloud Spanner Int64 object. * * @param {string|number} value The int as a number or string. * @returns {Int} * * @example * ``` * const {Spanner} = require('@google-cloud/spanner'); * const int = Spanner.int(10); * ``` */ static int(value) { return new codec_1.codec.Int(value); } /** * Helper function to get a Cloud Spanner Numeric object. * * @param {string} value The numeric value as a string. * @returns {Numeric} * * @example * ``` * const {Spanner} = require('@google-cloud/spanner'); * const numeric = Spanner.numeric("3.141592653"); * ``` */ static numeric(value) { return new codec_1.codec.Numeric(value); } /** * Helper function to get a Cloud Spanner pgNumeric object. * * @param {string} value The pgNumeric value as a string. * @returns {PGNumeric} * * @example * ``` * const {Spanner} = require('@google-cloud/spanner'); * const pgNumeric = Spanner.pgNumeric("3.141592653"); * ``` */ static pgNumeric(value) { return new codec_1.codec.PGNumeric(value); } /** * Helper function to get a Cloud Spanner pgJsonb object. * * @param {object|string} value The pgJsonb value as a string or object. * @returns {PGJsonb} * * @example * ``` * const {Spanner} = require('@google-cloud/spanner'); * const pgJsonb1 = Spanner.pgJsonb({rating: 6}); * const pgJsonb2 = Spanner.pgJsonb(`[ * { * "name": null, * "open": true * }]`) * ``` */ static pgJsonb(value) { return new codec_1.codec.PGJsonb(value); } /** * Helper function to get a Cloud Spanner Interval object. * * @param {number} months The months part of Interval as number. * @param {number} days The days part of Interval as number. * @param {bigint} nanoseconds The nanoseconds part of Interval as bigint. * @returns {Interval} * * @example * ``` * const {Spanner} = require('@google-cloud/spanner'); * const interval = Spanner.Interval(10, 20, BigInt(30)); * ``` */ static interval(months, days, nanoseconds) { return new codec_1.codec.Interval(months, days, nanoseconds); } /** * @typedef IProtoMessageParams * @property {object} value Proto Message value as serialized-buffer or message object. * @property {string} fullName Fully-qualified path name of proto message. * @property {Function} [messageFunction] Function generated by protobufs containing * helper methods for deserializing and serializing messages. */ /** * Helper function to get a Cloud Spanner proto Message object. * * @param {IProtoMessageParams} params The proto message value params * @returns {ProtoMessage} * * @example * ``` * const {Spanner} = require('@google-cloud/spanner'); * const protoMessage = Spanner.protoMessage({ * value: singerInfo, * messageFunction: music.SingerInfo, * fullName: "examples.spanner.music.SingerInfo" * }); * ``` */ static protoMessage(params) { return new codec_1.codec.ProtoMessage(params); } /** * @typedef IProtoEnumParams * @property {string | number} value Proto Enum value as a string constant or * an integer constant. * @property {string} fullName Fully-qualified path name of proto enum. * @property {object} [enumObject] An enum object generated by protobufjs-cli. */ /** * Helper function to get a Cloud Spanner proto enum object. * * @param {IProtoEnumParams} params The proto enum value params in the format of * @code{IProtoEnumParams} * @returns {ProtoEnum} * * @example * ``` * const {Spanner} = require('@google-cloud/spanner'); * const protoEnum = Spanner.protoEnum({ * value: 'ROCK', * enumObject: music.Genre, * fullName: "examples.spanner.music.Genre" * }); * ``` */ static protoEnum(params) { return new codec_1.codec.ProtoEnum(params); } /** * Helper function to get a Cloud Spanner Struct object. * * @param {object} value The struct as a JSON object. * @returns {Struct} * * @example * ``` * const {Spanner} = require('@google-cloud/spanner'); * const struct = Spanner.struct({ * user: 'bob', * age: 32 * }); * ``` */ static struct(value) { if (Array.isArray(value)) { return codec_1.codec.Struct.fromArray(value); } return codec_1.codec.Struct.fromJSON(value); } } exports.Spanner = Spanner; /*! Developer Documentation * * All async methods (except for streams) will return a Promise in the event * that a callback is omitted. */ (0, promisify_1.promisifyAll)(Spanner, { exclude: [ 'date', 'float32', 'float', 'instance', 'instanceConfig', 'int', 'numeric', 'pgNumeric', 'pgJsonb', 'operation', 'timestamp', 'interval', 'getInstanceAdminClient', 'getDatabaseAdminClient', ], }); /** * @type {object} * @property {constructor} DatabaseAdminClient * Reference to {@link v1.DatabaseAdminClient} * @property {constructor} InstanceAdminClient * Reference to {@link v1.InstanceAdminClient} * @property {constructor} SpannerClient * Reference to {@link v1.SpannerClient} */ const protos = require("../protos/protos"); exports.protos = protos; exports.default = { Spanner }; //# sourceMappingURL=index.js.map