1 11
/*!
2 11
 * Copyright 2016 Google Inc. All Rights Reserved.
3 11
 *
4 11
 * Licensed under the Apache License, Version 2.0 (the "License");
5 11
 * you may not use this file except in compliance with the License.
6 11
 * You may obtain a copy of the License at
7 11
 *
8 11
 *      http://www.apache.org/licenses/LICENSE-2.0
9 11
 *
10 11
 * Unless required by applicable law or agreed to in writing, software
11 11
 * distributed under the License is distributed on an "AS IS" BASIS,
12 11
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 11
 * See the License for the specific language governing permissions and
14 11
 * limitations under the License.
15 11
 */
16 11

17 11
import {GrpcService, GrpcServiceConfig} from './common-grpc/service';
18 11
import {paginator} from '@google-cloud/paginator';
19 11
import {PreciseDate} from '@google-cloud/precise-date';
20 11
import {replaceProjectIdToken} from '@google-cloud/projectify';
21 11
import {promisifyAll} from '@google-cloud/promisify';
22 11
import * as extend from 'extend';
23 11
import {GoogleAuth, GoogleAuthOptions} from 'google-auth-library';
24 11
import * as is from 'is';
25 11
import * as path from 'path';
26 11
import {common as p} from 'protobufjs';
27 11
import * as streamEvents from 'stream-events';
28 11
import * as through from 'through2';
29 11
import {codec, Float, Int, SpannerDate, Struct} from './codec';
30 11
import {Backup} from './backup';
31 11
import {Database} from './database';
32 11
import {
33 11
  Instance,
34 11
  CreateInstanceCallback,
35 11
  CreateInstanceResponse,
36 11
} from './instance';
37 11
import {google as instanceAdmin} from '../protos/protos';
38 11
import {PagedRequest, PagedResponse, PagedCallback} from './common';
39 11
import {Session} from './session';
40 11
import {SessionPool} from './session-pool';
41 11
import {Table} from './table';
42 11
import {PartitionedDml, Snapshot, Transaction} from './transaction';
43 11
import {GrpcClientOptions} from 'google-gax';
44 11
import {ChannelCredentials} from 'grpc';
45 11
import {
46 11
  createGcpApiConfig,
47 11
  gcpCallInvocationTransformer,
48 11
  gcpChannelFactoryOverride,
49 11
} from 'grpc-gcp';
50 11
import * as v1 from './v1';
51 11
import * as grpc from 'grpc';
52 11

53 11
// eslint-disable-next-line @typescript-eslint/no-var-requires
54 11
const gcpApiConfig = require('./spanner_grpc_config.json');
55 11

56 11
export type IOperation = instanceAdmin.longrunning.IOperation;
57 11

58 11
export type GetInstancesRequest = PagedRequest<
59 11
  instanceAdmin.spanner.admin.instance.v1.IListInstancesRequest & {
60 11
    maxResults?: number;
61 11
  }
62 11
>;
63 11
export type GetInstancesResponse = PagedResponse<
64 11
  Instance,
65 11
  instanceAdmin.spanner.admin.instance.v1.IListInstancesResponse
66 11
>;
67 11
export type GetInstancesCallback = PagedCallback<
68 11
  Instance,
69 11
  instanceAdmin.spanner.admin.instance.v1.IListInstancesResponse
70 11
>;
71 11

72 11
export type GetInstanceConfigsRequest = PagedRequest<
73 11
  instanceAdmin.spanner.admin.instance.v1.IListInstanceConfigsRequest & {
74 11
    maxResults?: number;
75 11
  }
76 11
>;
77 11
export type GetInstanceConfigsResponse = PagedResponse<
78 11
  instanceAdmin.spanner.admin.instance.v1.InstanceConfig,
79 11
  instanceAdmin.spanner.admin.instance.v1.IListInstanceConfigsResponse
80 11
>;
81 11
export type GetInstanceConfigsCallback = PagedCallback<
82 11
  instanceAdmin.spanner.admin.instance.v1.InstanceConfig,
83 11
  instanceAdmin.spanner.admin.instance.v1.IListInstanceConfigsResponse
84 11
>;
85 11

86 11
export interface SpannerOptions extends GrpcClientOptions {
87 11
  apiEndpoint?: string;
88 11
  servicePath?: string;
89 11
  port?: number;
90 11
  sslCreds?: ChannelCredentials;
91 11
}
92 11
export interface RequestConfig {
93 11
  client: string;
94 11
  method: string;
95 11
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
96 11
  reqOpts: any;
97 11
  gaxOpts?: {};
98 11
}
99 11
export interface CreateInstanceRequest {
100 11
  config: string;
101 11
  nodes?: number;
102 11
  labels?: {[k: string]: string} | null;
103 11
}
104 11
/**
105 11
 * Translates enum values to string keys.
106 11
 *
107 11
 * @param E enum type.
108 11
 */
109 11
export type EnumKey<E extends {[index: string]: unknown}> = keyof E;
110 11

111 11
/**
112 11
 * Translates an enum property of an object from enum value to enum key, leaving
113 11
 * all other properties as-is.
114 11
 *
115 11
 * @param T type containing properties to translate.
116 11
 * @param U name of the enum property.
117 11
 * @param E enum type to translate.
118 11
 */
119 11
export type TranslateEnumKeys<
120 11
  T,
121 11
  U extends keyof T,
122 11
  E extends {[index: string]: unknown}
123 11
> = {
124 11
  [P in keyof T]: P extends U ? EnumKey<E> | null | undefined : T[P];
125 11
};
126 11

