From af60f7c550863d224f0aceaaf1a8d0a0184e19f0 Mon Sep 17 00:00:00 2001 From: Alka Trivedi Date: Sat, 8 Feb 2025 13:04:57 +0530 Subject: [PATCH 1/3] feat(spanner): support for type UUID --- src/codec.ts | 28 ++++++++++++++++++++++++++++ src/index.ts | 21 ++++++++++++++++++++- test/codec.ts | 46 ++++++++++++++++++++++++++++++++++++++++++++++ test/index.ts | 18 ++++++++++++++++++ 4 files changed, 112 insertions(+), 1 deletion(-) diff --git a/src/codec.ts b/src/codec.ts index 1c49d8599..3a49ccb07 100644 --- a/src/codec.ts +++ b/src/codec.ts @@ -142,6 +142,20 @@ export class SpannerDate extends Date { } } +/** + * @typedef UUID + * @see Spanner.uuid + */ +export class UUID { + value: string; + constructor(value: string) { + this.value = value; + } + valueOf(): string { + return String(this.value); + } +} + /** * Using an abstract class to simplify checking for wrapped numbers. * @@ -529,6 +543,10 @@ function decode( enumObject: columnMetadata as object, }); break; + case spannerClient.spanner.v1.TypeCode.UUID: + case 'UUID': + decoded = new UUID(decoded); + break; case spannerClient.spanner.v1.TypeCode.FLOAT32: case 'FLOAT32': decoded = new Float32(decoded); @@ -665,6 +683,10 @@ function encodeValue(value: Value): Value { return value.value; } + if (value instanceof UUID) { + return value.value; + } + if (value instanceof Struct) { return Array.from(value).map(field => encodeValue(field.value)); } @@ -697,6 +719,7 @@ const TypeCode: { bool: 'BOOL', int64: 'INT64', pgOid: 'INT64', + uuid: 'UUID', float32: 'FLOAT32', float64: 'FLOAT64', numeric: 'NUMERIC', @@ -774,6 +797,10 @@ function getType(value: Value): Type { const isSpecialNumber = is.infinite(value) || (is.number(value) && isNaN(value)); + if (value instanceof UUID) { + return {type: 'uuid'}; + } + if (value instanceof Float32) { return {type: 'float32'}; } @@ -969,6 +996,7 @@ export const codec = { convertProtoTimestampToDate, createTypeObject, SpannerDate, + UUID, Float32, Float, Int, diff --git a/src/index.ts b/src/index.ts index 9281484c9..bbdfa7c2a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -26,6 +26,7 @@ import * as streamEvents from 'stream-events'; import * as through from 'through2'; import { codec, + UUID, Float32, Float, Int, @@ -1704,6 +1705,23 @@ class Spanner extends GrpcService { return new PreciseDate(value as number); } + /** + * Helper function to get a Cloud Spanner UUID object. + * + * @param {string} value The uuid as a string. + * @returns {UUID} + * + * @example + * ``` + * const {Spanner} = require('@google-cloud/spanner'); + * const value = uuidv4(); + * const uuid = Spanner.uuid(value); + * ``` + */ + static uuid(value): UUID { + return new codec.UUID(value); + } + /** * Helper function to get a Cloud Spanner Float32 object. * @@ -1891,6 +1909,7 @@ class Spanner extends GrpcService { promisifyAll(Spanner, { exclude: [ 'date', + 'uuid', 'float32', 'float', 'instance', @@ -2070,5 +2089,5 @@ import * as protos from '../protos/protos'; import IInstanceConfig = instanceAdmin.spanner.admin.instance.v1.IInstanceConfig; export {v1, protos}; export default {Spanner}; -export {Float32, Float, Int, Struct, Numeric, PGNumeric, SpannerDate}; +export {UUID, Float32, Float, Int, Struct, Numeric, PGNumeric, SpannerDate}; export {ObservabilityOptions}; diff --git a/test/codec.ts b/test/codec.ts index 9a925be86..2f4d90fa3 100644 --- a/test/codec.ts +++ b/test/codec.ts @@ -24,6 +24,7 @@ import {GrpcService} from '../src/common-grpc/service'; import {google} from '../protos/protos'; import {GoogleError} from 'google-gax'; import {util} from 'protobufjs'; +import {v4 as uuidv4} from 'uuid'; import Long = util.Long; const singer = require('./data/singer'); const music = singer.examples.spanner.music; @@ -152,6 +153,22 @@ describe('codec', () => { }); }); + describe('UUID', () => { + it('should store the value', () => { + const value = uuidv4(); + const uuid = new codec.UUID(value); + + assert.strictEqual(uuid.value, value); + }); + + it('should return as a uuid', () => { + const value = uuidv4(); + const uuid = new codec.UUID(value); + + assert.strictEqual(uuid.valueOf(), String(value)); + }); + }); + describe('Float', () => { it('should store the value', () => { const value = 8; @@ -680,6 +697,17 @@ describe('codec', () => { assert.deepStrictEqual(decoded, expected); }); + it('should decode UUID', () => { + const value = uuidv4(); + + const decoded = codec.decode(value, { + code: google.spanner.v1.TypeCode.UUID, + }); + + assert(decoded instanceof codec.UUID); + assert.strictEqual(decoded.value, value); + }); + it.skip('should decode FLOAT32', () => { const value = 'Infinity'; @@ -1070,6 +1098,15 @@ describe('codec', () => { assert.strictEqual(encoded, '10'); }); + it('should encode UUID', () => { + const random = uuidv4(); + const value = new codec.UUID(random); + + const encoded = codec.encode(value); + + assert.strictEqual(encoded, random); + }); + it.skip('should encode FLOAT32', () => { const value = new codec.Float32(10); @@ -1165,6 +1202,12 @@ describe('codec', () => { }); }); + it('should determine if the value is a uuid', () => { + assert.deepStrictEqual(codec.getType(new codec.UUID(uuidv4())), { + type: 'uuid', + }); + }); + it.skip('should determine if the value is a float32', () => { assert.deepStrictEqual(codec.getType(new codec.Float32(1.1)), { type: 'float32', @@ -1317,6 +1360,9 @@ describe('codec', () => { google.spanner.v1.TypeCode.TYPE_CODE_UNSPECIFIED ], }, + uuid: { + code: google.spanner.v1.TypeCode[google.spanner.v1.TypeCode.UUID], + }, bool: { code: google.spanner.v1.TypeCode[google.spanner.v1.TypeCode.BOOL], }, diff --git a/test/index.ts b/test/index.ts index 5952c3dcd..67da27e96 100644 --- a/test/index.ts +++ b/test/index.ts @@ -84,6 +84,7 @@ const fakePfy = extend({}, pfy, { promisified = true; assert.deepStrictEqual(options.exclude, [ 'date', + 'uuid', 'float32', 'float', 'instance', @@ -517,6 +518,23 @@ describe('Spanner', () => { }); }); + describe('uuid', () => { + it('should create a UUID instance', () => { + const value = {}; + const customValue = {}; + + fakeCodec.UUID = class { + constructor(value_) { + assert.strictEqual(value_, value); + return customValue; + } + }; + + const uuid = Spanner.uuid(value); + assert.strictEqual(uuid, customValue); + }); + }); + describe.skip('float32', () => { it('should create a Float32 instance', () => { const value = {}; From 57386989312fd803f3c2ba50a3fd59c086b168ea Mon Sep 17 00:00:00 2001 From: Alka Trivedi Date: Sat, 8 Feb 2025 13:04:57 +0530 Subject: [PATCH 2/3] feat(spanner): support for type UUID --- src/codec.ts | 33 +---- src/index.ts | 21 +--- src/transaction.ts | 12 +- system-test/spanner.ts | 273 ++++++++++++++++++++++++++++++++++++++++- test/codec.ts | 38 ++---- test/index.ts | 18 --- test/transaction.ts | 11 +- 7 files changed, 305 insertions(+), 101 deletions(-) diff --git a/src/codec.ts b/src/codec.ts index 3a49ccb07..dff450564 100644 --- a/src/codec.ts +++ b/src/codec.ts @@ -21,6 +21,7 @@ import * as is from 'is'; import {common as p} from 'protobufjs'; import {google as spannerClient} from '../protos/protos'; import {GoogleError} from 'google-gax'; +import * as uuid from 'uuid'; // eslint-disable-next-line @typescript-eslint/no-explicit-any export type Value = any; @@ -142,20 +143,6 @@ export class SpannerDate extends Date { } } -/** - * @typedef UUID - * @see Spanner.uuid - */ -export class UUID { - value: string; - constructor(value: string) { - this.value = value; - } - valueOf(): string { - return String(this.value); - } -} - /** * Using an abstract class to simplify checking for wrapped numbers. * @@ -543,10 +530,6 @@ function decode( enumObject: columnMetadata as object, }); break; - case spannerClient.spanner.v1.TypeCode.UUID: - case 'UUID': - decoded = new UUID(decoded); - break; case spannerClient.spanner.v1.TypeCode.FLOAT32: case 'FLOAT32': decoded = new Float32(decoded); @@ -683,10 +666,6 @@ function encodeValue(value: Value): Value { return value.value; } - if (value instanceof UUID) { - return value.value; - } - if (value instanceof Struct) { return Array.from(value).map(field => encodeValue(field.value)); } @@ -760,6 +739,7 @@ interface FieldType extends Type { /** * @typedef {object} ParamType * @property {string} type The param type. Must be one of the following: + * - uuid * - float32 * - float64 * - int64 @@ -797,10 +777,6 @@ function getType(value: Value): Type { const isSpecialNumber = is.infinite(value) || (is.number(value) && isNaN(value)); - if (value instanceof UUID) { - return {type: 'uuid'}; - } - if (value instanceof Float32) { return {type: 'float32'}; } @@ -841,6 +817,10 @@ function getType(value: Value): Type { return {type: 'bool'}; } + if (uuid.validate(value)) { + return {type: 'unspecified'}; + } + if (is.string(value)) { return {type: 'string'}; } @@ -996,7 +976,6 @@ export const codec = { convertProtoTimestampToDate, createTypeObject, SpannerDate, - UUID, Float32, Float, Int, diff --git a/src/index.ts b/src/index.ts index bbdfa7c2a..9281484c9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -26,7 +26,6 @@ import * as streamEvents from 'stream-events'; import * as through from 'through2'; import { codec, - UUID, Float32, Float, Int, @@ -1705,23 +1704,6 @@ class Spanner extends GrpcService { return new PreciseDate(value as number); } - /** - * Helper function to get a Cloud Spanner UUID object. - * - * @param {string} value The uuid as a string. - * @returns {UUID} - * - * @example - * ``` - * const {Spanner} = require('@google-cloud/spanner'); - * const value = uuidv4(); - * const uuid = Spanner.uuid(value); - * ``` - */ - static uuid(value): UUID { - return new codec.UUID(value); - } - /** * Helper function to get a Cloud Spanner Float32 object. * @@ -1909,7 +1891,6 @@ class Spanner extends GrpcService { promisifyAll(Spanner, { exclude: [ 'date', - 'uuid', 'float32', 'float', 'instance', @@ -2089,5 +2070,5 @@ import * as protos from '../protos/protos'; import IInstanceConfig = instanceAdmin.spanner.admin.instance.v1.IInstanceConfig; export {v1, protos}; export default {Spanner}; -export {UUID, Float32, Float, Int, Struct, Numeric, PGNumeric, SpannerDate}; +export {Float32, Float, Int, Struct, Numeric, PGNumeric, SpannerDate}; export {ObservabilityOptions}; diff --git a/src/transaction.ts b/src/transaction.ts index f774f5567..adbe68526 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -1521,7 +1521,17 @@ export class Snapshot extends EventEmitter { if (!is.empty(typeMap)) { Object.keys(typeMap).forEach(param => { const type = typeMap[param]; - paramTypes[param] = codec.createTypeObject(type); + const typeObject = codec.createTypeObject(type); + if (type.child) { + if ( + typeObject.code === 'ARRAY' && + typeObject.arrayElementType?.code !== 'TYPE_CODE_UNSPECIFIED' + ) { + paramTypes[param] = codec.createTypeObject(type); + } + } else if (typeObject.code !== 'TYPE_CODE_UNSPECIFIED') { + paramTypes[param] = codec.createTypeObject(type); + } }); } diff --git a/system-test/spanner.ts b/system-test/spanner.ts index 09996119a..35323715d 100644 --- a/system-test/spanner.ts +++ b/system-test/spanner.ts @@ -427,6 +427,7 @@ describe('Spanner', () => { before(async () => { if (IS_EMULATOR_ENABLED) { // TODO: add column Float32Value FLOAT32 and FLOAT32Array Array while using float32 feature. + // TODO: add column UUIDValue UUID and UUIDArray Array while using uuid feature. const [googleSqlOperationUpdateDDL] = await DATABASE.updateSchema( ` CREATE TABLE ${TABLE_NAME} @@ -454,6 +455,7 @@ describe('Spanner', () => { ); await googleSqlOperationUpdateDDL.promise(); // TODO: add column Float32Value DOUBLE PRECISION and FLOAT32Array DOUBLE PRECISION[] while using float32 feature. + // TODO: add column UUIDValue UUID and UUIDArray UUID[] while using uuid feature. const [postgreSqlOperationUpdateDDL] = await PG_DATABASE.updateSchema( ` CREATE TABLE ${TABLE_NAME} @@ -484,6 +486,7 @@ describe('Spanner', () => { await postgreSqlOperationUpdateDDL.promise(); } else { // TODO: add column Float32Value FLOAT32 and FLOAT32Array Array while using float32 feature. + // TODO: add column UUIDValue UUID and UUIDArray Array while using uuid feature. const [googleSqlOperationUpdateDDL] = await DATABASE.updateSchema( ` CREATE TABLE ${TABLE_NAME} @@ -517,6 +520,7 @@ describe('Spanner', () => { ); await googleSqlOperationUpdateDDL.promise(); // TODO: add column Float32Value DOUBLE PRECISION and FLOAT32Array DOUBLE PRECISION[] while using float32 feature. + // TODO: add column UUIDValue UUID and UUIDArray UUID[] while using uuid feature. const [postgreSqlOperationUpdateDDL] = await PG_DATABASE.updateSchema( ` CREATE TABLE ${TABLE_NAME} @@ -930,6 +934,68 @@ describe('Spanner', () => { }); }); + // TODO: Enable when the uuid feature has been released. + describe.skip('uuids', () => { + const uuidInsert = (done, dialect, value) => { + insert({UUIDValue: value}, dialect, (err, row) => { + assert.ifError(err); + if (typeof value === 'object' && value !== null) { + value = value.value; + } + assert.deepStrictEqual(row.toJSON().UUIDValue, value); + done(); + }); + }; + + it('GOOGLE_STANDARD_SQL should write uuid values', done => { + uuidInsert(done, Spanner.GOOGLE_STANDARD_SQL, uuid.v4()); + }); + + it('POSTGRESQL should write uuid values', done => { + uuidInsert(done, Spanner.POSTGRESQL, uuid.v4()); + }); + + it('GOOGLE_STANDARD_SQL should write empty uuid array values', done => { + insert({UUIDArray: []}, Spanner.GOOGLE_STANDARD_SQL, (err, row) => { + assert.ifError(err); + assert.deepStrictEqual(row.toJSON().UUIDArray, []); + done(); + }); + }); + + it('POSTGRESQL should write empty uuid array values', done => { + insert({UUIDArray: []}, Spanner.POSTGRESQL, (err, row) => { + assert.ifError(err); + assert.deepStrictEqual(row.toJSON().UUIDArray, []); + done(); + }); + }); + + it('GOOGLE_STANDARD_SQL should write uuid array values', done => { + const values = [uuid.v4(), uuid.v4(), uuid.v4()]; + + insert({UUIDArray: values}, Spanner.GOOGLE_STANDARD_SQL, (err, row) => { + assert.ifError(err); + for (let i = 0; i < values.length; i++) { + assert.deepStrictEqual(row.toJSON().UUIDArray[i], values[i]); + } + done(); + }); + }); + + it('POSTGRESQL should write uuid array values', done => { + const values = [uuid.v4(), uuid.v4(), uuid.v4()]; + + insert({UUIDArray: values}, Spanner.POSTGRESQL, (err, row) => { + assert.ifError(err); + for (let i = 0; i < values.length; i++) { + assert.deepStrictEqual(row.toJSON().UUIDArray[i], values[i]); + } + done(); + }); + }); + }); + // TODO: Enable when the float32 feature has been released. describe.skip('float32s', () => { const float32Insert = (done, dialect, value) => { @@ -4016,6 +4082,7 @@ describe('Spanner', () => { before(async () => { // TODO: Add column Float32 FLOAT32 while using float32 feature. + // TODO: Add column UUID UUID while using uuid feature. const googleSqlCreateTable = await googleSqlTable.create( `CREATE TABLE ${TABLE_NAME} ( @@ -4035,6 +4102,7 @@ describe('Spanner', () => { await onPromiseOperationComplete(googleSqlCreateTable); // TODO: Add column "Float32" DOUBLE PRECISION while using float32 feature. + // TODO: Add column "UUID" UUID while using uuid feature. const postgreSqlCreateTable = await postgreSqlTable.create( `CREATE TABLE ${TABLE_NAME} ( @@ -4553,6 +4621,7 @@ describe('Spanner', () => { describe('insert & query', () => { const ID = generateName('id'); const NAME = generateName('name'); + // const UUID = uuid.v4(); // TODO: Uncomment while using uuid feature. // const FLOAT32 = 8.2; // TODO: Uncomment while using float32 feature. const FLOAT = 8.2; const INT = 2; @@ -4566,6 +4635,7 @@ describe('Spanner', () => { const GOOGLE_SQL_INSERT_ROW = { SingerId: ID, Name: NAME, + // UUID: UUID, // TODO: Uncomment while using uuid feature. // Float32: FLOAT32, // TODO: Uncomment while using float32 feature. Float: FLOAT, Int: INT, @@ -4580,6 +4650,7 @@ describe('Spanner', () => { const POSTGRESQL_INSERT_ROW = { SingerId: ID, Name: NAME, + // UUID: UUID, // TODO: Uncomment while using uuid feature. // Float32: FLOAT32, // TODO: Uncomment while using float32 feature. Float: FLOAT, Int: INT, @@ -4797,8 +4868,10 @@ describe('Spanner', () => { assert.strictEqual(metadata.rowType!.fields!.length, 10); assert.strictEqual(metadata.rowType!.fields![0].name, 'SingerId'); assert.strictEqual(metadata.rowType!.fields![1].name, 'Name'); - // TODO: Uncomment while using float32 feature and increase the index by 1 for all the asserts below this. - // assert.strictEqual(metadata.rowType!.fields![2].name, 'Float32'); + // TODO: Uncomment while using uuid feature. + // assert.strictEqual(metadata.rowType!.fields![2].name, 'uuid'); + // TODO: Uncomment while using float32 feature and increase the index by 2(if using float32 otherwise increase the index by 1) for all the asserts below this. + // assert.strictEqual(metadata.rowType!.fields![3].name, 'Float32'); assert.strictEqual(metadata.rowType!.fields![2].name, 'Float'); assert.strictEqual(metadata.rowType!.fields![3].name, 'Int'); assert.strictEqual(metadata.rowType!.fields![4].name, 'Info'); @@ -4820,8 +4893,10 @@ describe('Spanner', () => { assert.strictEqual(metadata.rowType!.fields!.length, 7); assert.strictEqual(metadata.rowType!.fields![0].name, 'SingerId'); assert.strictEqual(metadata.rowType!.fields![1].name, 'Name'); - // uncomment while using float32 feature and increase the index by 1 for all the asserts below this. - // assert.strictEqual(metadata.rowType!.fields![2].name, 'Float32'); + // uncomment while using uuid feature. + // assert.strictEqual(metadata.rowType!.fields![2].name, 'UUID'); + // uncomment while using float32 feature and increase the index by 2(if using Float32 otherwise increase the index by 1) for all the asserts below this. + // assert.strictEqual(metadata.rowType!.fields![3].name, 'Float32'); assert.strictEqual(metadata.rowType!.fields![2].name, 'Float'); assert.strictEqual(metadata.rowType!.fields![3].name, 'Int'); assert.strictEqual(metadata.rowType!.fields![4].name, 'Info'); @@ -5170,6 +5245,196 @@ describe('Spanner', () => { }); }); + // TODO: Enable when the uuid feature has been released. + describe.skip('uuid', () => { + const uuidQuery = (done, database, query, value) => { + database.run(query, (err, rows) => { + assert.ifError(err); + const queriedValue = rows[0][0].value; + assert.deepStrictEqual(queriedValue, value); + done(); + }); + }; + + it('GOOGLE_STANDARD_SQL should bind the value when param type uuid is used', done => { + const value = uuid.v4(); + const query = { + sql: 'SELECT @v', + params: { + v: value, + }, + types: { + v: 'uuid', + }, + }; + uuidQuery(done, DATABASE, query, value); + }); + + it('GOOGLE_STANDARD_SQL should bind the value as uuid when param type is not specified', async () => { + const val = uuid.v4(); + const id = generateName('id'); + try { + await googleSqlTable.insert({SingerId: id, UUID: val}); + const query = { + sql: + 'SELECT SingerId, UUID FROM `' + + googleSqlTable.name + + '` WHERE UUID = @v', + params: { + v: val, + }, + }; + const [rows] = await DATABASE.run(query); + assert.strictEqual(rows[0][0].value, id); + assert.strictEqual(uuid.validate(rows[0][1].value), true); + } catch (err) { + assert.ifError(err); + } + }); + + it('POSTGRESQL should bind the value when param type uuid is used', done => { + const value = uuid.v4(); + const query = { + sql: 'SELECT $1', + params: { + p1: value, + }, + types: { + p1: 'uuid', + }, + }; + uuidQuery(done, PG_DATABASE, query, value); + }); + + it('POSTGRESQL should bind the value as uuid when param type is not specified', async () => { + const val = uuid.v4(); + const id = generateName('id'); + try { + await postgreSqlTable.insert({SingerId: id, UUID: val}); + const query = { + sql: + 'SELECT "SingerId", "UUID" FROM ' + + postgreSqlTable.name + + ' WHERE "UUID" = $1', + params: { + p1: val, + }, + }; + const [rows] = await PG_DATABASE.run(query); + assert.strictEqual(rows[0][0].value, id); + assert.strictEqual(uuid.validate(rows[0][1].value), true); + } catch (err) { + assert.ifError(err); + } + }); + + it('GOOGLE_STANDARD_SQL should bind arrays', done => { + const values = [uuid.v4(), uuid.v4(), uuid.v4()]; + + const query = { + sql: 'SELECT @v', + params: { + v: values, + }, + types: { + v: { + type: 'array', + child: 'uuid', + }, + }, + }; + + DATABASE.run(query, (err, rows) => { + assert.ifError(err); + + const expected = values.map(val => { + return val; + }); + + for (let i = 0; i < rows[0][0].value.length; i++) { + assert.deepStrictEqual(rows[0][0].value[i], expected[i]); + } + done(); + }); + }); + + it('GOOGLE_STANDARD_SQL should bind empty arrays', done => { + const values = []; + + const query: ExecuteSqlRequest = { + sql: 'SELECT @v', + params: { + v: values, + }, + types: { + v: { + type: 'array', + child: 'uuid', + }, + }, + }; + + DATABASE.run(query, (err, rows) => { + assert.ifError(err); + assert.deepStrictEqual(rows![0][0].value, values); + done(); + }); + }); + + it('POSTGRESQL should bind arrays', done => { + const values = [uuid.v4(), uuid.v4(), uuid.v4()]; + + const query = { + sql: 'SELECT $1', + params: { + p1: values, + }, + types: { + p1: { + type: 'array', + child: 'uuid', + }, + }, + }; + + PG_DATABASE.run(query, (err, rows) => { + assert.ifError(err); + + const expected = values.map(val => { + return val; + }); + + for (let i = 0; i < rows[0][0].value.length; i++) { + assert.deepStrictEqual(rows[0][0].value[i], expected[i]); + } + done(); + }); + }); + + it('POSTGRESQL should bind empty arrays', done => { + const values = []; + + const query: ExecuteSqlRequest = { + sql: 'SELECT $1', + params: { + p1: values, + }, + types: { + p1: { + type: 'array', + child: 'uuid', + }, + }, + }; + + PG_DATABASE.run(query, (err, rows) => { + assert.ifError(err); + assert.deepStrictEqual(rows![0][0].value, values); + done(); + }); + }); + }); + // TODO: Enable when the float32 feature has been released. describe.skip('float32', () => { const float32Query = (done, database, query, value) => { diff --git a/test/codec.ts b/test/codec.ts index 2f4d90fa3..1dcbff620 100644 --- a/test/codec.ts +++ b/test/codec.ts @@ -18,13 +18,13 @@ import * as assert from 'assert'; import {before, beforeEach, afterEach, describe, it} from 'mocha'; import * as proxyquire from 'proxyquire'; import * as sinon from 'sinon'; +import * as uuid from 'uuid'; import {Big} from 'big.js'; import {PreciseDate} from '@google-cloud/precise-date'; import {GrpcService} from '../src/common-grpc/service'; import {google} from '../protos/protos'; import {GoogleError} from 'google-gax'; import {util} from 'protobufjs'; -import {v4 as uuidv4} from 'uuid'; import Long = util.Long; const singer = require('./data/singer'); const music = singer.examples.spanner.music; @@ -153,22 +153,6 @@ describe('codec', () => { }); }); - describe('UUID', () => { - it('should store the value', () => { - const value = uuidv4(); - const uuid = new codec.UUID(value); - - assert.strictEqual(uuid.value, value); - }); - - it('should return as a uuid', () => { - const value = uuidv4(); - const uuid = new codec.UUID(value); - - assert.strictEqual(uuid.valueOf(), String(value)); - }); - }); - describe('Float', () => { it('should store the value', () => { const value = 8; @@ -697,15 +681,14 @@ describe('codec', () => { assert.deepStrictEqual(decoded, expected); }); - it('should decode UUID', () => { - const value = uuidv4(); + it.skip('should decode UUID', () => { + const value = uuid.v4(); const decoded = codec.decode(value, { code: google.spanner.v1.TypeCode.UUID, }); - assert(decoded instanceof codec.UUID); - assert.strictEqual(decoded.value, value); + assert.strictEqual(decoded, value); }); it.skip('should decode FLOAT32', () => { @@ -1098,13 +1081,12 @@ describe('codec', () => { assert.strictEqual(encoded, '10'); }); - it('should encode UUID', () => { - const random = uuidv4(); - const value = new codec.UUID(random); + it.skip('should encode UUID', () => { + const value = uuid.v4(); const encoded = codec.encode(value); - assert.strictEqual(encoded, random); + assert.strictEqual(encoded, value); }); it.skip('should encode FLOAT32', () => { @@ -1202,9 +1184,9 @@ describe('codec', () => { }); }); - it('should determine if the value is a uuid', () => { - assert.deepStrictEqual(codec.getType(new codec.UUID(uuidv4())), { - type: 'uuid', + it.skip('should determine if the uuid value is unspecified', () => { + assert.deepStrictEqual(codec.getType(uuid.v4()), { + type: 'unspecified', }); }); diff --git a/test/index.ts b/test/index.ts index 67da27e96..5952c3dcd 100644 --- a/test/index.ts +++ b/test/index.ts @@ -84,7 +84,6 @@ const fakePfy = extend({}, pfy, { promisified = true; assert.deepStrictEqual(options.exclude, [ 'date', - 'uuid', 'float32', 'float', 'instance', @@ -518,23 +517,6 @@ describe('Spanner', () => { }); }); - describe('uuid', () => { - it('should create a UUID instance', () => { - const value = {}; - const customValue = {}; - - fakeCodec.UUID = class { - constructor(value_) { - assert.strictEqual(value_, value); - return customValue; - } - }; - - const uuid = Spanner.uuid(value); - assert.strictEqual(uuid, customValue); - }); - }); - describe.skip('float32', () => { it('should create a Float32 instance', () => { const value = {}; diff --git a/test/transaction.ts b/test/transaction.ts index dbccff289..fc53f8bab 100644 --- a/test/transaction.ts +++ b/test/transaction.ts @@ -1133,7 +1133,10 @@ describe('Transaction', () => { const fakeParams = {a: 'foo', b: 3}; const fakeTypes = {b: 'number'}; const fakeMissingType = {type: 'string'}; - const expectedType = {code: google.spanner.v1.TypeCode.STRING}; + const expectedTypes = { + a: {code: google.spanner.v1.TypeCode.STRING}, + b: {code: google.spanner.v1.TypeCode.INT64}, + }; sandbox .stub(codec, 'getType') @@ -1142,15 +1145,17 @@ describe('Transaction', () => { sandbox .stub(codec, 'createTypeObject') + .withArgs(fakeTypes.b) + .returns(expectedTypes.b as google.spanner.v1.Type) .withArgs(fakeMissingType) - .returns(expectedType as google.spanner.v1.Type); + .returns(expectedTypes.a as google.spanner.v1.Type); const {paramTypes} = Snapshot.encodeParams({ params: fakeParams, types: fakeTypes, }); - assert.strictEqual(paramTypes.a, expectedType); + assert.strictEqual(paramTypes.a, expectedTypes.a); }); }); }); From 65f76b3cabc6fcf2c6ae1713bb79a4be1e07e72d Mon Sep 17 00:00:00 2001 From: Alka Trivedi Date: Mon, 24 Feb 2025 10:51:36 +0530 Subject: [PATCH 3/3] refactor --- src/transaction.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/transaction.ts b/src/transaction.ts index adbe68526..35606864f 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -1522,14 +1522,12 @@ export class Snapshot extends EventEmitter { Object.keys(typeMap).forEach(param => { const type = typeMap[param]; const typeObject = codec.createTypeObject(type); - if (type.child) { - if ( + if ( + (type.child && typeObject.code === 'ARRAY' && - typeObject.arrayElementType?.code !== 'TYPE_CODE_UNSPECIFIED' - ) { - paramTypes[param] = codec.createTypeObject(type); - } - } else if (typeObject.code !== 'TYPE_CODE_UNSPECIFIED') { + typeObject.arrayElementType?.code !== 'TYPE_CODE_UNSPECIFIED') || + (!type.child && typeObject.code !== 'TYPE_CODE_UNSPECIFIED') + ) { paramTypes[param] = codec.createTypeObject(type); } });