Skip to content

Commit af60f7c

Browse files
committed
feat(spanner): support for type UUID
1 parent 444ed5c commit af60f7c

File tree

4 files changed

+112
-1
lines changed

4 files changed

+112
-1
lines changed

src/codec.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,20 @@ export class SpannerDate extends Date {
142142
}
143143
}
144144

145+
/**
146+
* @typedef UUID
147+
* @see Spanner.uuid
148+
*/
149+
export class UUID {
150+
value: string;
151+
constructor(value: string) {
152+
this.value = value;
153+
}
154+
valueOf(): string {
155+
return String(this.value);
156+
}
157+
}
158+
145159
/**
146160
* Using an abstract class to simplify checking for wrapped numbers.
147161
*
@@ -529,6 +543,10 @@ function decode(
529543
enumObject: columnMetadata as object,
530544
});
531545
break;
546+
case spannerClient.spanner.v1.TypeCode.UUID:
547+
case 'UUID':
548+
decoded = new UUID(decoded);
549+
break;
532550
case spannerClient.spanner.v1.TypeCode.FLOAT32:
533551
case 'FLOAT32':
534552
decoded = new Float32(decoded);
@@ -665,6 +683,10 @@ function encodeValue(value: Value): Value {
665683
return value.value;
666684
}
667685

686+
if (value instanceof UUID) {
687+
return value.value;
688+
}
689+
668690
if (value instanceof Struct) {
669691
return Array.from(value).map(field => encodeValue(field.value));
670692
}
@@ -697,6 +719,7 @@ const TypeCode: {
697719
bool: 'BOOL',
698720
int64: 'INT64',
699721
pgOid: 'INT64',
722+
uuid: 'UUID',
700723
float32: 'FLOAT32',
701724
float64: 'FLOAT64',
702725
numeric: 'NUMERIC',
@@ -774,6 +797,10 @@ function getType(value: Value): Type {
774797
const isSpecialNumber =
775798
is.infinite(value) || (is.number(value) && isNaN(value));
776799

800+
if (value instanceof UUID) {
801+
return {type: 'uuid'};
802+
}
803+
777804
if (value instanceof Float32) {
778805
return {type: 'float32'};
779806
}
@@ -969,6 +996,7 @@ export const codec = {
969996
convertProtoTimestampToDate,
970997
createTypeObject,
971998
SpannerDate,
999+
UUID,
9721000
Float32,
9731001
Float,
9741002
Int,

src/index.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import * as streamEvents from 'stream-events';
2626
import * as through from 'through2';
2727
import {
2828
codec,
29+
UUID,
2930
Float32,
3031
Float,
3132
Int,
@@ -1704,6 +1705,23 @@ class Spanner extends GrpcService {
17041705
return new PreciseDate(value as number);
17051706
}
17061707

1708+
/**
1709+
* Helper function to get a Cloud Spanner UUID object.
1710+
*
1711+
* @param {string} value The uuid as a string.
1712+
* @returns {UUID}
1713+
*
1714+
* @example
1715+
* ```
1716+
* const {Spanner} = require('@google-cloud/spanner');
1717+
* const value = uuidv4();
1718+
* const uuid = Spanner.uuid(value);
1719+
* ```
1720+
*/
1721+
static uuid(value): UUID {
1722+
return new codec.UUID(value);
1723+
}
1724+
17071725
/**
17081726
* Helper function to get a Cloud Spanner Float32 object.
17091727
*
@@ -1891,6 +1909,7 @@ class Spanner extends GrpcService {
18911909
promisifyAll(Spanner, {
18921910
exclude: [
18931911
'date',
1912+
'uuid',
18941913
'float32',
18951914
'float',
18961915
'instance',
@@ -2070,5 +2089,5 @@ import * as protos from '../protos/protos';
20702089
import IInstanceConfig = instanceAdmin.spanner.admin.instance.v1.IInstanceConfig;
20712090
export {v1, protos};
20722091
export default {Spanner};
2073-
export {Float32, Float, Int, Struct, Numeric, PGNumeric, SpannerDate};
2092+
export {UUID, Float32, Float, Int, Struct, Numeric, PGNumeric, SpannerDate};
20742093
export {ObservabilityOptions};

test/codec.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {GrpcService} from '../src/common-grpc/service';
2424
import {google} from '../protos/protos';
2525
import {GoogleError} from 'google-gax';
2626
import {util} from 'protobufjs';
27+
import {v4 as uuidv4} from 'uuid';
2728
import Long = util.Long;
2829
const singer = require('./data/singer');
2930
const music = singer.examples.spanner.music;
@@ -152,6 +153,22 @@ describe('codec', () => {
152153
});
153154
});
154155

156+
describe('UUID', () => {
157+
it('should store the value', () => {
158+
const value = uuidv4();
159+
const uuid = new codec.UUID(value);
160+
161+
assert.strictEqual(uuid.value, value);
162+
});
163+
164+
it('should return as a uuid', () => {
165+
const value = uuidv4();
166+
const uuid = new codec.UUID(value);
167+
168+
assert.strictEqual(uuid.valueOf(), String(value));
169+
});
170+
});
171+
155172
describe('Float', () => {
156173
it('should store the value', () => {
157174
const value = 8;
@@ -680,6 +697,17 @@ describe('codec', () => {
680697
assert.deepStrictEqual(decoded, expected);
681698
});
682699

700+
it('should decode UUID', () => {
701+
const value = uuidv4();
702+
703+
const decoded = codec.decode(value, {
704+
code: google.spanner.v1.TypeCode.UUID,
705+
});
706+
707+
assert(decoded instanceof codec.UUID);
708+
assert.strictEqual(decoded.value, value);
709+
});
710+
683711
it.skip('should decode FLOAT32', () => {
684712
const value = 'Infinity';
685713

@@ -1070,6 +1098,15 @@ describe('codec', () => {
10701098
assert.strictEqual(encoded, '10');
10711099
});
10721100

1101+
it('should encode UUID', () => {
1102+
const random = uuidv4();
1103+
const value = new codec.UUID(random);
1104+
1105+
const encoded = codec.encode(value);
1106+
1107+
assert.strictEqual(encoded, random);
1108+
});
1109+
10731110
it.skip('should encode FLOAT32', () => {
10741111
const value = new codec.Float32(10);
10751112

@@ -1165,6 +1202,12 @@ describe('codec', () => {
11651202
});
11661203
});
11671204

1205+
it('should determine if the value is a uuid', () => {
1206+
assert.deepStrictEqual(codec.getType(new codec.UUID(uuidv4())), {
1207+
type: 'uuid',
1208+
});
1209+
});
1210+
11681211
it.skip('should determine if the value is a float32', () => {
11691212
assert.deepStrictEqual(codec.getType(new codec.Float32(1.1)), {
11701213
type: 'float32',
@@ -1317,6 +1360,9 @@ describe('codec', () => {
13171360
google.spanner.v1.TypeCode.TYPE_CODE_UNSPECIFIED
13181361
],
13191362
},
1363+
uuid: {
1364+
code: google.spanner.v1.TypeCode[google.spanner.v1.TypeCode.UUID],
1365+
},
13201366
bool: {
13211367
code: google.spanner.v1.TypeCode[google.spanner.v1.TypeCode.BOOL],
13221368
},

test/index.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ const fakePfy = extend({}, pfy, {
8484
promisified = true;
8585
assert.deepStrictEqual(options.exclude, [
8686
'date',
87+
'uuid',
8788
'float32',
8889
'float',
8990
'instance',
@@ -517,6 +518,23 @@ describe('Spanner', () => {
517518
});
518519
});
519520

521+
describe('uuid', () => {
522+
it('should create a UUID instance', () => {
523+
const value = {};
524+
const customValue = {};
525+
526+
fakeCodec.UUID = class {
527+
constructor(value_) {
528+
assert.strictEqual(value_, value);
529+
return customValue;
530+
}
531+
};
532+
533+
const uuid = Spanner.uuid(value);
534+
assert.strictEqual(uuid, customValue);
535+
});
536+
});
537+
520538
describe.skip('float32', () => {
521539
it('should create a Float32 instance', () => {
522540
const value = {};

0 commit comments

Comments
 (0)