127 11
/**
128 11
 * [Cloud Spanner](https://cloud.google.com/spanner) is a highly scalable,
129 11
 * transactional, managed, NewSQL database service. Cloud Spanner solves the
130 11
 * need for a horizontally-scaling database with consistent global transaction
131 11
 * and SQL semantics. With Cloud Spanner you don't need to choose between
132 11
 * consistency and horizontal scaling — you get both.
133 11
 *
134 11
 * @class
135 11
 *
136 11
 * @see [Cloud Spanner Documentation](https://cloud.google.com/spanner/docs)
137 11
 * @see [Cloud Spanner Concepts](https://cloud.google.com/spanner/docs/concepts)
138 11
 *
139 11
 * @example <caption>Install the client library with <a
140 11
 * href="https://www.npmjs.com/">npm</a>:</caption> npm install --save
141 11
 * @google-cloud/spanner
142 11
 *
143 11
 * @example <caption>Create a client that uses <a
144 11
 * href="https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application">Application
145 11
 * Default Credentials (ADC)</a>:</caption> const client = new Spanner();
146 11
 *
147 11
 * @example <caption>Create a client with <a
148 11
 * href="https://cloud.google.com/docs/authentication/production#obtaining_and_providing_service_account_credentials_manually">explicit
149 11
 * credentials</a>:</caption> const client = new Spanner({ projectId:
150 11
 * 'your-project-id', keyFilename: '/path/to/keyfile.json'
151 11
 * });
152 11
 *
153 11
 * @example <caption>include:samples/quickstart.js</caption>
154 11
 * region_tag:spanner_quickstart
155 11
 * Full quickstart example:
156 11
 *
157 11
 * @param {ClientConfig} [options] Configuration options.
158 11
 */
