Skip to content

Commit e2fe5b7

Browse files
authored
feat(spanner): add emulator support for the admin client autogenerated API samples (#1994)
This PR introduces emulator support for the API samples, which are using admin client autogenerated methods. **Sample usage** For Instance samples: ``` const {Spanner} = require('@google-cloud/spanner'); const spanner = new Spanner({ projectId: projectId, }); const instanceAdminClient = spanner.get_instance_admin_client(); ``` For Database samples: ``` const {Spanner} = require('@google-cloud/spanner'); const spanner = new Spanner({ projectId: projectID, }); const databaseAdminClient = spanner.get_database_admin_client(); ```
1 parent 53e529f commit e2fe5b7

File tree

4 files changed

+169
-1
lines changed

4 files changed

+169
-1
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ system-test/*key.json
1313
package-lock.json
1414
__pycache__
1515
.idea
16+
.vscode

src/index.ts

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,13 @@ import {
4646
CreateInstanceConfigCallback,
4747
CreateInstanceConfigResponse,
4848
} from './instance-config';
49-
import {grpc, GrpcClientOptions, CallOptions, GoogleError} from 'google-gax';
49+
import {
50+
grpc,
51+
GrpcClientOptions,
52+
CallOptions,
53+
GoogleError,
54+
ClientOptions,
55+
} from 'google-gax';
5056
import {google, google as instanceAdmin} from '../protos/protos';
5157
import {
5258
PagedOptions,
@@ -347,6 +353,54 @@ class Spanner extends GrpcService {
347353
this.directedReadOptions = directedReadOptions;
348354
}
349355

356+
/**
357+
* Gets the InstanceAdminClient object.
358+
* The returned InstanceAdminClient object is a shared, managed instance and should not be manually closed.
359+
* @returns {v1.InstanceAdminClient} The InstanceAdminClient object
360+
* @example
361+
* ```
362+
* const {Spanner} = require('@google-cloud/spanner');
363+
* const spanner = new Spanner({
364+
* projectId: projectId,
365+
* });
366+
* const instanceAdminClient = spanner.getInstanceAdminClient();
367+
* ```
368+
*/
369+
getInstanceAdminClient(): v1.InstanceAdminClient {
370+
const clientName = 'InstanceAdminClient';
371+
if (!this.clients_.has(clientName)) {
372+
this.clients_.set(
373+
clientName,
374+
new v1[clientName](this.options as ClientOptions)
375+
);
376+
}
377+
return this.clients_.get(clientName)! as v1.InstanceAdminClient;
378+
}
379+
380+
/**
381+
* Gets the DatabaseAdminClient object.
382+
* The returned DatabaseAdminClient object is a managed, shared instance and should not be manually closed.
383+
* @returns {v1.DatabaseAdminClient} The DatabaseAdminClient object.
384+
* @example
385+
* ```
386+
* const {Spanner} = require('@google-cloud/spanner');
387+
* const spanner = new Spanner({
388+
* projectId: projectId,
389+
* });
390+
* const databaseAdminClient = spanner.getDatabaseAdminClient();
391+
* ```
392+
*/
393+
getDatabaseAdminClient(): v1.DatabaseAdminClient {
394+
const clientName = 'DatabaseAdminClient';
395+
if (!this.clients_.has(clientName)) {
396+
this.clients_.set(
397+
clientName,
398+
new v1[clientName](this.options as ClientOptions)
399+
);
400+
}
401+
return this.clients_.get(clientName)! as v1.DatabaseAdminClient;
402+
}
403+
350404
/** Closes this Spanner client and cleans up all resources used by it. */
351405
close(): void {
352406
this.clients_.forEach(c => {
@@ -1741,6 +1795,8 @@ promisifyAll(Spanner, {
17411795
'pgJsonb',
17421796
'operation',
17431797
'timestamp',
1798+
'getInstanceAdminClient',
1799+
'getDatabaseAdminClient',
17441800
],
17451801
});
17461802

system-test/spanner.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,115 @@ describe('Spanner', () => {
231231
);
232232
});
233233

234+
describe('Autogenerated Admin Client', async () => {
235+
const projectId = process.env.GCLOUD_PROJECT;
236+
const instanceId = envInstanceName
237+
? envInstanceName
238+
: generateName('instance');
239+
const DATABASE = generateName('database');
240+
const instanceAdminClient = spanner.getInstanceAdminClient();
241+
const databaseAdminClient = spanner.getDatabaseAdminClient();
242+
243+
before(async () => {
244+
assert(projectId);
245+
if (generateInstanceForTest) {
246+
const [operation] = await instanceAdminClient.createInstance({
247+
parent: instanceAdminClient.projectPath(projectId),
248+
instanceId: instanceId,
249+
instance: {
250+
config: instanceAdminClient.instanceConfigPath(
251+
projectId,
252+
'regional-us-central1'
253+
),
254+
nodeCount: 1,
255+
displayName: instanceId,
256+
labels: {
257+
cloud_spanner_samples: 'true',
258+
created: Math.round(Date.now() / 1000).toString(), // current time
259+
},
260+
},
261+
});
262+
const [instance] = await operation.promise();
263+
RESOURCES_TO_CLEAN.push(instance as Instance);
264+
} else {
265+
console.log(
266+
`Not creating temp instance, using + ${instanceAdminClient.instancePath(
267+
projectId,
268+
envInstanceName
269+
)}...`
270+
);
271+
}
272+
const [operation] = await databaseAdminClient.createDatabase({
273+
createStatement: 'CREATE DATABASE `' + DATABASE + '`',
274+
extraStatements: [
275+
`CREATE TABLE ${TABLE_NAME} (
276+
SingerId STRING(1024) NOT NULL,
277+
Name STRING(1024),
278+
) PRIMARY KEY(SingerId)`,
279+
],
280+
parent: databaseAdminClient.instancePath(projectId, instanceId),
281+
});
282+
await operation.promise();
283+
});
284+
285+
describe('Instances', () => {
286+
it('should have created the instance', async () => {
287+
assert(projectId);
288+
try {
289+
const [metadata] = await instanceAdminClient.getInstance({
290+
name: instanceAdminClient.instancePath(projectId, instanceId),
291+
});
292+
assert.strictEqual(
293+
metadata!.name,
294+
instanceAdminClient.instancePath(projectId, instanceId)
295+
);
296+
} catch (err) {
297+
if (!err) {
298+
assert.ifError(err);
299+
}
300+
}
301+
});
302+
303+
it('should list the instances', async () => {
304+
assert(projectId);
305+
const [instances] = await instanceAdminClient.listInstances({
306+
parent: instanceAdminClient.projectPath(projectId),
307+
});
308+
assert(instances!.length > 0);
309+
});
310+
});
311+
312+
describe('Databases', () => {
313+
async function createDatabase(database, dialect) {
314+
assert(projectId);
315+
const [metadata] = await databaseAdminClient.getDatabase({
316+
name: databaseAdminClient.databasePath(
317+
projectId,
318+
instanceId,
319+
database
320+
),
321+
});
322+
assert.strictEqual(
323+
metadata!.name,
324+
databaseAdminClient.databasePath(projectId, instanceId, database)
325+
);
326+
assert.strictEqual(metadata!.state, 'READY');
327+
if (IS_EMULATOR_ENABLED) {
328+
assert.strictEqual(
329+
metadata!.databaseDialect,
330+
'DATABASE_DIALECT_UNSPECIFIED'
331+
);
332+
} else {
333+
assert.strictEqual(metadata!.databaseDialect, dialect);
334+
}
335+
}
336+
337+
it('GOOGLE_STANDARD_SQL should have created the database', async () => {
338+
createDatabase(DATABASE, 'GOOGLE_STANDARD_SQL');
339+
});
340+
});
341+
});
342+
234343
describe('types', () => {
235344
const TABLE_NAME = 'TypeCheck';
236345
const googleSqlTable = DATABASE.table(TABLE_NAME);

test/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ const fakePfy = extend({}, pfy, {
9191
'pgJsonb',
9292
'operation',
9393
'timestamp',
94+
'getInstanceAdminClient',
95+
'getDatabaseAdminClient',
9496
]);
9597
},
9698
});

0 commit comments

Comments
 (0)