Skip to content
This repository was archived by the owner on Mar 4, 2026. It is now read-only.

Commit 4364d2b

Browse files
authored
fix: replace projectId placeholder in formatted names (#1407)
The `{{projectId}}` placeholder was only replaced in the names of the objects that were included in the requests, and not in the headers and formatted names. This would cause the `CLOUD_RESOURCE_HEADER` to be filled with the `{{projectId}}` placeholder instead of the actual value. As the actual project ID is gotten through an async method, and the constructor and `instance` methods are synchronous, we can only replace the actual values once it is retrieved for the first request. Fixes #1375
1 parent 6496fe1 commit 4364d2b

6 files changed

Lines changed: 199 additions & 18 deletions

File tree

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,13 @@ Samples are in the [`samples/`](https://github.com/googleapis/nodejs-spanner/tre
108108
| Datatypes | [source code](https://github.com/googleapis/nodejs-spanner/blob/master/samples/datatypes.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/datatypes.js,samples/README.md) |
109109
| DML | [source code](https://github.com/googleapis/nodejs-spanner/blob/master/samples/dml.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/dml.js,samples/README.md) |
110110
| Get-commit-stats | [source code](https://github.com/googleapis/nodejs-spanner/blob/master/samples/get-commit-stats.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/get-commit-stats.js,samples/README.md) |
111+
| Creates a new value-storing index | [source code](https://github.com/googleapis/nodejs-spanner/blob/master/samples/index-create-storing.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/index-create-storing.js,samples/README.md) |
112+
| Creates a new index | [source code](https://github.com/googleapis/nodejs-spanner/blob/master/samples/index-create.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/index-create.js,samples/README.md) |
113+
| Executes a read-only SQL query using an existing index. | [source code](https://github.com/googleapis/nodejs-spanner/blob/master/samples/index-query-data.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/index-query-data.js,samples/README.md) |
114+
| Reads data using an existing storing index. | [source code](https://github.com/googleapis/nodejs-spanner/blob/master/samples/index-read-data-with-storing.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/index-read-data-with-storing.js,samples/README.md) |
115+
| Read data using an existing index. | [source code](https://github.com/googleapis/nodejs-spanner/blob/master/samples/index-read-data.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/index-read-data.js,samples/README.md) |
111116
| Indexing | [source code](https://github.com/googleapis/nodejs-spanner/blob/master/samples/indexing.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/indexing.js,samples/README.md) |
117+
| Instance-with-processing-units | [source code](https://github.com/googleapis/nodejs-spanner/blob/master/samples/instance-with-processing-units.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/instance-with-processing-units.js,samples/README.md) |
112118
| Instance | [source code](https://github.com/googleapis/nodejs-spanner/blob/master/samples/instance.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/instance.js,samples/README.md) |
113119
| Numeric-add-column | [source code](https://github.com/googleapis/nodejs-spanner/blob/master/samples/numeric-add-column.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/numeric-add-column.js,samples/README.md) |
114120
| Numeric-query-parameter | [source code](https://github.com/googleapis/nodejs-spanner/blob/master/samples/numeric-query-parameter.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/numeric-query-parameter.js,samples/README.md) |

samples/README.md

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,13 @@ and automatic, synchronous replication for high availability.
3232
* [Datatypes](#datatypes)
3333
* [DML](#dml)
3434
* [Get-commit-stats](#get-commit-stats)
35+
* [Creates a new value-storing index](#creates-a-new-value-storing-index)
36+
* [Creates a new index](#creates-a-new-index)
37+
* [Executes a read-only SQL query using an existing index.](#executes-a-read-only-sql-query-using-an-existing-index.)
38+
* [Reads data using an existing storing index.](#reads-data-using-an-existing-storing-index.)
39+
* [Read data using an existing index.](#read-data-using-an-existing-index.)
3540
* [Indexing](#indexing)
41+
* [Instance-with-processing-units](#instance-with-processing-units)
3642
* [Instance](#instance)
3743
* [Numeric-add-column](#numeric-add-column)
3844
* [Numeric-query-parameter](#numeric-query-parameter)
@@ -366,6 +372,91 @@ __Usage:__
366372

367373

368374

375+
### Creates a new value-storing index
376+
377+
View the [source code](https://github.com/googleapis/nodejs-spanner/blob/master/samples/index-create-storing.js).
378+
379+
[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/index-create-storing.js,samples/README.md)
380+
381+
__Usage:__
382+
383+
384+
`node createStoringIndex <INSTANCE_ID> <DATABASE_ID> <PROJECT_ID>`
385+
386+
387+
-----
388+
389+
390+
391+
392+
### Creates a new index
393+
394+
View the [source code](https://github.com/googleapis/nodejs-spanner/blob/master/samples/index-create.js).
395+
396+
[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/index-create.js,samples/README.md)
397+
398+
__Usage:__
399+
400+
401+
`node createIndex <INSTANCE_ID> <DATABASE_ID> <PROJECT_ID>`
402+
403+
404+
-----
405+
406+
407+
408+
409+
### Executes a read-only SQL query using an existing index.
410+
411+
View the [source code](https://github.com/googleapis/nodejs-spanner/blob/master/samples/index-query-data.js).
412+
413+
[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/index-query-data.js,samples/README.md)
414+
415+
__Usage:__
416+
417+
418+
`node queryDataWithIndex <INSTANCE_ID> <DATABASE_ID> <PROJECT_ID> <START_TITLE> <END_TITLE>`
419+
420+
421+
-----
422+
423+
424+
425+
426+
### Reads data using an existing storing index.
427+
428+
View the [source code](https://github.com/googleapis/nodejs-spanner/blob/master/samples/index-read-data-with-storing.js).
429+
430+
[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/index-read-data-with-storing.js,samples/README.md)
431+
432+
__Usage:__
433+
434+
435+
`node readDataWithStoringIndex <INSTANCE_ID> <DATABASE_ID> <PROJECT_ID>`
436+
437+
438+
-----
439+
440+
441+
442+
443+
### Read data using an existing index.
444+
445+
View the [source code](https://github.com/googleapis/nodejs-spanner/blob/master/samples/index-read-data.js).
446+
447+
[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/index-read-data.js,samples/README.md)
448+
449+
__Usage:__
450+
451+
452+
`node readDataWithIndex <INSTANCE_ID> <DATABASE_ID> <PROJECT_ID>`
453+
454+
455+
-----
456+
457+
458+
459+
369460
### Indexing
370461

371462
View the [source code](https://github.com/googleapis/nodejs-spanner/blob/master/samples/indexing.js).
@@ -383,6 +474,23 @@ __Usage:__
383474

384475

385476

477+
### Instance-with-processing-units
478+
479+
View the [source code](https://github.com/googleapis/nodejs-spanner/blob/master/samples/instance-with-processing-units.js).
480+
481+
[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/instance-with-processing-units.js,samples/README.md)
482+
483+
__Usage:__
484+
485+
486+
`node samples/instance-with-processing-units.js`
487+
488+
489+
-----
490+
491+
492+
493+
386494
### Instance
387495

388496
View the [source code](https://github.com/googleapis/nodejs-spanner/blob/master/samples/instance.js).

src/index.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ class Spanner extends GrpcService {
158158
auth: GoogleAuth;
159159
clients_: Map<string, {}>;
160160
instances_: Map<string, Instance>;
161+
projectIdReplaced_: boolean;
161162
projectFormattedName_: string;
162163
resourceHeader_: {[k: string]: string};
163164

@@ -260,6 +261,7 @@ class Spanner extends GrpcService {
260261
this.auth = new GoogleAuth(this.options);
261262
this.clients_ = new Map();
262263
this.instances_ = new Map();
264+
this.projectIdReplaced_ = false;
263265
this.projectFormattedName_ = 'projects/' + this.projectId;
264266
this.resourceHeader_ = {
265267
[CLOUD_RESOURCE_HEADER]: this.projectFormattedName_,
@@ -889,6 +891,34 @@ class Spanner extends GrpcService {
889891
const gaxClient = this.clients_.get(clientName)!;
890892
let reqOpts = extend(true, {}, config.reqOpts);
891893
reqOpts = replaceProjectIdToken(reqOpts, projectId!);
894+
// It would have been preferable to replace the projectId already in the
895+
// constructor of Spanner, but that is not possible as auth.getProjectId
896+
// is an async method. This is therefore the first place where we have
897+
// access to the value that should be used instead of the placeholder.
898+
if (!this.projectIdReplaced_) {
899+
this.projectId = replaceProjectIdToken(this.projectId, projectId!);
900+
this.projectFormattedName_ = replaceProjectIdToken(
901+
this.projectFormattedName_,
902+
projectId!
903+
);
904+
this.instances_.forEach(instance => {
905+
instance.formattedName_ = replaceProjectIdToken(
906+
instance.formattedName_,
907+
projectId!
908+
);
909+
instance.databases_.forEach(database => {
910+
database.formattedName_ = replaceProjectIdToken(
911+
database.formattedName_,
912+
projectId!
913+
);
914+
});
915+
});
916+
this.projectIdReplaced_ = true;
917+
}
918+
config.headers[CLOUD_RESOURCE_HEADER] = replaceProjectIdToken(
919+
config.headers[CLOUD_RESOURCE_HEADER],
920+
projectId!
921+
);
892922
const requestFn = gaxClient[config.method].bind(
893923
gaxClient,
894924
reqOpts,

test/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1400,8 +1400,10 @@ describe('Spanner', () => {
14001400
const replacedReqOpts = {};
14011401

14021402
replaceProjectIdTokenOverride = (reqOpts, projectId) => {
1403-
assert.deepStrictEqual(reqOpts, CONFIG.reqOpts);
1404-
assert.notStrictEqual(reqOpts, CONFIG.reqOpts);
1403+
if (typeof reqOpts === 'object') {
1404+
assert.deepStrictEqual(reqOpts, CONFIG.reqOpts);
1405+
assert.notStrictEqual(reqOpts, CONFIG.reqOpts);
1406+
}
14051407
assert.strictEqual(projectId, PROJECT_ID);
14061408
return replacedReqOpts;
14071409
};

test/mockserver/mockspanner.ts

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import * as path from 'path';
1818
import {google} from '../../protos/protos';
1919
import {grpc} from 'google-gax';
2020
import * as protoLoader from '@grpc/proto-loader';
21+
// eslint-disable-next-line node/no-extraneous-import
22+
import {Metadata} from '@grpc/grpc-js';
2123
import {Transaction} from '../../src';
2224
import protobuf = google.spanner.v1;
2325
import Timestamp = google.protobuf.Timestamp;
@@ -221,6 +223,7 @@ interface Request {}
221223
*/
222224
export class MockSpanner {
223225
private requests: Request[] = [];
226+
private metadata: Metadata[] = [];
224227
private frozen = 0;
225228
private sessionCounter = 0;
226229
private sessions: Map<string, protobuf.Session> = new Map<
@@ -274,6 +277,7 @@ export class MockSpanner {
274277

275278
resetRequests(): void {
276279
this.requests = [];
280+
this.metadata = [];
277281
}
278282

279283
/**
@@ -283,6 +287,13 @@ export class MockSpanner {
283287
return this.requests;
284288
}
285289

290+
/**
291+
* @return the metadata that have been received by this mock server.
292+
*/
293+
getMetadata(): Metadata[] {
294+
return this.metadata;
295+
}
296+
286297
/**
287298
* Registers a result for an SQL statement on the server.
288299
* @param sql The SQL statement that should return the result.
@@ -417,14 +428,19 @@ export class MockSpanner {
417428
return undefined;
418429
}
419430

431+
private pushRequest(request: Request, metadata: Metadata): void {
432+
this.requests.push(request);
433+
this.metadata.push(metadata);
434+
}
435+
420436
batchCreateSessions(
421437
call: grpc.ServerUnaryCall<
422438
protobuf.BatchCreateSessionsRequest,
423439
protobuf.BatchCreateSessionsResponse
424440
>,
425441
callback: protobuf.Spanner.BatchCreateSessionsCallback
426442
) {
427-
this.requests.push(call.request!);
443+
this.pushRequest(call.request!, call.metadata);
428444
this.simulateExecutionTime(this.batchCreateSessions.name)
429445
.then(() => {
430446
const sessions = new Array<protobuf.Session>();
@@ -445,7 +461,7 @@ export class MockSpanner {
445461
call: grpc.ServerUnaryCall<protobuf.CreateSessionRequest, protobuf.Session>,
446462
callback: protobuf.Spanner.CreateSessionCallback
447463
) {
448-
this.requests.push(call.request!);
464+
this.pushRequest(call.request!, call.metadata);
449465
this.simulateExecutionTime(this.createSession.name).then(() => {
450466
callback(null, this.newSession(call.request!.database));
451467
});
@@ -455,7 +471,7 @@ export class MockSpanner {
455471
call: grpc.ServerUnaryCall<protobuf.GetSessionRequest, protobuf.Session>,
456472
callback: protobuf.Spanner.GetSessionCallback
457473
) {
458-
this.requests.push(call.request!);
474+
this.pushRequest(call.request!, call.metadata);
459475
this.simulateExecutionTime(this.getSession.name).then(() => {
460476
const session = this.sessions[call.request!.name];
461477
if (session) {
@@ -473,7 +489,7 @@ export class MockSpanner {
473489
>,
474490
callback: protobuf.Spanner.ListSessionsCallback
475491
) {
476-
this.requests.push(call.request!);
492+
this.pushRequest(call.request!, call.metadata);
477493
this.simulateExecutionTime(this.listSessions.name).then(() => {
478494
callback(
479495
null,
@@ -493,7 +509,7 @@ export class MockSpanner {
493509
>,
494510
callback: protobuf.Spanner.DeleteSessionCallback
495511
) {
496-
this.requests.push(call.request!);
512+
this.pushRequest(call.request!, call.metadata);
497513
if (this.sessions.delete(call.request!.name)) {
498514
callback(null, google.protobuf.Empty.create());
499515
} else {
@@ -505,7 +521,7 @@ export class MockSpanner {
505521
call: grpc.ServerUnaryCall<protobuf.ExecuteSqlRequest, {}>,
506522
callback: protobuf.Spanner.ExecuteSqlCallback
507523
) {
508-
this.requests.push(call.request!);
524+
this.pushRequest(call.request!, call.metadata);
509525
callback(createUnimplementedError('ExecuteSql is not yet implemented'));
510526
}
511527

@@ -515,7 +531,7 @@ export class MockSpanner {
515531
protobuf.PartialResultSet
516532
>
517533
) {
518-
this.requests.push(call.request!);
534+
this.pushRequest(call.request!, call.metadata);
519535
this.simulateExecutionTime(this.executeStreamingSql.name)
520536
.then(() => {
521537
if (call.request!.transaction && call.request!.transaction.id) {
@@ -687,7 +703,7 @@ export class MockSpanner {
687703
>,
688704
callback: protobuf.Spanner.ExecuteBatchDmlCallback
689705
) {
690-
this.requests.push(call.request!);
706+
this.pushRequest(call.request!, call.metadata);
691707
this.simulateExecutionTime(this.executeBatchDml.name)
692708
.then(() => {
693709
if (call.request!.transaction && call.request!.transaction.id) {
@@ -778,12 +794,12 @@ export class MockSpanner {
778794
call: grpc.ServerUnaryCall<protobuf.ReadRequest, {}>,
779795
callback: protobuf.Spanner.ReadCallback
780796
) {
781-
this.requests.push(call.request!);
797+
this.pushRequest(call.request!, call.metadata);
782798
callback(createUnimplementedError('Read is not yet implemented'));
783799
}
784800

785801
streamingRead(call: grpc.ServerWritableStream<protobuf.ReadRequest, {}>) {
786-
this.requests.push(call.request!);
802+
this.pushRequest(call.request!, call.metadata);
787803
call.emit(
788804
'error',
789805
createUnimplementedError('StreamingRead is not yet implemented')
@@ -798,7 +814,7 @@ export class MockSpanner {
798814
>,
799815
callback: protobuf.Spanner.BeginTransactionCallback
800816
) {
801-
this.requests.push(call.request!);
817+
this.pushRequest(call.request!, call.metadata);
802818
this.simulateExecutionTime(this.beginTransaction.name)
803819
.then(() => {
804820
const session = this.sessions.get(call.request!.session);
@@ -838,7 +854,7 @@ export class MockSpanner {
838854
call: grpc.ServerUnaryCall<protobuf.CommitRequest, protobuf.CommitResponse>,
839855
callback: protobuf.Spanner.CommitCallback
840856
) {
841-
this.requests.push(call.request!);
857+
this.pushRequest(call.request!, call.metadata);
842858
this.simulateExecutionTime(this.commit.name)
843859
.then(() => {
844860
if (call.request!.transactionId) {
@@ -888,7 +904,7 @@ export class MockSpanner {
888904
call: grpc.ServerUnaryCall<protobuf.RollbackRequest, google.protobuf.Empty>,
889905
callback: protobuf.Spanner.RollbackCallback
890906
) {
891-
this.requests.push(call.request!);
907+
this.pushRequest(call.request!, call.metadata);
892908
const session = this.sessions.get(call.request!.session);
893909
if (session) {
894910
const buffer = Buffer.from(call.request!.transactionId as string);
@@ -911,15 +927,15 @@ export class MockSpanner {
911927
call: grpc.ServerUnaryCall<protobuf.PartitionQueryRequest, {}>,
912928
callback: protobuf.Spanner.PartitionQueryCallback
913929
) {
914-
this.requests.push(call.request!);
930+
this.pushRequest(call.request!, call.metadata);
915931
callback(createUnimplementedError('PartitionQuery is not yet implemented'));
916932
}
917933

918934
partitionRead(
919935
call: grpc.ServerUnaryCall<protobuf.PartitionReadRequest, {}>,
920936
callback: protobuf.Spanner.PartitionReadCallback
921937
) {
922-
this.requests.push(call.request!);
938+
this.pushRequest(call.request!, call.metadata);
923939
callback(createUnimplementedError('PartitionQuery is not yet implemented'));
924940
}
925941
}

0 commit comments

Comments
 (0)