159 11
class Spanner extends GrpcService {
160 11
  options: GoogleAuthOptions;
161 11
  auth: GoogleAuth;
162 11
  clients_: Map<string, {}>;
163 11
  instances_: Map<string, Instance>;
164 11
  getInstancesStream: Function;
165 11

166 11
  /**
167 11
   * Placeholder used to auto populate a column with the commit timestamp.
168 11
   * This can only be used for timestamp columns that have set the option
169 11
   * "(allow_commit_timestamp=true)" in the schema.
170 11
   *
171 11
   * @type {string}
172 11
   */
173 11
  static COMMIT_TIMESTAMP = 'spanner.commit_timestamp()';
174 11

175 11
  /**
176 11
   * Gets the configured Spanner emulator host from an environment variable.
177 11
   */
178 11
  static getSpannerEmulatorHost():
179 11
    | {endpoint: string; port?: number}
180 11
    | undefined {
181 11
    const endpointWithPort = process.env.SPANNER_EMULATOR_HOST;
182 11
    if (endpointWithPort) {
183 11
      if (
184 11
        endpointWithPort.startsWith('http:') ||
185 11
        endpointWithPort.startsWith('https:')
186 11
      ) {
187 11
        throw new Error(
188 11
          'SPANNER_EMULATOR_HOST must not start with a protocol specification (http/https)'
189 11
        );
190 11
      }
191 11
      const index = endpointWithPort.indexOf(':');
192 11
      if (index > -1) {
193 11
        const portName = endpointWithPort.substring(index + 1);
194 11
        const port = +portName;
195 11
        if (!port || port < 1 || port > 65535) {
196 11
          throw new Error(`Invalid port number: ${portName}`);
197 11
        }
198 11
        return {
199 11
          endpoint: endpointWithPort.substring(0, index),
200 11
          port: +endpointWithPort.substring(index + 1),
201 11
        };
202 11
      }
203 11
      return {endpoint: endpointWithPort};
204 11
    }
205 11
    return undefined;
206 11
  }
207 11

208 11
  constructor(options?: SpannerOptions) {
209 11
    const scopes: Array<{}> = [];
210 11
    const clientClasses = [
211 11
      v1.DatabaseAdminClient,
212 11
      v1.InstanceAdminClient,
213 11
      v1.SpannerClient,
214 11
    ];
215 11
    for (const clientClass of clientClasses) {
216 11
      for (const scope of clientClass.scopes) {
217 11
        if (scopes.indexOf(scope) === -1) {
218 11
          scopes.push(scope);
219 11
        }
220 11
      }
221 11
    }
222 11
    options = (Object.assign(
223 11
      {
224 11
        libName: 'gccl',
225 11
        libVersion: require('../../package.json').version,
226 11
        scopes,
227 11
        // Enable grpc-gcp support
228 11
        'grpc.callInvocationTransformer': gcpCallInvocationTransformer,
229 11
        'grpc.channelFactoryOverride': gcpChannelFactoryOverride,
230 11
        'grpc.gcpApiConfig': createGcpApiConfig(gcpApiConfig),
231 11
        grpc,
232 11
      },
233 11
      options || {}
234 11
    ) as {}) as SpannerOptions;
235 11
    const emulatorHost = Spanner.getSpannerEmulatorHost();
236 11
    if (
237 11
      emulatorHost &&
238 11
      emulatorHost.endpoint &&
239 11
      emulatorHost.endpoint.length > 0
240 11
    ) {
241 11
      options.servicePath = emulatorHost.endpoint;
242 11
      options.port = emulatorHost.port;
243 11
      options.sslCreds = grpc.credentials.createInsecure();
244 11
    }
245 11
    const config = ({
246 11
      baseUrl:
247 11
        options.apiEndpoint ||
248 11
        options.servicePath ||
249 11
        v1.SpannerClient.servicePath,
250 11
      protosDir: path.resolve(__dirname, '../protos'),
251 11
      protoServices: {
252 11
        Operations: {
253 11
          path: 'google/longrunning/operations.proto',
254 11
          service: 'longrunning',
255 11
        },
256 11
      },
257 11
      scopes: ['https://www.googleapis.com/auth/cloud-platform'],
258 11
      packageJson: require('../../package.json'),
259 11
    } as {}) as GrpcServiceConfig;
260 11
    super(config, options);
261 11
    this.options = options;
262 11
    this.auth = new GoogleAuth(this.options);
263 11
    this.clients_ = new Map();
264 11
    this.instances_ = new Map();
265 11

266 11
    /**
267 11
     * Get a list of {@link Instance} objects as a readable object stream.
268 11
     *
269 11
     * Wrapper around {@link v1.InstanceAdminClient#listInstances}.
270 11
     *
271 11
     * @see {@link v1.InstanceAdminClient#listInstances}
272 11
     * @see [ListInstances API Documentation](https://cloud.google.com/spanner/docs/reference/rpc/google.spanner.admin.instance.v1#google.spanner.admin.instance.v1.InstanceAdmin.ListInstances)
273 11
     *
274 11
     * @method Spanner#getInstancesStream
275 11
     * @param {GetInstancesRequest} [query] Query object for listing instances.
276 11
     * @returns {ReadableStream} A readable stream that emits {@link Instance}
277 11
     *     instances.
278 11
     *
279 11
     * @example
280 11
     * const {Spanner} = require('@google-cloud/spanner');
281 11
     * const spanner = new Spanner();
282 11
     *
283 11
     * spanner.getInstancesStream()
284 11
     *   .on('error', console.error)
285 11
     *   .on('data', function(instance) {
286 11
     *     // `instance` is an `Instance` object.
287 11
     *   })
288 11
     *   .on('end', function() {
289 11
     *     // All instances retrieved.
290 11
     *   });
291 11
     *
292 11
     * //-
293 11
     * // If you anticipate many results, you can end a stream early to prevent
294 11
     * // unnecessary processing and API requests.
295 11
     * //-
296 11
     * spanner.getInstancesStream()
297 11
     *   .on('data', function(instance) {
298 11
     *     this.end();
299 11
     *   });
300 11
     */
301 11
    this.getInstancesStream = paginator.streamify('getInstances');
302 11
  }
303 11

304 11
  createInstance(
305 11
    name: string,
306 11
    config: CreateInstanceRequest
307 11
  ): Promise<CreateInstanceResponse>;
308 11
  createInstance(
309 11
    name: string,
310 11
    config: CreateInstanceRequest,
311 11
    callback: CreateInstanceCallback
312 11
  ): void;
313 11
  /**
314 11
   * Config for the new instance.
315 11
   *
316 11
   * @typedef {object} CreateInstanceRequest
317 11
   * @property {string} config The name of the instance's configuration.
318 11
   * @property {number} [nodes=1] The number of nodes allocated to this instance.
319 11
   *     Defaults to 1.
320 11
   * @property {Object.<string, string>} [labels] Labels are a flexible and
321 11
   *     lightweight mechanism for organizing cloud resources into groups that
322 11
   *     reflect a customer's organizational needs and deployment strategies.
323 11
   *     Cloud Labels can be used to filter collections of resources. They can
324 11
   *     be used to control how resource metrics are aggregated. And they can
325 11
   *     be used as arguments to policy management rules (e.g. route,
326 11
   *     firewall, load balancing, etc.).
327 11
   */
328 11
  /**
329 11
   * @typedef {array} CreateInstanceResponse
330 11
   * @property {Instance} 0 The new {@link Instance}.
331 11
   * @property {Operation} 1 An operation object that can be used to check
332 11
   *     the status of the request.
333 11
   * @property {IOperation} 2 The full API response.
334 11
   */
335 11
  /**
336 11
   * @callback CreateInstanceCallback
337 11
   * @param {?Error} err Request error, if any.
338 11
   * @param {Instance} instance The new {@link Instance}.
339 11
   * @param {Operation} operation An operation object that can be used to
340 11
   *     check the status of the request.
341 11
   * @param {IOperation} apiResponse The full API response.
342 11
   */
343 11
  /**
344 11
   * Create an instance.
345 11
   *
346 11
   * Wrapper around {@link v1.InstanceAdminClient#createInstance}.
347 11
   *
348 11
   * @see {@link v1.InstanceAdminClient#createInstance}
349 11
   * @see [CreateInstace API Documentation](https://cloud.google.com/spanner/docs/reference/rpc/google.spanner.admin.instance.v1#google.spanner.admin.instance.v1.InstanceAdmin.CreateInstance)
350 11
   *
351 11
   * @throws {Error} If a name is not provided.
352 11
   * @throws {Error} If a configuration object is not provided.
353 11
   *
354 11
   * @param {string} name The name of the instance to be created.
355 11
   * @param {CreateInstanceRequest} config Configuration object.
356 11
   * @param {CreateInstanceCallback} [callback] Callback function.
357 11
   * @returns {Promise<CreateInstanceResponse>}
358 11
   *
359 11
   * @example
360 11
   * const {Spanner} = require('@google-cloud/spanner');
361 11
   * const spanner = new Spanner();
362 11
   *
363 11
   * const config = {
364 11
   *   config: 'regional-us-central1',
365 11
   *   nodes: 1
366 11
   * };
367 11
   *
368 11
   * function callback(err, instance, operation, apiResponse) {
369 11
   *   if (err) {
370 11
   *     // Error handling omitted.
371 11
   *   }
372 11
   *
373 11
   *   operation
374 11
   *     .on('error', function(err) {})
375 11
   *     .on('complete', function() {
376 11
   *       // Instance created successfully.
377 11
   *     });
378 11
   * }
379 11
   *
380 11
   * spanner.createInstance('new-instance-name', config, callback);
381 11
   *
382 11
   * //-
383 11
   * // If the callback is omitted, we'll return a Promise.
384 11
   * //-
385 11
   * spanner.createInstance('new-instance-name', config)
386 11
   *   .then(function(data) {
387 11
   *     const instance = data[0];
388 11
   *     const operation = data[1];
389 11
   *     return operation.promise();
390 11
   *   })
391 11
   *   .then(function() {
392 11
   *     // Instance created successfully.
393 11
   *   });
394 11
   */
395 11
  createInstance(
396 11
    name: string,
397 11
    config: CreateInstanceRequest,
398 11
    callback?: CreateInstanceCallback
399 11
  ): void | Promise<CreateInstanceResponse> {
400 11
    if (!name) {
401 11
      throw new Error('A name is required to create an instance.');
402 11
    }
403 11
    if (!config) {
404 11
      throw new Error(
405 11
        ['A configuration object is required to create an instance.'].join('')
406 11
      );
407 11
    }
408 11
    const formattedName = Instance.formatName_(this.projectId, name);
409 11
    const shortName = formattedName.split('/').pop();
410 11
    const reqOpts = {
411 11
      parent: 'projects/' + this.projectId,
412 11
      instanceId: shortName,
413 11
      instance: extend(
414 11
        {
415 11
          name: formattedName,
416 11
          displayName: shortName,
417 11
          nodeCount: config.nodes || 1,
418 11
        },
419 11
        config
420 11
      ),
421 11
    };
422 11

423 11
    delete reqOpts.instance.nodes;
424 11

425 11
    if (config.config.indexOf('/') === -1) {
426 11
      reqOpts.instance.config = `projects/${this.projectId}/instanceConfigs/${config.config}`;
427 11
    }
428 11
    this.request(
429 11
      {
430 11
        client: 'InstanceAdminClient',
431 11
        method: 'createInstance',
432 11
        reqOpts,
433 11
      },
434 11
      (err, operation, resp) => {
435 11
        if (err) {
436 11
          callback!(err, null, null, resp);
437 11
          return;
438 11
        }
439 11
        const instance = this.instance(formattedName);
440 11
        callback!(null, instance, operation, resp);
441 11
      }
442 11
    );
443 11
  }
444 11

445 11
  getInstances(query?: GetInstancesRequest): Promise<GetInstancesResponse>;
446 11
  getInstances(callback: GetInstancesCallback): void;
447 11
  getInstances(
448 11
    query: GetInstancesRequest,
449 11
    callback: GetInstancesCallback
450 11
  ): void;
451 11
  /**
452 11
   * Query object for listing instances.
453 11
   *
454 11
   * @typedef {object} GetInstancesRequest
455 11
   * @property {boolean} [autoPaginate=true] Have pagination handled
456 11
   *     automatically.
457 11
   * @property {string} [filter] An expression for filtering the results of the
458 11
   *     request. Filter rules are case insensitive. The fields eligible for
459 11
   *     filtering are:
460 11
   *     - **`name`**
461 11
   *     - **`display_name`**
462 11
   *     - **`labels.key`** where key is the name of a label
463 11
   *
464 11
   *     Some examples of using filters are:
465 11
   *     - **`name:*`** The instance has a name.
466 11
   *     - **`name:Howl`** The instance's name is howl.
467 11
   *     - **`labels.env:*`** The instance has the label env.
468 11
   *     - **`labels.env:dev`** The instance's label env has the value dev.
469 11
   *     - **`name:howl labels.env:dev`** The instance's name is howl and it has
470 11
   *       the label env with value dev.
471 11
   * @property {number} [maxApiCalls] Maximum number of API calls to make.
472 11
   * @property {number} [maxResults] Maximum number of items to return.
473 11
   * @property {number} [pageSize] Maximum number of results per page.
474 11
   * @property {string} [pageToken] A previously-returned page token
475 11
   *     representing part of the larger set of results to view.
476 11
   */
477 11
  /**
478 11
   * @typedef {array} GetInstancesResponse
479 11
   * @property {Instance[]} 0 Array of {@link Instance} instances.
480 11
   * @property {object} 1 The full API response.
481 11
   */
482 11
  /**
483 11
   * @callback GetInstancesCallback
484 11
   * @param {?Error} err Request error, if any.
485 11
   * @param {Instance[]} instances Array of {@link Instance} instances.
486 11
   * @param {object} apiResponse The full API response.
487 11
   */
488 11
  /**
489 11
   * Get a list of instances.
490 11
   *
491 11
   * Wrapper around {@link v1.InstanceAdminClient#listInstances}.
492 11
   *
493 11
   * @see {@link v1.InstanceAdminClient#listInstances}
494 11
   * @see [ListInstances API Documentation](https://cloud.google.com/spanner/docs/reference/rpc/google.spanner.admin.instance.v1#google.spanner.admin.instance.v1.InstanceAdmin.ListInstances)
495 11
   *
496 11
   * @param {GetInstancesRequest} [query] Query object for listing instances.
497 11
   * @param {GetInstancesCallback} [callback] Callback function.
498 11
   * @returns {Promise<GetInstancesResponse>}
499 11
   *
500 11
   * @example
501 11
   * const {Spanner} = require('@google-cloud/spanner');
502 11
   * const spanner = new Spanner();
503 11
   *
504 11
   * spanner.getInstances(function(err, instances) {
505 11
   *   // `instances` is an array of `Instance` objects.
506 11
   * });
507 11
   *
508 11
   * //-
509 11
   * // To control how many API requests are made and page through the results
510 11
   * // manually, set `autoPaginate` to `false`.
511 11
   * //-
512 11
   * function callback(err, instances, nextQuery, apiResponse) {
513 11
   *   if (nextQuery) {
514 11
   *     // More results exist.
515 11
   *     spanner.getInstances(nextQuery, callback);
516 11
   *   }
517 11
   * }
518 11
   *
519 11
   * spanner.getInstances({
520 11
   *   autoPaginate: false
521 11
   * }, callback);
522 11
   *
523 11
   * //-
524 11
   * // If the callback is omitted, we'll return a Promise.
525 11
   * //-
526 11
   * spanner.getInstances().then(function(data) {
527 11
   *   const instances = data[0];
528 11
   * });
529 11
   */
530 11
  getInstances(
531 11
    query?: GetInstancesRequest | GetInstancesCallback,
532 11
    callback?: GetInstancesCallback
533 11
  ): Promise<GetInstancesResponse> | void {
534 11
    // eslint-disable-next-line @typescript-eslint/no-this-alias
535 11
    const self = this;
536 11
    if (is.fn(query)) {
537 11
      callback = query as GetInstancesCallback;
538 11
      query = {};
539 11
    }
540 11
    const reqOpts = extend({}, query, {
541 11
      parent: 'projects/' + this.projectId,
542 11
    });
543 11
    this.request(
544 11
      {
545 11
        client: 'InstanceAdminClient',
546 11
        method: 'listInstances',
547 11
        reqOpts,
548 11
        gaxOpts: query,
549 11
      },
550 11
      (err, instances, ...args) => {
551 11
        let instanceInstances: Instance[] | null = null;
552 11
        if (instances) {
553 11
          instanceInstances = instances.map(instance => {
554 11
            const instanceInstance = self.instance(instance.name);
555 11
            instanceInstance.metadata = instance;
556 11
            return instanceInstance;
557 11
          });
558 11
        }
559 11
        callback!(err, instanceInstances, ...args);
560 11
      }
561 11
    );
562 11
  }
563 11

564 11
  getInstanceConfigs(
565 11
    query?: GetInstanceConfigsRequest
566 11
  ): Promise<GetInstanceConfigsResponse>;
567 11
  getInstanceConfigs(callback: GetInstanceConfigsCallback): void;
568 11
  getInstanceConfigs(
569 11
    query: GetInstanceConfigsRequest,
570 11
    callback: GetInstanceConfigsCallback
571 11
  ): void;
572 11
  /**
573 11
   * Query object for listing instance configs.
574 11
   *
575 11
   * @typedef {object} GetInstanceConfigsRequest
576 11
   * @property {boolean} [autoPaginate=true] Have pagination handled
577 11
   *     automatically.
578 11
   * @property {number} [maxApiCalls] Maximum number of API calls to make.
579 11
   * @property {number} [maxResults] Maximum number of items to return.
580 11
   * @property {number} [pageSize] Maximum number of results per page.
581 11
   * @property {string} [pageToken] A previously-returned page token
582 11
   *     representing part of the larger set of results to view.
583 11
   */
584 11
  /**
585 11
   * @typedef {array} GetInstanceConfigsResponse
586 11
   * @property {object[]} 0 List of all available instance configs.
587 11
   * @property {string} 0.name The unique identifier for the instance config.
588 11
   * @property {string} 0.displayName The name of the instance config as it
589 11
   *     appears in UIs.
590 11
   * @property {object} 1 The full API response.
591 11
   */
592 11
  /**
593 11
   * @callback GetInstanceConfigsCallback
594 11
   * @param {?Error} err Request error, if any.
595 11
   * @param {object[]} instanceConfigs List of all available instance configs.
596 11
   * @param {string} instanceConfigs.name The unique identifier for the instance
597 11
   *     config.
598 11
   * @param {string} instanceConfigs.displayName The name of the instance config
599 11
   *     as it appears in UIs.
600 11
   * @param {object} apiResponse The full API response.
601 11
   */
602 11
  /**
603 11
   * Get a list of instance configs.
604 11
   *
605 11
   * Wrapper around {@link v1.InstanceAdminClient#listInstanceConfigs}.
606 11
   *
607 11
   * @see {@link v1.InstanceAdminClient#listInstanceConfigs}
608 11
   * @see [ListInstanceConfigs API Documentation](https://cloud.google.com/spanner/docs/reference/rpc/google.spanner.admin.instance.v1#google.spanner.admin.instance.v1.InstanceAdmin.ListInstanceConfigs)
609 11
   *
610 11
   * @param {GetInstanceConfigsRequest} [query] Query object for listing instance
611 11
   *     configs.
612 11
   * @param {GetInstanceConfigsCallback} [callback] Callback function.
613 11
   * @returns {Promise<GetInstanceConfigsResponse>}
614 11
   *
615 11
   * @example
616 11
   * const {Spanner} = require('@google-cloud/spanner');
617 11
   * const spanner = new Spanner();
618 11
   *
619 11
   * spanner.getInstanceConfigs(function(err, instanceConfigs) {
620 11
   *   // `instanceConfigs` is an array of instance configuration descriptors.
621 11
   * });
622 11
   *
623 11
   * //-
624 11
   * // To control how many API requests are made and page through the results
625 11
   * // manually, set `autoPaginate` to `false`.
626 11
   * //-
627 11
   * function callback(err, instanceConfigs, nextQuery, apiResponse) {
628 11
   *   if (nextQuery) {
629 11
   *     // More results exist.
630 11
   *     spanner.getInstanceConfigs(nextQuery, callback);
631 11
   *   }
632 11
   * }
633 11
   *
634 11
   * spanner.getInstanceConfigs({
635 11
   *   autoPaginate: false
636 11
   * }, callback);
637 11
   *
638 11
   * //-
639 11
   * // If the callback is omitted, we'll return a Promise.
640 11
   * //-
641 11
   * spanner.getInstanceConfigs().then(function(data) {
642 11
   *   const instanceConfigs = data[0];
643 11
   * });
644 11
   */
645 11
  getInstanceConfigs(
646 11
    queryOrCallback?: GetInstanceConfigsRequest | GetInstanceConfigsCallback,
647 11
    cb?: GetInstanceConfigsCallback
648 11
  ): Promise<GetInstanceConfigsResponse> | void {
649 11
    const callback =
650 11
      typeof queryOrCallback === 'function'
651 11
        ? (queryOrCallback as GetInstanceConfigsCallback)
652 11
        : cb;
653 11
    const query =
654 11
      typeof queryOrCallback === 'object'
655 11
        ? (queryOrCallback as GetInstanceConfigsRequest)
656 11
        : {};
657 11
    const reqOpts = extend({}, query, {
658 11
      parent: 'projects/' + this.projectId,
659 11
    });
660 11
    return this.request(
661 11
      {
662 11
        client: 'InstanceAdminClient',
663 11
        method: 'listInstanceConfigs',
664 11
        reqOpts,
665 11
        gaxOpts: query,
666 11
      },
667 11
      callback
668 11
    );
669 11
  }
670 11

671 11
  /**
672 11
   * Get a list of instance configs as a readable object stream.
673 11
   *
674 11
   * Wrapper around {@link v1.InstanceAdminClient#listInstanceConfigsStream}.
675 11
   *
676 11
   * @see {@link v1.InstanceAdminClient#listInstanceConfigsStream}
677 11
   * @see [ListInstanceConfigs API Documentation](https://cloud.google.com/spanner/docs/reference/rpc/google.spanner.admin.instance.v1#google.spanner.admin.instance.v1.InstanceAdmin.ListInstanceConfigs)
678 11
   *
679 11
   * @method Spanner#getInstanceConfigsStream
680 11
   * @param {GetInstanceConfigsRequest} [query] Query object for listing instance
681 11
   *     configs.
682 11
   * @returns {ReadableStream} A readable stream that emits instance configs.
683 11
   *
684 11
   * @example
685 11
   * const {Spanner} = require('@google-cloud/spanner');
686 11
   * const spanner = new Spanner();
687 11
   *
688 11
   * spanner.getInstanceConfigsStream()
689 11
   *   .on('error', console.error)
690 11
   *   .on('data', function(instanceConfig) {})
691 11
   *   .on('end', function() {
692 11
   *     // All instances retrieved.
693 11
   *   });
694 11
   *
695 11
   * //-
696 11
   * // If you anticipate many results, you can end a stream early to prevent
697 11
   * // unnecessary processing and API requests.
698 11
   * //-
699 11
   * spanner.getInstanceConfigsStream()
700 11
   *   .on('data', function(instanceConfig) {
701 11
   *     this.end();
702 11
   *   });
703 11
   */
704 11
  getInstanceConfigsStream(
705 11
    query?: GetInstanceConfigsRequest
706 11
  ): NodeJS.ReadableStream {
707 11
    const reqOpts = extend({}, query, {
708 11
      parent: 'projects/' + this.projectId,
709 11
    });
710 11
    return this.requestStream({
711 11
      client: 'InstanceAdminClient',
712 11
      method: 'listInstanceConfigsStream',
713 11
      reqOpts,
714 11
      gaxOpts: query,
715 11
    });
716 11
  }
717 11

718 11
  /**
719 11
   * Get a reference to an Instance object.
720 11
   *
721 11
   * @throws {Error} If a name is not provided.
722 11
   *
723 11
   * @param {string} name The name of the instance.
724 11
   * @returns {Instance} An Instance object.
725 11
   *
726 11
   * @example
727 11
   * const {Spanner} = require('@google-cloud/spanner');
728 11
   * const spanner = new Spanner();
729 11
   * const instance = spanner.instance('my-instance');
730 11
   */
731 11
  instance(name: string): Instance {
732 11
    if (!name) {
733 11
      throw new Error('A name is required to access an Instance object.');
734 11
    }
735 11
    const key = name.split('/').pop()!;
736 11
    if (!this.instances_.has(key)) {
737 11
      this.instances_.set(key, new Instance(this, name));
738 11
    }
739 11
    return this.instances_.get(key)!;
740 11
  }
741 11

742 11
  /**
743 11
   * Prepare a gapic request. This will cache the GAX client and replace
744 11
   * {{projectId}} placeholders, if necessary.
745 11
   *
746 11
   * @private
747 11
   *
748 11
   * @param {object} config Request config
749 11
   * @param {function} callback Callback function
750 11
   */
751 11
  prepareGapicRequest_(config, callback) {
752 11
    this.auth.getProjectId((err, projectId) => {
753 11
      if (err) {
754 11
        callback(err);
755 11
        return;
756 11
      }
757 11
      const clientName = config.client;
758 11
      if (!this.clients_.has(clientName)) {
759 11
        this.clients_.set(clientName, new v1[clientName](this.options));
760 11
      }
761 11
      const gaxClient = this.clients_.get(clientName)!;
762 11
      let reqOpts = extend(true, {}, config.reqOpts);
763 11
      reqOpts = replaceProjectIdToken(reqOpts, projectId!);
764 11
      const requestFn = gaxClient[config.method].bind(
765 11
        gaxClient,
766 11
        reqOpts,
767 11
        config.gaxOpts
768 11
      );
769 11
      callback(null, requestFn);
770 11
    });
771 11
  }
772 11

773 11
  /**
774 11
   * Funnel all API requests through this method to be sure we have a project
775 11
   * ID.
776 11
   *
777 11
   * @param {object} config Configuration object.
778 11
   * @param {object} config.gaxOpts GAX options.
779 11
   * @param {function} config.method The gax method to call.
780 11
   * @param {object} config.reqOpts Request options.
781 11
   * @param {function} [callback] Callback function.
782 11
   * @returns {Promise}
783 11
   */
784 11
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
785 11
  request(config: any, callback?: any): any {
786 11
    if (is.fn(callback)) {
787 11
      this.prepareGapicRequest_(config, (err, requestFn) => {
788 11
        if (err) {
789 11
          callback(err);
790 11
        } else {
791 11
          requestFn(callback);
792 11
        }
793 11
      });
794 11
    } else {
795 11
      return new Promise((resolve, reject) => {
796 11
        this.prepareGapicRequest_(config, (err, requestFn) => {
797 11
          if (err) {
798 11
            reject(err);
799 11
          } else {
800 11
            resolve(requestFn());
801 11
          }
802 11
        });
803 11
      });
804 11
    }
805 11
  }
806 11

807 11
  /**
808 11
   * Funnel all streaming API requests through this method to be sure we have a
809 11
   * project ID.
810 11
   *
811 11
   * @param {object} config Configuration object.
812 11
   * @param {object} config.gaxOpts GAX options.
813 11
   * @param {function} config.method The gax method to call.
814 11
   * @param {object} config.reqOpts Request options.
815 11
   * @param {function} [callback] Callback function.
816 11
   * @returns {Stream}
817 11
   */
818 11
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
819 11
  requestStream(config): any {
820 11
    const stream = streamEvents(through.obj());
821 11
    stream.once('reading', () => {
822 11
      this.prepareGapicRequest_(config, (err, requestFn) => {
823 11
        if (err) {
824 11
          stream.destroy(err);
825 11
          return;
826 11
        }
827 11
        requestFn()
828 11
          .on('error', err => {
829 11
            stream.destroy(err);
830 11
          })
831 11
          .pipe(stream);
832 11
      });
833 11
    });
834 11
    return stream;
835 11
  }
836 11

837 11
  static date(dateString?: string);
838 11
  static date(year: number, month: number, date: number);
839 11
  /**
840 11
   * Helper function to get a Cloud Spanner Date object.
841 11
   *
842 11
   * DATE types represent a logical calendar date, independent of time zone.
843 11
   * DATE values do not represent a specific 24-hour period. Rather, a given
844 11
   * DATE value represents a different 24-hour period when interpreted in a
845 11
   * different time zone. Because of this, all values passed to
846 11
   * {@link Spanner.date} will be interpreted as local time.
847 11
   *
848 11
   * To represent an absolute point in time, use {@link Spanner.timestamp}.
849 11
   *
850 11
   * @param {string|number} [date] String representing the date or number
851 11
   *     representing the year.
852 11
   * @param {number} [month] Number representing the month.
853 11
   * @param {number} [date] Number representing the date.
854 11
   * @returns {SpannerDate}
855 11
   *
856 11
   * @example
857 11
   * const {Spanner} = require('@google-cloud/spanner');
858 11
   * const date = Spanner.date('08-20-1969');
859 11
   */
860 11
  static date(
861 11
    dateStringOrYear?: string | number,
862 11
    month?: number,
863 11
    date?: number
864 11
  ): SpannerDate {
865 11
    if (typeof dateStringOrYear === 'number') {
866 11
      return new codec.SpannerDate(dateStringOrYear, month!, date!);
867 11
    }
868 11
    return new codec.SpannerDate(dateStringOrYear);
869 11
  }
870 11

871 11
  /**
872 11
   * Date object with nanosecond precision. Supports all standard Date arguments
873 11
   * in addition to several custom types.
874 11
   * @external PreciseDate
875 11
   * @see {@link https://github.com/googleapis/nodejs-precise-date|PreciseDate}
876 11
   */
877 11
  /**
878 11
   * Helper function to get a Cloud Spanner Timestamp object.
879 11
   *
880 11
   * String timestamps should have a canonical format of
881 11
   * `YYYY-[M]M-[D]D[( |T)[H]H:[M]M:[S]S[.DDDDDDDDD]]Z`
882 11
   *
883 11
   * **Timestamp values must be expressed in Zulu time and cannot include a UTC
884 11
   * offset.**
885 11
   *
886 11
   * @see https://cloud.google.com/spanner/docs/data-types#timestamp-type
887 11
   *
888 11
   * @param {string|number|google.protobuf.Timestamp|external:PreciseDate}
889 11
   *     [timestamp] Either a RFC 3339 timestamp formatted string or a
890 11
   *     {@link google.protobuf.Timestamp} object. If a PreciseDate is given, it
891 11
   *     will return that timestamp as is.
892 11
   * @returns {external:PreciseDate}
893 11
   *
894 11
   * @example
895 11
   * const timestamp = Spanner.timestamp('2019-02-08T10:34:29.481145231Z');
896 11
   *
897 11
   * @example <caption>With a `google.protobuf.Timestamp` object</caption>
898 11
   * const [seconds, nanos] = process.hrtime();
899 11
   * const timestamp = Spanner.timestamp({seconds, nanos});
900 11
   *
901 11
   * @example <caption>With a Date timestamp</caption>
902 11
   * const timestamp = Spanner.timestamp(Date.now());
903 11
   */
904 11
  static timestamp(
905 11
    value?: string | number | p.ITimestamp | PreciseDate
906 11
  ): PreciseDate {
907 11
    value = value || Date.now();
908 11
    if (value instanceof PreciseDate) {
909 11
      return value;
910 11
    }
911 11
    return new PreciseDate(value as number);
912 11
  }
913 11

914 11
  /**
915 11
   * Helper function to get a Cloud Spanner Float64 object.
916 11
   *
917 11
   * @param {string|number} value The float as a number or string.
918 11
   * @returns {Float}
919 11
   *
920 11
   * @example
921 11
   * const {Spanner} = require('@google-cloud/spanner');
922 11
   * const float = Spanner.float(10);
923 11
   */
924 11
  static float(value): Float {
925 11
    return new codec.Float(value);
926 11
  }
927 11

928 11
  /**
929 11
   * Helper function to get a Cloud Spanner Int64 object.
930 11
   *
931 11
   * @param {string|number} value The int as a number or string.
932 11
   * @returns {Int}
933 11
   *
934 11
   * @example
935 11
   * const {Spanner} = require('@google-cloud/spanner');
936 11
   * const int = Spanner.int(10);
937 11
   */
938 11
  static int(value): Int {
939 11
    return new codec.Int(value);
940 11
  }
941 11

942 11
  /**
943 11
   * Helper function to get a Cloud Spanner Struct object.
944 11
   *
945 11
   * @param {object} value The struct as a JSON object.
946 11
   * @returns {Struct}
947 11
   *
948 11
   * @example
949 11
   * const {Spanner} = require('@google-cloud/spanner');
950 11
   * const struct = Spanner.struct({
951 11
   *   user: 'bob',
952 11
   *   age: 32
953 11
   * });
954 11
   */
955 11
  static struct(value?): Struct {
956 11
    if (Array.isArray(value)) {
957 11
      return codec.Struct.fromArray(value);
958 11
    }
959 11
    return codec.Struct.fromJSON(value);
960 11
  }
961 11
}
962 11

963 11
/*! Developer Documentation
964 11
 *
965 11
 * All async methods (except for streams) will return a Promise in the event
966 11
 * that a callback is omitted.
967 11
 */
968 11
promisifyAll(Spanner, {
969 11
  exclude: [
970 11
    'date',
971 11
    'float',
972 11
    'getInstanceConfigs',
973 11
    'instance',
974 11
    'int',
975 11
    'operation',
976 11
    'timestamp',
977 11
  ],
978 11
});
979 11

980 11
/**
981 11
 * The default export of the `@google-cloud/spanner` package is the
982 11
 * {@link Spanner} class.
983 11
 *
984 11
 * See {@link Spanner} and {@link ClientConfig} for client methods and
985 11
 * configuration options.
986 11
 *
987 11
 * @module {constructor} @google-cloud/spanner
988 11
 * @alias nodejs-spanner
989 11
 *
990 11
 * @example <caption>Install the client library with <a
991 11
 * href="https://www.npmjs.com/">npm</a>:</caption> npm install --save
992 11
 * @google-cloud/spanner
993 11
 *
994 11
 * @example <caption>Import the client library</caption>
995 11
 * const {Spanner} = require('@google-cloud/spanner');
996 11
 *
997 11
 * @example <caption>Create a client that uses <a
998 11
 * href="https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application">Application
999 11
 * Default Credentials (ADC)</a>:</caption> const client = new Spanner();
1000 11
 *
1001 11
 * @example <caption>Create a client with <a
1002 11
 * href="https://cloud.google.com/docs/authentication/production#obtaining_and_providing_service_account_credentials_manually">explicit
1003 11
 * credentials</a>:</caption> const client = new Spanner({ projectId:
1004 11
 * 'your-project-id', keyFilename: '/path/to/keyfile.json'
1005 11
 * });
1006 11
 *
1007 11
 * @example <caption>include:samples/quickstart.js</caption>
1008 11
 * region_tag:spanner_quickstart
1009 11
 * Full quickstart example:
1010 11
 */
1011 11
export {Spanner};
1012 11

1013 11
/**
1014 11
 * {@link Instance} class.
1015 11
 *
1016 11
 * @name Spanner.Instance
1017 11
 * @see Instance
1018 11
 * @type {Constructor}
1019 11
 */
1020 11
export {Instance};
1021 11

1022 11
/**
1023 11
 * {@link Database} class.
1024 11
 *
1025 11
 * @name Spanner.Database
1026 11
 * @see Database
1027 11
 * @type {Constructor}
1028 11
 */
1029 11
export {Database};
1030 11

1031 11
/**
1032 11
 * {@link Backup} class.
1033 11
 *
1034 11
 * @name Spanner.Backup
1035 11
 * @see Backup
1036 11
 * @type {Constructor}
1037 11
 */
1038 11
export {Backup};
1039 11

1040 11
/**
1041 11
 * {@link Session} class.
1042 11
 *
1043 11
 * @name Spanner.Session
1044 11
 * @see Session
1045 11
 * @type {Constructor}
1046 11
 */
1047 11
export {Session};
1048 11

1049 11
/**
1050 11
 * {@link SessionPool} class.
1051 11
 *
1052 11
 * @name Spanner.SessionPool
1053 11
 * @see SessionPool
1054 11
 * @type {Constructor}
1055 11
 */
1056 11
export {SessionPool};
1057 11

1058 11
/**
1059 11
 * {@link Table} class.
1060 11
 *
1061 11
 * @name Spanner.Table
1062 11
 * @see Table
1063 11
 * @type {Constructor}
1064 11
 */
1065 11
export {Table};
1066 11

1067 11
/**
1068 11
 * {@link PartitionedDml} class.
1069 11
 *
1070 11
 * @name Spanner.PartitionedDml
1071 11
 * @see PartitionedDml
1072 11
 * @type {Constructor}
1073 11
 */
1074 11
export {PartitionedDml};
1075 11

1076 11
/**
1077 11
 * {@link Snapshot} class.
1078 11
 *
1079 11
 * @name Spanner.Snapshot
1080 11
 * @see Snapshot
1081 11
 * @type {Constructor}
1082 11
 */
1083 11
export {Snapshot};
1084 11

1085 11
/**
1086 11
 * {@link Transaction} class.
1087 11
 *
1088 11
 * @name Spanner.Transaction
1089 11
 * @see Transaction
1090 11
 * @type {Constructor}
1091 11
 */
1092 11
export {Transaction};
1093 11

1094 11
/**
1095 11
 * @type {object}
1096 11
 * @property {constructor} DatabaseAdminClient
1097 11
 *   Reference to {@link v1.DatabaseAdminClient}
1098 11
 * @property {constructor} InstanceAdminClient
1099 11
 *   Reference to {@link v1.InstanceAdminClient}
1100 11
 * @property {constructor} SpannerClient
1101 11
 *   Reference to {@link v1.SpannerClient}
1102 11
 */
1103 11
import * as protos from '../protos/protos';
1104 11
export {v1, protos};
1105 11
export default {Spanner};

Read our documentation on viewing source code .

Loading