From 28c2327708c60eb6217ab0fd5debbe4f029ca93c Mon Sep 17 00:00:00 2001 From: Chris Wilcox Date: Mon, 7 Dec 2020 13:27:03 -0800 Subject: [PATCH 01/36] fix: discontinue auto-conversion of JavaScript Numbers to datastore types --- src/entity.ts | 73 +++++++++---------- system-test/datastore.ts | 96 ++++++++++++------------ test/entity.ts | 124 +++++++++++++------------------ test/index.ts | 153 ++++++++++++++++++++------------------- 4 files changed, 211 insertions(+), 235 deletions(-) diff --git a/src/entity.ts b/src/entity.ts index a8c812f8f..f3e923ded 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -16,11 +16,11 @@ import arrify = require('arrify'); import * as extend from 'extend'; import * as is from 'is'; -import {Query, QueryProto, IntegerTypeCastOptions} from './query'; -import {PathType} from '.'; -import {protobuf as Protobuf} from 'google-gax'; +import { Query, QueryProto, IntegerTypeCastOptions } from './query'; +import { PathType } from '.'; +import { protobuf as Protobuf } from 'google-gax'; import * as path from 'path'; -import {google} from '../protos/protos'; +import { google } from '../protos/protos'; // eslint-disable-next-line @typescript-eslint/no-namespace export namespace entity { @@ -33,7 +33,7 @@ export namespace entity { const errorMessages = { MISSING_KIND: 'A key should contain at least a kind.', MISSING_ANCESTOR_ID: 'Ancestor keys require an id or name.', - } as {[index: string]: string}; + } as { [index: string]: string }; super(errorMessages[opts.code]); this.name = 'InvalidKey'; } @@ -188,7 +188,7 @@ export namespace entity { } toJSON(): Json { - return {type: this.type, value: this.value}; + return { type: this.type, value: this.value }; } } @@ -216,7 +216,7 @@ export namespace entity { return ( isDsInt(maybeDsInt) || (is.object(maybeDsInt) && - is.string(maybeDsInt.value) && + (is.string(maybeDsInt.value) || is.number(maybeDsInt.value)) && maybeDsInt.type === 'DatastoreInt') ); } @@ -446,16 +446,16 @@ export namespace entity { if (!Number.isSafeInteger(num)) { throw new Error( 'We attempted to return all of the numeric values, but ' + - (value.propertyName ? value.propertyName + ' ' : '') + - 'value ' + - value.integerValue + - " is out of bounds of 'Number.MAX_SAFE_INTEGER'.\n" + - "To prevent this error, please consider passing 'options.wrapNumbers=true' or\n" + - "'options.wrapNumbers' as\n" + - '{\n' + - ' integerTypeCastFunction: provide \n' + - ' properties: optionally specify property name(s) to be custom casted\n' + - '}\n' + (value.propertyName ? value.propertyName + ' ' : '') + + 'value ' + + value.integerValue + + " is out of bounds of 'Number.MAX_SAFE_INTEGER'.\n" + + "To prevent this error, please consider passing 'options.wrapNumbers=true' or\n" + + "'options.wrapNumbers' as\n" + + '{\n' + + ' integerTypeCastFunction: provide \n' + + ' properties: optionally specify property name(s) to be custom casted\n' + + '}\n' ); } return num; @@ -580,29 +580,12 @@ export namespace entity { return valueProto; } - if (typeof value === 'number') { - if (Number.isInteger(value)) { - if (!Number.isSafeInteger(value)) { - process.emitWarning( - 'IntegerOutOfBoundsWarning: ' + - "the value for '" + - property + - "' property is outside of bounds of a JavaScript Number.\n" + - "Use 'Datastore.int()' to preserve accuracy during the upload." - ); - } - value = new entity.Int(value); - } else { - value = new entity.Double(value); - } - } - - if (isDsInt(value)) { + if (isDsInt(value) || isDsIntLike(value)) { valueProto.integerValue = value.value; return valueProto; } - if (isDsDouble(value)) { + if (isDsDouble(value) || isDsDoubleLike(value)) { valueProto.doubleValue = value.value; return valueProto; } @@ -648,12 +631,24 @@ export namespace entity { return valueProto; } + if (typeof value === 'number') { + const message = "The value for '" + + property + + "' property is a JavaScript Number. Use " + + "'Datastore.int()' or " + + "'Datastore.double()' to preserve " + + "accuracy during the upload." + + throw new Error(message); + } + if (is.object(value)) { if (!is.empty(value)) { value = extend(true, {}, value); for (const prop in value) { value[prop] = entity.encodeValue(value[prop], prop); + // value[prop] = entity.encodeValue(value, prop); } } @@ -1317,7 +1312,7 @@ export namespace entity { const reference = { app: projectId, namespace: key.namespace, - path: {element: elements}, + path: { element: elements }, }; const buffer = this.protos.Reference.encode(reference).finish(); @@ -1414,7 +1409,7 @@ export interface ValueProto { export interface EntityProto { key?: KeyProto | null; - properties?: {[k: string]: ValueProto}; + properties?: { [k: string]: ValueProto }; excludeFromIndexes?: boolean; } @@ -1438,7 +1433,7 @@ export interface ResponseResult { } export interface EntityObject { - data: {[k: string]: Entity}; + data: { [k: string]: Entity }; excludeFromIndexes: string[]; } diff --git a/system-test/datastore.ts b/system-test/datastore.ts index f9398ffb9..4a0956fb3 100644 --- a/system-test/datastore.ts +++ b/system-test/datastore.ts @@ -13,13 +13,13 @@ // limitations under the License. import * as assert from 'assert'; -import {readFileSync} from 'fs'; +import { readFileSync } from 'fs'; import * as path from 'path'; -import {before, after, describe, it} from 'mocha'; +import { before, after, describe, it } from 'mocha'; import * as yaml from 'js-yaml'; -import {Datastore, Index} from '../src'; -import {google} from '../protos/protos'; -import {Storage} from '@google-cloud/storage'; +import { Datastore, Index } from '../src'; +import { google } from '../protos/protos'; +import { Storage } from '@google-cloud/storage'; describe('Datastore', () => { const testKinds: string[] = []; @@ -36,9 +36,9 @@ describe('Datastore', () => { return keyObject; }; - const {indexes: DECLARED_INDEXES} = yaml.safeLoad( + const { indexes: DECLARED_INDEXES } = yaml.safeLoad( readFileSync(path.join(__dirname, 'data', 'index.yaml'), 'utf8') - ) as {indexes: google.datastore.admin.v1.IIndex[]}; + ) as { indexes: google.datastore.admin.v1.IIndex[] }; // TODO/DX ensure indexes before testing, and maybe? cleanup indexes after // possible implications with kokoro project @@ -67,11 +67,11 @@ describe('Datastore', () => { publishedAt: new Date(), author: 'Silvano', isDraft: false, - wordCount: 400, - rating: 5.0, + wordCount: datastore.int(400), + rating: datastore.double(5.0), likes: null, metadata: { - views: 100, + views: datastore.int(100), }, }; @@ -261,8 +261,8 @@ describe('Datastore', () => { const postKey = datastore.key('Team'); const largeIntValueAsString = '9223372036854775807'; const points = Datastore.int(largeIntValueAsString); - await datastore.save({key: postKey, data: {points}}); - const [entity] = await datastore.get(postKey, {wrapNumbers: true}); + await datastore.save({ key: postKey, data: { points } }); + const [entity] = await datastore.get(postKey, { wrapNumbers: true }); assert.strictEqual(entity.points.value, largeIntValueAsString); assert.throws(() => entity.points.valueOf()); await datastore.delete(postKey); @@ -272,9 +272,9 @@ describe('Datastore', () => { const postKey = datastore.key('Scores'); const largeIntValueAsString = '9223372036854775807'; const panthers = Datastore.int(largeIntValueAsString); - const broncos = 922337203; + const broncos = Datastore.int(922337203); let integerTypeCastFunctionCalled = 0; - await datastore.save({key: postKey, data: {panthers, broncos}}); + await datastore.save({ key: postKey, data: { panthers, broncos } }); const [entity] = await datastore.get(postKey, { wrapNumbers: { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -295,7 +295,7 @@ describe('Datastore', () => { it('should save/get/delete with a key name', async () => { const postKey = datastore.key(['Post', 'post1']); - await datastore.save({key: postKey, data: post}); + await datastore.save({ key: postKey, data: post }); const [entity] = await datastore.get(postKey); assert.deepStrictEqual(entity[datastore.KEY], postKey); delete entity[datastore.KEY]; @@ -305,7 +305,7 @@ describe('Datastore', () => { it('should save/get/delete with a numeric key id', async () => { const postKey = datastore.key(['Post', 123456789]); - await datastore.save({key: postKey, data: post}); + await datastore.save({ key: postKey, data: post }); const [entity] = await datastore.get(postKey); delete entity[datastore.KEY]; assert.deepStrictEqual(entity, post); @@ -317,7 +317,7 @@ describe('Datastore', () => { const data = { buf: Buffer.from('010100000000000000000059400000000000006940', 'hex'), }; - await datastore.save({key: postKey, data}); + await datastore.save({ key: postKey, data }); const assignedId = postKey.id; assert(assignedId); const [entity] = await datastore.get(postKey); @@ -331,7 +331,7 @@ describe('Datastore', () => { const data = { buf: Buffer.from([]), }; - await datastore.save({key: postKey, data}); + await datastore.save({ key: postKey, data }); const assignedId = postKey.id; assert(assignedId); const [entity] = await datastore.get(postKey); @@ -342,7 +342,7 @@ describe('Datastore', () => { it('should save/get/delete with a generated key id', async () => { const postKey = datastore.key('Post'); - await datastore.save({key: postKey, data: post}); + await datastore.save({ key: postKey, data: post }); // The key's path should now be complete. assert(postKey.id); @@ -355,7 +355,7 @@ describe('Datastore', () => { it('should save/get/update', async () => { const postKey = datastore.key('Post'); - await datastore.save({key: postKey, data: post}); + await datastore.save({ key: postKey, data: post }); const [entity] = await datastore.get(postKey); assert.strictEqual(entity.title, post.title); entity.title = 'Updated'; @@ -405,7 +405,7 @@ describe('Datastore', () => { it('should fail explicitly set second insert on save', async () => { const postKey = datastore.key('Post'); - await datastore.save({key: postKey, data: post}); + await datastore.save({ key: postKey, data: post }); // The key's path should now be complete. assert(postKey.id); @@ -440,14 +440,14 @@ describe('Datastore', () => { publishedAt: new Date('2001-01-01T00:00:00.000Z'), author: 'Silvano', isDraft: false, - wordCount: 450, - rating: 4.5, + wordCount: datastore.int(450), + rating: datastore.double(4.5), }; const key1 = datastore.key('Post'); const key2 = datastore.key('Post'); await datastore.save([ - {key: key1, data: post}, - {key: key2, data: post2}, + { key: key1, data: post }, + { key: key2, data: post2 }, ]); const [entities] = await datastore.get([key1, key2]); assert.strictEqual(entities.length, 2); @@ -460,8 +460,8 @@ describe('Datastore', () => { datastore.save( [ - {key: key1, data: post}, - {key: key2, data: post}, + { key: key1, data: post }, + { key: key2, data: post }, ], err => { assert.ifError(err); @@ -570,49 +570,49 @@ describe('Datastore', () => { { name: 'Rickard', family: 'Stark', - appearances: 9, + appearances: datastore.int(9), alive: false, }, { name: 'Eddard', family: 'Stark', - appearances: 9, + appearances: datastore.int(9), alive: false, }, { name: 'Catelyn', family: ['Stark', 'Tully'], - appearances: 26, + appearances: datastore.int(26), alive: false, }, { name: 'Arya', family: 'Stark', - appearances: 33, + appearances: datastore.int(33), alive: true, }, { name: 'Sansa', family: 'Stark', - appearances: 31, + appearances: datastore.int(31), alive: true, }, { name: 'Robb', family: 'Stark', - appearances: 22, + appearances: datastore.int(22), alive: false, }, { name: 'Bran', family: 'Stark', - appearances: 25, + appearances: datastore.int(25), alive: true, }, { name: 'Jon Snow', family: 'Stark', - appearances: 32, + appearances: datastore.int(32), alive: true, }, ]; @@ -715,7 +715,7 @@ describe('Datastore', () => { const q = datastore .createQuery('Character') .hasAncestor(ancestor) - .filter('appearances', '>=', 20); + .filter('appearances', '>=', datastore.int(20)); const [entities] = await datastore.runQuery(q); assert.strictEqual(entities!.length, 6); }); @@ -725,7 +725,7 @@ describe('Datastore', () => { .createQuery('Character') .hasAncestor(ancestor) .filter('family', 'Stark') - .filter('appearances', '>=', 20); + .filter('appearances', '>=', datastore.int(20)); const [entities] = await datastore.runQuery(q); assert.strictEqual(entities!.length, 6); }); @@ -841,7 +841,7 @@ describe('Datastore', () => { const transaction = datastore.transaction(); await transaction.run(); await transaction.get(key); - transaction.save({key, data: obj}); + transaction.save({ key, data: obj }); await transaction.commit(); const [entity] = await datastore.get(key); delete entity[datastore.KEY]; @@ -865,11 +865,11 @@ describe('Datastore', () => { transaction.save([ { key, - data: {rating: 10}, + data: { rating: datastore.int(10) }, }, { key: incompleteKey, - data: {rating: 100}, + data: { rating: datastore.int(100) }, }, ]); @@ -885,7 +885,7 @@ describe('Datastore', () => { datastore.get(key), ]); assert.strictEqual(typeof deletedEntity, 'undefined'); - assert.strictEqual(fetchedEntity.rating, 10); + assert.strictEqual(fetchedEntity.rating, datastore.int(10)); }); it('should use the last modification to a key', async () => { @@ -897,13 +897,13 @@ describe('Datastore', () => { { key, data: { - rating: 10, + rating: datastore.int(10), }, }, { key: incompleteKey, data: { - rating: 100, + rating: datastore.int(100), }, }, ]); @@ -934,18 +934,18 @@ describe('Datastore', () => { }); it('should read in a readOnly transaction', async () => { - const transaction = datastore.transaction({readOnly: true}); + const transaction = datastore.transaction({ readOnly: true }); const key = datastore.key(['Company', 'Google']); await transaction.run(); await transaction.get(key); }); it('should not write in a readOnly transaction', async () => { - const transaction = datastore.transaction({readOnly: true}); + const transaction = datastore.transaction({ readOnly: true }); const key = datastore.key(['Company', 'Google']); await transaction.run(); await transaction.get(key); - transaction.save({key, data: {}}); + transaction.save({ key, data: {} }); await assert.rejects(transaction.commit()); }); }); @@ -1008,10 +1008,10 @@ describe('Datastore', () => { const bucket = gcs.bucket('nodejs-datastore-system-tests'); it('should export, then import entities', async () => { - const [exportOperation] = await datastore.export({bucket}); + const [exportOperation] = await datastore.export({ bucket }); await exportOperation.promise(); - const [files] = await bucket.getFiles({maxResults: 1}); + const [files] = await bucket.getFiles({ maxResults: 1 }); const [exportedFile] = files; assert.ok(exportedFile.name.includes('overall_export_metadata')); diff --git a/test/entity.ts b/test/entity.ts index 84ca5b3dd..bbd9a8941 100644 --- a/test/entity.ts +++ b/test/entity.ts @@ -13,12 +13,12 @@ // limitations under the License. import * as assert from 'assert'; -import {beforeEach, afterEach, describe, it} from 'mocha'; +import { beforeEach, afterEach, describe, it } from 'mocha'; import * as extend from 'extend'; import * as sinon from 'sinon'; -import {Datastore} from '../src'; -import {Entity} from '../src/entity'; -import {IntegerTypeCastOptions} from '../src/query'; +import { Datastore } from '../src'; +import { Entity } from '../src/entity'; +import { IntegerTypeCastOptions } from '../src/query'; export function outOfBoundsError(opts: { propertyName?: string; @@ -26,16 +26,16 @@ export function outOfBoundsError(opts: { }) { return new Error( 'We attempted to return all of the numeric values, but ' + - (opts.propertyName ? opts.propertyName + ' ' : '') + - 'value ' + - opts.integerValue + - " is out of bounds of 'Number.MAX_SAFE_INTEGER'.\n" + - "To prevent this error, please consider passing 'options.wrapNumbers=true' or\n" + - "'options.wrapNumbers' as\n" + - '{\n' + - ' integerTypeCastFunction: provide \n' + - ' properties: optionally specify property name(s) to be custom casted\n' + - '}\n' + (opts.propertyName ? opts.propertyName + ' ' : '') + + 'value ' + + opts.integerValue + + " is out of bounds of 'Number.MAX_SAFE_INTEGER'.\n" + + "To prevent this error, please consider passing 'options.wrapNumbers=true' or\n" + + "'options.wrapNumbers' as\n" + + '{\n' + + ' integerTypeCastFunction: provide \n' + + ' properties: optionally specify property name(s) to be custom casted\n' + + '}\n' ); } @@ -156,12 +156,12 @@ describe('entity', () => { // should throw when Number is passed assert.throws(() => { new entity.Int(largeIntegerValue).valueOf(); - }, outOfBoundsError({integerValue: largeIntegerValue})); + }, outOfBoundsError({ integerValue: largeIntegerValue })); // should throw when string is passed assert.throws(() => { new entity.Int(smallIntegerValue.toString()).valueOf(); - }, outOfBoundsError({integerValue: smallIntegerValue})); + }, outOfBoundsError({ integerValue: smallIntegerValue })); }); it('should not auto throw on initialization', () => { @@ -272,12 +272,12 @@ describe('entity', () => { describe('isDsGeoPoint', () => { it('should correctly identify a GeoPoint', () => { - const geoPoint = new entity.GeoPoint({latitude: 24, longitude: 88}); + const geoPoint = new entity.GeoPoint({ latitude: 24, longitude: 88 }); assert.strictEqual(entity.isDsGeoPoint(geoPoint), true); }); it('should correctly identify a homomorphic non-GeoPoint', () => { - const geoPoint = new entity.GeoPoint({latitude: 24, longitude: 88}); + const geoPoint = new entity.GeoPoint({ latitude: 24, longitude: 88 }); const nonGeoPoint = Object.assign({}, geoPoint); assert.strictEqual(entity.isDsGeoPoint(nonGeoPoint), false); }); @@ -286,47 +286,47 @@ describe('entity', () => { describe('Key', () => { it('should assign the namespace', () => { const namespace = 'NS'; - const key = new entity.Key({namespace, path: []}); + const key = new entity.Key({ namespace, path: [] }); assert.strictEqual(key.namespace, namespace); }); it('should assign the kind', () => { const kind = 'kind'; - const key = new entity.Key({path: [kind]}); + const key = new entity.Key({ path: [kind] }); assert.strictEqual(key.kind, kind); }); it('should assign the ID', () => { const id = 11; - const key = new entity.Key({path: ['Kind', id]}); + const key = new entity.Key({ path: ['Kind', id] }); assert.strictEqual(key.id, id); }); it('should assign the ID from an Int', () => { const id = new entity.Int(11); - const key = new entity.Key({path: ['Kind', id]}); + const key = new entity.Key({ path: ['Kind', id] }); assert.strictEqual(key.id, id.value); }); it('should assign the name', () => { const name = 'name'; - const key = new entity.Key({path: ['Kind', name]}); + const key = new entity.Key({ path: ['Kind', name] }); assert.strictEqual(key.name, name); }); it('should assign a parent', () => { - const key = new entity.Key({path: ['ParentKind', 1, 'Kind', 1]}); + const key = new entity.Key({ path: ['ParentKind', 1, 'Kind', 1] }); assert(key.parent instanceof entity.Key); }); it('should not modify input path', () => { const inputPath = ['ParentKind', 1, 'Kind', 1]; - new entity.Key({path: inputPath}); + new entity.Key({ path: inputPath }); assert.deepStrictEqual(inputPath, ['ParentKind', 1, 'Kind', 1]); }); it('should always compute the correct path', () => { - const key = new entity.Key({path: ['ParentKind', 1, 'Kind', 1]}); + const key = new entity.Key({ path: ['ParentKind', 1, 'Kind', 1] }); assert.deepStrictEqual(key.path, ['ParentKind', 1, 'Kind', 1]); key.parent.kind = 'GrandParentKind'; @@ -373,12 +373,12 @@ describe('entity', () => { describe('isDsKey', () => { it('should correctly identify a Key', () => { - const key = new entity.Key({path: ['Kind', 1]}); + const key = new entity.Key({ path: ['Kind', 1] }); assert.strictEqual(entity.isDsKey(key), true); }); it('should correctly identify a homomorphic non-Key', () => { - const notKey = Object.assign({}, new entity.Key({path: ['Kind', 1]})); + const notKey = Object.assign({}, new entity.Key({ path: ['Kind', 1] })); assert.strictEqual(entity.isDsKey(notKey), false); }); }); @@ -594,7 +594,7 @@ describe('entity', () => { }); it('should wrap ints with wrapNumbers as object', () => { - const wrapNumbers = {integerTypeCastFunction: () => {}}; + const wrapNumbers = { integerTypeCastFunction: () => { } }; const stub = sinon.spy(entity, 'Int'); entity.decodeValueProto(valueProto, wrapNumbers); @@ -602,11 +602,11 @@ describe('entity', () => { }); it('should call #valueOf if integerTypeCastFunction is provided', () => { - Object.assign(valueProto, {integerValue: Number.MAX_SAFE_INTEGER}); + Object.assign(valueProto, { integerValue: Number.MAX_SAFE_INTEGER }); const takeFirstTen = sinon .stub() .callsFake((value: string) => value.toString().substr(0, 10)); - const wrapNumbers = {integerTypeCastFunction: takeFirstTen}; + const wrapNumbers = { integerTypeCastFunction: takeFirstTen }; assert.strictEqual( entity.decodeValueProto(valueProto, wrapNumbers), @@ -744,36 +744,17 @@ describe('entity', () => { assert.deepStrictEqual(entity.encodeValue(value), expectedValueProto); }); + it('should throw if an invalid value was provided', () => { + assert.throws(() => { + entity.encodeValue(); + }, /Unsupported field value/); + }); it('should encode an int', () => { const value = 8; - const expectedValueProto = { - integerValue: value, - }; - - entity.Int = function (value_: {}) { - assert.strictEqual(value_, value); - this.value = value_; - }; - - assert.deepStrictEqual(entity.encodeValue(value), expectedValueProto); - }); - - it('should emit warning on out of bounce int', done => { - const largeIntValue = 9223372036854775807; - const property = 'largeInt'; - const expectedWarning = - 'IntegerOutOfBoundsWarning: ' + - "the value for '" + - property + - "' property is outside of bounds of a JavaScript Number.\n" + - "Use 'Datastore.int()' to preserve accuracy during the upload."; - - process.on('warning', warning => { - assert.strictEqual(warning.message, expectedWarning); - done(); - }); - entity.encodeValue(largeIntValue, property); + assert.throws(() => { + entity.encodeValue(value); + }, /The value for 'undefined' property is a JavaScript Number/); }); it('should encode an Int object', () => { @@ -789,16 +770,11 @@ describe('entity', () => { it('should encode a double', () => { const value = 8.3; - const expectedValueProto = { - doubleValue: value, - }; - entity.Double = function (value_: {}) { - assert.strictEqual(value_, value); - this.value = value_; - }; + assert.throws(() => { + entity.encodeValue(value); + }, /The value for 'undefined' property is a JavaScript Number/); - assert.deepStrictEqual(entity.encodeValue(value), expectedValueProto); }); it('should encode a Double object', () => { @@ -985,7 +961,7 @@ describe('entity', () => { }); describe('should pass `wrapNumbers` to decodeValueProto', () => { - const entityProto = {properties: {number: {}}}; + const entityProto = { properties: { number: {} } }; let decodeValueProtoStub: sinon.SinonStub; let wrapNumbers: boolean | IntegerTypeCastOptions | undefined; @@ -1017,7 +993,7 @@ describe('entity', () => { it('should pass `wrapNumbers` to decodeValueProto as IntegerTypeCastOptions', () => { const integerTypeCastOptions = { - integerTypeCastFunction: () => {}, + integerTypeCastFunction: () => { }, properties: 'that', }; @@ -1151,7 +1127,7 @@ describe('entity', () => { nestedArrayVariants: [ { - a: [{b: value13}, {c: value14}], + a: [{ b: value13 }, { c: value14 }], }, { a: null, @@ -1160,7 +1136,7 @@ describe('entity', () => { a: [value15], }, { - a: [{b: ['nasty', 'array']}], + a: [{ b: ['nasty', 'array'] }], }, ], @@ -1515,7 +1491,7 @@ describe('entity', () => { }); describe('should pass `wrapNumbers` to entityFromEntityProto', () => { - const results = [{entity: {}}]; + const results = [{ entity: {} }]; // eslint-disable-next-line @typescript-eslint/no-explicit-any let entityFromEntityProtoStub: any; let wrapNumbers: boolean | IntegerTypeCastOptions | undefined; @@ -1545,7 +1521,7 @@ describe('entity', () => { it('should pass `wrapNumbers` to entityFromEntityProto as IntegerTypeCastOptions', () => { const integerTypeCastOptions = { - integerTypeCastFunction: () => {}, + integerTypeCastFunction: () => { }, properties: 'that', }; @@ -1865,7 +1841,7 @@ describe('entity', () => { path: ['Kind2', 'somename'], }); - const ds = new Datastore({projectId: 'project-id'}); + const ds = new Datastore({ projectId: 'project-id' }); const query = ds .createQuery('Kind1') @@ -1883,7 +1859,7 @@ describe('entity', () => { }); it('should handle buffer start and end values', () => { - const ds = new Datastore({projectId: 'project-id'}); + const ds = new Datastore({ projectId: 'project-id' }); const startVal = Buffer.from('start'); const endVal = Buffer.from('end'); diff --git a/test/index.ts b/test/index.ts index 42bb76d52..c6d44c743 100644 --- a/test/index.ts +++ b/test/index.ts @@ -13,15 +13,15 @@ // limitations under the License. import * as assert from 'assert'; -import {before, beforeEach, after, afterEach, describe, it} from 'mocha'; +import { before, beforeEach, after, afterEach, describe, it } from 'mocha'; import * as gax from 'google-gax'; import * as proxyquire from 'proxyquire'; -import {PassThrough, Readable} from 'stream'; +import { PassThrough, Readable } from 'stream'; import * as ds from '../src'; -import {DatastoreOptions} from '../src'; -import {entity, Entity, EntityProto, EntityObject} from '../src/entity'; -import {RequestConfig} from '../src/request'; +import { DatastoreOptions } from '../src'; +import { entity, Entity, EntityProto, EntityObject } from '../src/entity'; +import { RequestConfig } from '../src/request'; import * as is from 'is'; import * as sinon from 'sinon'; import * as extend from 'extend'; @@ -34,8 +34,10 @@ const fakeEntity: any = { KEY_SYMBOL: Symbol('fake key symbol'), Int: class { value: {}; + type: string; constructor(value: {}) { this.value = value; + this.type = 'DatastoreInt'; } }, // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -44,8 +46,11 @@ const fakeEntity: any = { }, Double: class { value: {}; + type: string; + constructor(value: {}) { this.value = value; + this.type = 'DatastoreDouble'; } }, // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -84,7 +89,7 @@ const fakeEntity: any = { let googleAuthOverride: Function | null; function fakeGoogleAuth(...args: Array<{}>) { - return (googleAuthOverride || (() => {}))(...args); + return (googleAuthOverride || (() => { }))(...args); } let createInsecureOverride: Function | null; @@ -99,7 +104,7 @@ const fakeGoogleGax = { credentials: { // eslint-disable-next-line @typescript-eslint/no-explicit-any createInsecure(...args: any[]) { - return (createInsecureOverride || (() => {}))(...args); + return (createInsecureOverride || (() => { }))(...args); }, }, } as {}) as gax.GrpcModule; @@ -133,7 +138,7 @@ class FakeTransaction { } } -function FakeV1() {} +function FakeV1() { } const sandbox = sinon.createSandbox(); @@ -158,10 +163,10 @@ describe('Datastore', () => { before(() => { Datastore = proxyquire('../src', { - './entity.js': {entity: fakeEntity}, - './index-class.js': {Index: FakeIndex}, - './query.js': {Query: FakeQuery}, - './transaction.js': {Transaction: FakeTransaction}, + './entity.js': { entity: fakeEntity }, + './index-class.js': { Index: FakeIndex }, + './query.js': { Query: FakeQuery }, + './transaction.js': { Transaction: FakeTransaction }, './v1': FakeV1, 'google-auth-library': { GoogleAuth: fakeGoogleAuth, @@ -330,13 +335,13 @@ describe('Datastore', () => { describe('geoPoint', () => { it('should expose GeoPoint builder', () => { - const aGeoPoint = {latitude: 24, longitude: 88}; + const aGeoPoint = { latitude: 24, longitude: 88 }; const geoPoint = Datastore.geoPoint(aGeoPoint); assert.strictEqual(geoPoint.value, aGeoPoint); }); it('should also be on the prototype', () => { - const aGeoPoint = {latitude: 24, longitude: 88}; + const aGeoPoint = { latitude: 24, longitude: 88 }; const geoPoint = datastore.geoPoint(aGeoPoint); assert.strictEqual(geoPoint.value, aGeoPoint); }); @@ -380,7 +385,7 @@ describe('Datastore', () => { describe('isGeoPoint', () => { it('should pass value to entity', () => { - const value = {fakeLatitude: 1, fakeLongitude: 2}; + const value = { fakeLatitude: 1, fakeLongitude: 2 }; let called = false; const saved = fakeEntity.isDsGeoPoint; fakeEntity.isDsGeoPoint = (arg: {}) => { @@ -424,7 +429,7 @@ describe('Datastore', () => { describe('isKey', () => { it('should pass value to entity', () => { - const value = {zz: true}; + const value = { zz: true }; let called = false; const saved = fakeEntity.isDsKey; fakeEntity.isDsKey = (arg: {}) => { @@ -538,7 +543,7 @@ describe('Datastore', () => { done(); }; - datastore.export({bucket}, assert.ifError); + datastore.export({ bucket }, assert.ifError); }); it('should remove extraneous gs:// prefix from input', done => { @@ -550,11 +555,11 @@ describe('Datastore', () => { done(); }; - datastore.export({bucket}, assert.ifError); + datastore.export({ bucket }, assert.ifError); }); it('should accept a Bucket object destination', done => { - const bucket = {name: 'bucket'}; + const bucket = { name: 'bucket' }; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any) => { @@ -565,7 +570,7 @@ describe('Datastore', () => { done(); }; - datastore.export({bucket}, assert.ifError); + datastore.export({ bucket }, assert.ifError); }); it('should throw if a destination is not provided', () => { @@ -588,7 +593,7 @@ describe('Datastore', () => { it('should accept kinds', done => { const kinds = ['kind1', 'kind2']; - const config = {bucket: 'bucket', kinds}; + const config = { bucket: 'bucket', kinds }; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any) => { @@ -614,7 +619,7 @@ describe('Datastore', () => { it('should accept namespaces', done => { const namespaces = ['ns1', 'n2']; - const config = {bucket: 'bucket', namespaces}; + const config = { bucket: 'bucket', namespaces }; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any) => { @@ -663,7 +668,7 @@ describe('Datastore', () => { it('should send any user input to API', done => { const userProperty = 'abc'; - const config = {bucket: 'bucket', userProperty}; + const config = { bucket: 'bucket', userProperty }; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any) => { @@ -675,7 +680,7 @@ describe('Datastore', () => { }); it('should send correct request', done => { - const config = {bucket: 'bucket'}; + const config = { bucket: 'bucket' }; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any) => { @@ -690,7 +695,7 @@ describe('Datastore', () => { it('should accept gaxOptions', done => { const gaxOptions = {}; - const config = {bucket: 'bucket', gaxOptions}; + const config = { bucket: 'bucket', gaxOptions }; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any) => { @@ -704,7 +709,7 @@ describe('Datastore', () => { describe('getIndexes', () => { it('should send the correct request', done => { - const options = {a: 'b'}; + const options = { a: 'b' }; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any) => { @@ -789,7 +794,7 @@ describe('Datastore', () => { it('should accept gaxOptions', done => { const options = { - gaxOptions: {a: 'b'}, + gaxOptions: { a: 'b' }, }; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -804,7 +809,7 @@ describe('Datastore', () => { it('should not send gaxOptions as request options', done => { const options = { - gaxOptions: {a: 'b'}, + gaxOptions: { a: 'b' }, }; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -868,7 +873,7 @@ describe('Datastore', () => { }); it('should execute callback with Index instances', done => { - const rawIndex = {indexId: 'name', a: 'b'}; + const rawIndex = { indexId: 'name', a: 'b' }; const indexInstance = {}; datastore.index = (id: string) => { @@ -892,8 +897,8 @@ describe('Datastore', () => { }); it('should execute callback with prepared nextQuery', done => { - const options = {pageToken: '1'}; - const nextQuery = {pageToken: '2'}; + const options = { pageToken: '1' }; + const nextQuery = { pageToken: '2' }; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any, callback: Function) => { @@ -913,7 +918,7 @@ describe('Datastore', () => { describe('getIndexesStream', () => { it('should make correct request', done => { - const options = {a: 'b'}; + const options = { a: 'b' }; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.requestStream_ = (config: any) => { @@ -931,7 +936,7 @@ describe('Datastore', () => { }); it('should accept gaxOptions', done => { - const options = {gaxOptions: {}}; + const options = { gaxOptions: {} }; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.requestStream_ = (config: any) => { @@ -944,7 +949,7 @@ describe('Datastore', () => { }); it('should transform response indexes into Index objects', done => { - const rawIndex = {indexId: 'name', a: 'b'}; + const rawIndex = { indexId: 'name', a: 'b' }; const indexInstance = {}; const requestStream = new Readable({ objectMode: true, @@ -996,7 +1001,7 @@ describe('Datastore', () => { done(); }; - datastore.import({file}, assert.ifError); + datastore.import({ file }, assert.ifError); }); it('should remove extraneous gs:// prefix from input', done => { @@ -1008,11 +1013,11 @@ describe('Datastore', () => { done(); }; - datastore.import({file}, assert.ifError); + datastore.import({ file }, assert.ifError); }); it('should accept a File object source', done => { - const file = {bucket: {name: 'bucket'}, name: 'file'}; + const file = { bucket: { name: 'bucket' }, name: 'file' }; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any) => { @@ -1023,7 +1028,7 @@ describe('Datastore', () => { done(); }; - datastore.import({file}, assert.ifError); + datastore.import({ file }, assert.ifError); }); it('should throw if a source is not provided', () => { @@ -1034,7 +1039,7 @@ describe('Datastore', () => { it('should accept kinds', done => { const kinds = ['kind1', 'kind2']; - const config = {file: 'file', kinds}; + const config = { file: 'file', kinds }; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any) => { @@ -1060,7 +1065,7 @@ describe('Datastore', () => { it('should accept namespaces', done => { const namespaces = ['ns1', 'n2']; - const config = {file: 'file', namespaces}; + const config = { file: 'file', namespaces }; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any) => { @@ -1109,7 +1114,7 @@ describe('Datastore', () => { it('should send any user input to API', done => { const userProperty = 'abc'; - const config = {file: 'file', userProperty}; + const config = { file: 'file', userProperty }; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any) => { @@ -1121,7 +1126,7 @@ describe('Datastore', () => { }); it('should send correct request', done => { - const config = {file: 'file'}; + const config = { file: 'file' }; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any) => { @@ -1136,7 +1141,7 @@ describe('Datastore', () => { it('should accept gaxOptions', done => { const gaxOptions = {}; - const config = {file: 'file', gaxOptions}; + const config = { file: 'file', gaxOptions }; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any) => { @@ -1167,7 +1172,7 @@ describe('Datastore', () => { it('should prepare entity objects', done => { const entityObject = {}; - const preparedEntityObject = {prepared: true}; + const preparedEntityObject = { prepared: true }; const expectedEntityObject = Object.assign({}, preparedEntityObject, { method: 'insert', }); @@ -1202,8 +1207,8 @@ describe('Datastore', () => { ]); callback(); }; - const key = new entity.Key({namespace: 'ns', path: ['Company']}); - datastore.insert({key, data: {}}, done); + const key = new entity.Key({ namespace: 'ns', path: ['Company'] }); + datastore.insert({ key, data: {} }, done); }); }); @@ -1297,8 +1302,8 @@ describe('Datastore', () => { }; datastore.save( [ - {key, data: {k: 'v'}}, - {key, data: {k: 'v'}}, + { key, data: { k: 'v' } }, + { key, data: { k: 'v' } }, ], done ); @@ -1316,7 +1321,7 @@ describe('Datastore', () => { arrayValue: { values: [ { - integerValue: '0', + integerValue: 0, }, { nullValue: 0, @@ -1342,7 +1347,7 @@ describe('Datastore', () => { data: { stringField: 'string value', nullField: null, - arrayField: [0, null], + arrayField: [Datastore.int(0), null], objectField: null, }, excludeLargeProperties: true, @@ -1380,7 +1385,7 @@ describe('Datastore', () => { return { key, method: 'insert', - data: {k: 'v'}, + data: { k: 'v' }, } as {}; }); @@ -1400,22 +1405,22 @@ describe('Datastore', () => { assert(is.object(config.reqOpts!.mutations![2].upsert)); const insert = config.reqOpts!.mutations![0].insert!; - assert.deepStrictEqual(insert.properties!.k, {stringValue: 'v'}); + assert.deepStrictEqual(insert.properties!.k, { stringValue: 'v' }); const update = config.reqOpts!.mutations![1].update!; - assert.deepStrictEqual(update.properties!.k2, {stringValue: 'v2'}); + assert.deepStrictEqual(update.properties!.k2, { stringValue: 'v2' }); const upsert = config.reqOpts!.mutations![2].upsert!; - assert.deepStrictEqual(upsert.properties!.k3, {stringValue: 'v3'}); + assert.deepStrictEqual(upsert.properties!.k3, { stringValue: 'v3' }); callback(); }; datastore.save( [ - {key, method: 'insert', data: {k: 'v'}}, - {key, method: 'update', data: {k2: 'v2'}}, - {key, method: 'upsert', data: {k3: 'v3'}}, + { key, method: 'insert', data: { k: 'v' } }, + { key, method: 'update', data: { k2: 'v2' } }, + { key, method: 'upsert', data: { k3: 'v3' } }, ], done ); @@ -1445,7 +1450,7 @@ describe('Datastore', () => { data: { value: { a: 'b', - c: [1, 2, 3], + c: [ds.Datastore.int(1), ds.Datastore.int(2), ds.Datastore.int(3)], }, }, }, @@ -1463,13 +1468,13 @@ describe('Datastore', () => { }); it('should return apiResponse in callback', done => { - const key = new entity.Key({namespace: 'ns', path: ['Company']}); + const key = new entity.Key({ namespace: 'ns', path: ['Company'] }); const mockCommitResponse = {}; datastore.request_ = (config: RequestConfig, callback: Function) => { callback(null, mockCommitResponse); }; datastore.save( - {key, data: {}}, + { key, data: {} }, (err: Error | null, apiResponse: Entity) => { assert.ifError(err); assert.strictEqual(mockCommitResponse, apiResponse); @@ -1820,9 +1825,9 @@ describe('Datastore', () => { }); it('should assign ID on keys without them', done => { - const incompleteKey = new entity.Key({path: ['Incomplete']}); - const incompleteKey2 = new entity.Key({path: ['Incomplete']}); - const completeKey = new entity.Key({path: ['Complete', 'Key']}); + const incompleteKey = new entity.Key({ path: ['Incomplete'] }); + const incompleteKey2 = new entity.Key({ path: ['Incomplete'] }); + const completeKey = new entity.Key({ path: ['Complete', 'Key'] }); const keyProtos: Array<{}> = []; const ids = [1, 2]; @@ -1852,9 +1857,9 @@ describe('Datastore', () => { datastore.save( [ - {key: incompleteKey, data: {}}, - {key: incompleteKey2, data: {}}, - {key: completeKey, data: {}}, + { key: incompleteKey, data: {} }, + { key: incompleteKey2, data: {} }, + { key: completeKey, data: {} }, ], (err: Error) => { assert.ifError(err); @@ -1882,7 +1887,7 @@ describe('Datastore', () => { it('should queue request & callback', () => { datastore.save({ key, - data: [{name: 'name', value: 'value'}], + data: [{ name: 'name', value: 'value' }], }); assert.strictEqual(typeof datastore.requestCallbacks_[0], 'function'); @@ -1898,7 +1903,7 @@ describe('Datastore', () => { it('should prepare entity objects', done => { const entityObject = {}; - const preparedEntityObject = {prepared: true}; + const preparedEntityObject = { prepared: true }; const expectedEntityObject = Object.assign({}, preparedEntityObject, { method: 'update', }); @@ -1934,8 +1939,8 @@ describe('Datastore', () => { callback(); }; - const key = new entity.Key({namespace: 'ns', path: ['Company']}); - datastore.update({key, data: {}}, done); + const key = new entity.Key({ namespace: 'ns', path: ['Company'] }); + datastore.update({ key, data: {} }, done); }); }); @@ -1946,7 +1951,7 @@ describe('Datastore', () => { it('should prepare entity objects', done => { const entityObject = {}; - const preparedEntityObject = {prepared: true}; + const preparedEntityObject = { prepared: true }; const expectedEntityObject = Object.assign({}, preparedEntityObject, { method: 'upsert', }); @@ -1983,8 +1988,8 @@ describe('Datastore', () => { callback(); }; - const key = new entity.Key({namespace: 'ns', path: ['Company']}); - datastore.upsert({key, data: {}}, done); + const key = new entity.Key({ namespace: 'ns', path: ['Company'] }); + datastore.upsert({ key, data: {} }, done); }); }); @@ -2047,7 +2052,7 @@ describe('Datastore', () => { }); it('should not set customEndpoint_ when using default baseurl', () => { - const datastore = new Datastore({projectId: PROJECT_ID}); + const datastore = new Datastore({ projectId: PROJECT_ID }); datastore.determineBaseUrl_(); assert.strictEqual(datastore.customEndpoint_, undefined); }); From 8a5aba88eb0e79e2d384b083795207a463a6e66c Mon Sep 17 00:00:00 2001 From: Chris Wilcox Date: Tue, 8 Dec 2020 09:10:47 -0800 Subject: [PATCH 02/36] fix!:add a warning when a number is autoconverted to double or int --- src/entity.ts | 50 ++++++++++++++------- system-test/datastore.ts | 96 ++++++++++++++++++++-------------------- test/entity.ts | 75 +++++++++++++++++++++++++------ test/index.ts | 11 ++--- 4 files changed, 148 insertions(+), 84 deletions(-) diff --git a/src/entity.ts b/src/entity.ts index f3e923ded..5fce17d89 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -21,6 +21,7 @@ import { PathType } from '.'; import { protobuf as Protobuf } from 'google-gax'; import * as path from 'path'; import { google } from '../protos/protos'; +import { type } from 'os'; // eslint-disable-next-line @typescript-eslint/no-namespace export namespace entity { @@ -216,7 +217,7 @@ export namespace entity { return ( isDsInt(maybeDsInt) || (is.object(maybeDsInt) && - (is.string(maybeDsInt.value) || is.number(maybeDsInt.value)) && + is.string(maybeDsInt.value) && maybeDsInt.type === 'DatastoreInt') ); } @@ -580,16 +581,47 @@ export namespace entity { return valueProto; } - if (isDsInt(value) || isDsIntLike(value)) { + if (isDsInt(value)) { valueProto.integerValue = value.value; return valueProto; } - if (isDsDouble(value) || isDsDoubleLike(value)) { + if (isDsDouble(value)) { valueProto.doubleValue = value.value; return valueProto; } + if (typeof value === 'number') { + const integerOutOfBoundsWarning = "'IntegerOutOfBoundsWarning: the value for '" + + property + + "' property is outside of bounds of a JavaScript Number.\n" + + "Use 'Datastore.int()' to preserve accuracy during the upload." + + const typeCastWarning = "TypeCastWarning: the value for '" + + property + + "' property is a JavaScript Number.\n" + + "Use 'Datastore.int()' or " + + "'Datastore.double()' to preserve consistent " + + "Datastore types during the upload." + + + if (Number.isInteger(value)) { + if (!Number.isSafeInteger(value)) { + process.emitWarning(integerOutOfBoundsWarning); + } else { + process.emitWarning(typeCastWarning); + } + value = new entity.Int(value); + valueProto.integerValue = value.value; + return valueProto; + } else { + process.emitWarning(typeCastWarning); + value = new entity.Double(value); + valueProto.doubleValue = value.value; + return valueProto; + } + } + if (isDsGeoPoint(value)) { valueProto.geoPointValue = value.value; return valueProto; @@ -631,24 +663,12 @@ export namespace entity { return valueProto; } - if (typeof value === 'number') { - const message = "The value for '" + - property + - "' property is a JavaScript Number. Use " + - "'Datastore.int()' or " + - "'Datastore.double()' to preserve " + - "accuracy during the upload." - - throw new Error(message); - } - if (is.object(value)) { if (!is.empty(value)) { value = extend(true, {}, value); for (const prop in value) { value[prop] = entity.encodeValue(value[prop], prop); - // value[prop] = entity.encodeValue(value, prop); } } diff --git a/system-test/datastore.ts b/system-test/datastore.ts index 4a0956fb3..f9398ffb9 100644 --- a/system-test/datastore.ts +++ b/system-test/datastore.ts @@ -13,13 +13,13 @@ // limitations under the License. import * as assert from 'assert'; -import { readFileSync } from 'fs'; +import {readFileSync} from 'fs'; import * as path from 'path'; -import { before, after, describe, it } from 'mocha'; +import {before, after, describe, it} from 'mocha'; import * as yaml from 'js-yaml'; -import { Datastore, Index } from '../src'; -import { google } from '../protos/protos'; -import { Storage } from '@google-cloud/storage'; +import {Datastore, Index} from '../src'; +import {google} from '../protos/protos'; +import {Storage} from '@google-cloud/storage'; describe('Datastore', () => { const testKinds: string[] = []; @@ -36,9 +36,9 @@ describe('Datastore', () => { return keyObject; }; - const { indexes: DECLARED_INDEXES } = yaml.safeLoad( + const {indexes: DECLARED_INDEXES} = yaml.safeLoad( readFileSync(path.join(__dirname, 'data', 'index.yaml'), 'utf8') - ) as { indexes: google.datastore.admin.v1.IIndex[] }; + ) as {indexes: google.datastore.admin.v1.IIndex[]}; // TODO/DX ensure indexes before testing, and maybe? cleanup indexes after // possible implications with kokoro project @@ -67,11 +67,11 @@ describe('Datastore', () => { publishedAt: new Date(), author: 'Silvano', isDraft: false, - wordCount: datastore.int(400), - rating: datastore.double(5.0), + wordCount: 400, + rating: 5.0, likes: null, metadata: { - views: datastore.int(100), + views: 100, }, }; @@ -261,8 +261,8 @@ describe('Datastore', () => { const postKey = datastore.key('Team'); const largeIntValueAsString = '9223372036854775807'; const points = Datastore.int(largeIntValueAsString); - await datastore.save({ key: postKey, data: { points } }); - const [entity] = await datastore.get(postKey, { wrapNumbers: true }); + await datastore.save({key: postKey, data: {points}}); + const [entity] = await datastore.get(postKey, {wrapNumbers: true}); assert.strictEqual(entity.points.value, largeIntValueAsString); assert.throws(() => entity.points.valueOf()); await datastore.delete(postKey); @@ -272,9 +272,9 @@ describe('Datastore', () => { const postKey = datastore.key('Scores'); const largeIntValueAsString = '9223372036854775807'; const panthers = Datastore.int(largeIntValueAsString); - const broncos = Datastore.int(922337203); + const broncos = 922337203; let integerTypeCastFunctionCalled = 0; - await datastore.save({ key: postKey, data: { panthers, broncos } }); + await datastore.save({key: postKey, data: {panthers, broncos}}); const [entity] = await datastore.get(postKey, { wrapNumbers: { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -295,7 +295,7 @@ describe('Datastore', () => { it('should save/get/delete with a key name', async () => { const postKey = datastore.key(['Post', 'post1']); - await datastore.save({ key: postKey, data: post }); + await datastore.save({key: postKey, data: post}); const [entity] = await datastore.get(postKey); assert.deepStrictEqual(entity[datastore.KEY], postKey); delete entity[datastore.KEY]; @@ -305,7 +305,7 @@ describe('Datastore', () => { it('should save/get/delete with a numeric key id', async () => { const postKey = datastore.key(['Post', 123456789]); - await datastore.save({ key: postKey, data: post }); + await datastore.save({key: postKey, data: post}); const [entity] = await datastore.get(postKey); delete entity[datastore.KEY]; assert.deepStrictEqual(entity, post); @@ -317,7 +317,7 @@ describe('Datastore', () => { const data = { buf: Buffer.from('010100000000000000000059400000000000006940', 'hex'), }; - await datastore.save({ key: postKey, data }); + await datastore.save({key: postKey, data}); const assignedId = postKey.id; assert(assignedId); const [entity] = await datastore.get(postKey); @@ -331,7 +331,7 @@ describe('Datastore', () => { const data = { buf: Buffer.from([]), }; - await datastore.save({ key: postKey, data }); + await datastore.save({key: postKey, data}); const assignedId = postKey.id; assert(assignedId); const [entity] = await datastore.get(postKey); @@ -342,7 +342,7 @@ describe('Datastore', () => { it('should save/get/delete with a generated key id', async () => { const postKey = datastore.key('Post'); - await datastore.save({ key: postKey, data: post }); + await datastore.save({key: postKey, data: post}); // The key's path should now be complete. assert(postKey.id); @@ -355,7 +355,7 @@ describe('Datastore', () => { it('should save/get/update', async () => { const postKey = datastore.key('Post'); - await datastore.save({ key: postKey, data: post }); + await datastore.save({key: postKey, data: post}); const [entity] = await datastore.get(postKey); assert.strictEqual(entity.title, post.title); entity.title = 'Updated'; @@ -405,7 +405,7 @@ describe('Datastore', () => { it('should fail explicitly set second insert on save', async () => { const postKey = datastore.key('Post'); - await datastore.save({ key: postKey, data: post }); + await datastore.save({key: postKey, data: post}); // The key's path should now be complete. assert(postKey.id); @@ -440,14 +440,14 @@ describe('Datastore', () => { publishedAt: new Date('2001-01-01T00:00:00.000Z'), author: 'Silvano', isDraft: false, - wordCount: datastore.int(450), - rating: datastore.double(4.5), + wordCount: 450, + rating: 4.5, }; const key1 = datastore.key('Post'); const key2 = datastore.key('Post'); await datastore.save([ - { key: key1, data: post }, - { key: key2, data: post2 }, + {key: key1, data: post}, + {key: key2, data: post2}, ]); const [entities] = await datastore.get([key1, key2]); assert.strictEqual(entities.length, 2); @@ -460,8 +460,8 @@ describe('Datastore', () => { datastore.save( [ - { key: key1, data: post }, - { key: key2, data: post }, + {key: key1, data: post}, + {key: key2, data: post}, ], err => { assert.ifError(err); @@ -570,49 +570,49 @@ describe('Datastore', () => { { name: 'Rickard', family: 'Stark', - appearances: datastore.int(9), + appearances: 9, alive: false, }, { name: 'Eddard', family: 'Stark', - appearances: datastore.int(9), + appearances: 9, alive: false, }, { name: 'Catelyn', family: ['Stark', 'Tully'], - appearances: datastore.int(26), + appearances: 26, alive: false, }, { name: 'Arya', family: 'Stark', - appearances: datastore.int(33), + appearances: 33, alive: true, }, { name: 'Sansa', family: 'Stark', - appearances: datastore.int(31), + appearances: 31, alive: true, }, { name: 'Robb', family: 'Stark', - appearances: datastore.int(22), + appearances: 22, alive: false, }, { name: 'Bran', family: 'Stark', - appearances: datastore.int(25), + appearances: 25, alive: true, }, { name: 'Jon Snow', family: 'Stark', - appearances: datastore.int(32), + appearances: 32, alive: true, }, ]; @@ -715,7 +715,7 @@ describe('Datastore', () => { const q = datastore .createQuery('Character') .hasAncestor(ancestor) - .filter('appearances', '>=', datastore.int(20)); + .filter('appearances', '>=', 20); const [entities] = await datastore.runQuery(q); assert.strictEqual(entities!.length, 6); }); @@ -725,7 +725,7 @@ describe('Datastore', () => { .createQuery('Character') .hasAncestor(ancestor) .filter('family', 'Stark') - .filter('appearances', '>=', datastore.int(20)); + .filter('appearances', '>=', 20); const [entities] = await datastore.runQuery(q); assert.strictEqual(entities!.length, 6); }); @@ -841,7 +841,7 @@ describe('Datastore', () => { const transaction = datastore.transaction(); await transaction.run(); await transaction.get(key); - transaction.save({ key, data: obj }); + transaction.save({key, data: obj}); await transaction.commit(); const [entity] = await datastore.get(key); delete entity[datastore.KEY]; @@ -865,11 +865,11 @@ describe('Datastore', () => { transaction.save([ { key, - data: { rating: datastore.int(10) }, + data: {rating: 10}, }, { key: incompleteKey, - data: { rating: datastore.int(100) }, + data: {rating: 100}, }, ]); @@ -885,7 +885,7 @@ describe('Datastore', () => { datastore.get(key), ]); assert.strictEqual(typeof deletedEntity, 'undefined'); - assert.strictEqual(fetchedEntity.rating, datastore.int(10)); + assert.strictEqual(fetchedEntity.rating, 10); }); it('should use the last modification to a key', async () => { @@ -897,13 +897,13 @@ describe('Datastore', () => { { key, data: { - rating: datastore.int(10), + rating: 10, }, }, { key: incompleteKey, data: { - rating: datastore.int(100), + rating: 100, }, }, ]); @@ -934,18 +934,18 @@ describe('Datastore', () => { }); it('should read in a readOnly transaction', async () => { - const transaction = datastore.transaction({ readOnly: true }); + const transaction = datastore.transaction({readOnly: true}); const key = datastore.key(['Company', 'Google']); await transaction.run(); await transaction.get(key); }); it('should not write in a readOnly transaction', async () => { - const transaction = datastore.transaction({ readOnly: true }); + const transaction = datastore.transaction({readOnly: true}); const key = datastore.key(['Company', 'Google']); await transaction.run(); await transaction.get(key); - transaction.save({ key, data: {} }); + transaction.save({key, data: {}}); await assert.rejects(transaction.commit()); }); }); @@ -1008,10 +1008,10 @@ describe('Datastore', () => { const bucket = gcs.bucket('nodejs-datastore-system-tests'); it('should export, then import entities', async () => { - const [exportOperation] = await datastore.export({ bucket }); + const [exportOperation] = await datastore.export({bucket}); await exportOperation.promise(); - const [files] = await bucket.getFiles({ maxResults: 1 }); + const [files] = await bucket.getFiles({maxResults: 1}); const [exportedFile] = files; assert.ok(exportedFile.name.includes('overall_export_metadata')); diff --git a/test/entity.ts b/test/entity.ts index bbd9a8941..f3dd0381d 100644 --- a/test/entity.ts +++ b/test/entity.ts @@ -744,17 +744,48 @@ describe('entity', () => { assert.deepStrictEqual(entity.encodeValue(value), expectedValueProto); }); - it('should throw if an invalid value was provided', () => { - assert.throws(() => { - entity.encodeValue(); - }, /Unsupported field value/); - }); - it('should encode an int', () => { + it('should encode an int', done => { const value = 8; - assert.throws(() => { - entity.encodeValue(value); - }, /The value for 'undefined' property is a JavaScript Number/); + const expectedValueProto = { + integerValue: value, + }; + + const property = "value" + const expectedWarning = "TypeCastWarning: the value for '" + + property + + "' property is a JavaScript Number.\n" + + "Use 'Datastore.int()' or " + + "'Datastore.double()' to preserve consistent " + + "Datastore types during the upload." + process.on('warning', warning => { + assert.strictEqual(warning.message, expectedWarning); + done(); + }); + + entity.Int = function (value_: {}) { + assert.strictEqual(value_, value); + this.value = value_; + }; + + assert.deepStrictEqual(entity.encodeValue(value), expectedValueProto); + }); + + it('should emit warning on out of bounds int', done => { + const largeIntValue = 9223372036854775807; + const property = 'largeInt'; + const expectedWarning = + 'IntegerOutOfBoundsWarning: ' + + "the value for '" + + property + + "' property is outside of bounds of a JavaScript Number.\n" + + "Use 'Datastore.int()' to preserve accuracy during the upload."; + + process.on('warning', warning => { + assert.strictEqual(warning.message, expectedWarning); + done(); + }); + entity.encodeValue(largeIntValue, property); }); it('should encode an Int object', () => { @@ -767,14 +798,32 @@ describe('entity', () => { assert.deepStrictEqual(entity.encodeValue(value), expectedValueProto); }); - it('should encode a double', () => { + it('should encode a double', done => { const value = 8.3; + const expectedValueProto = { + doubleValue: value, + }; - assert.throws(() => { - entity.encodeValue(value); - }, /The value for 'undefined' property is a JavaScript Number/); + const property = "value" + const expectedWarning = "TypeCastWarning: the value for '" + + property + + "' property is a JavaScript Number.\n" + + "Use 'Datastore.int()' or " + + "'Datastore.double()' to preserve consistent " + + "Datastore types during the upload." + process.on('warning', warning => { + assert.strictEqual(warning.message, expectedWarning); + done(); + }); + + entity.Double = function (value_: {}) { + assert.strictEqual(value_, value); + this.value = value_; + }; + + assert.deepStrictEqual(entity.encodeValue(value), expectedValueProto); }); it('should encode a Double object', () => { diff --git a/test/index.ts b/test/index.ts index c6d44c743..cec680afe 100644 --- a/test/index.ts +++ b/test/index.ts @@ -34,10 +34,8 @@ const fakeEntity: any = { KEY_SYMBOL: Symbol('fake key symbol'), Int: class { value: {}; - type: string; constructor(value: {}) { this.value = value; - this.type = 'DatastoreInt'; } }, // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -46,11 +44,8 @@ const fakeEntity: any = { }, Double: class { value: {}; - type: string; - constructor(value: {}) { this.value = value; - this.type = 'DatastoreDouble'; } }, // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -1321,7 +1316,7 @@ describe('Datastore', () => { arrayValue: { values: [ { - integerValue: 0, + integerValue: '0', }, { nullValue: 0, @@ -1347,7 +1342,7 @@ describe('Datastore', () => { data: { stringField: 'string value', nullField: null, - arrayField: [Datastore.int(0), null], + arrayField: [datastore.int(0), null], objectField: null, }, excludeLargeProperties: true, @@ -1450,7 +1445,7 @@ describe('Datastore', () => { data: { value: { a: 'b', - c: [ds.Datastore.int(1), ds.Datastore.int(2), ds.Datastore.int(3)], + c: [datastore.int(1), datastore.int(2), datastore.int(3)], }, }, }, From 068b0bfe37fd8ee3c4ed50dd87a6e2c9c268a5e0 Mon Sep 17 00:00:00 2001 From: Chris Wilcox Date: Tue, 8 Dec 2020 11:01:45 -0800 Subject: [PATCH 03/36] chore: gts fix --- src/entity.ts | 51 +++++++++--------- test/entity.ts | 92 ++++++++++++++++---------------- test/index.ts | 142 ++++++++++++++++++++++++------------------------- 3 files changed, 144 insertions(+), 141 deletions(-) diff --git a/src/entity.ts b/src/entity.ts index 5fce17d89..17bde8bd0 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -16,12 +16,12 @@ import arrify = require('arrify'); import * as extend from 'extend'; import * as is from 'is'; -import { Query, QueryProto, IntegerTypeCastOptions } from './query'; -import { PathType } from '.'; -import { protobuf as Protobuf } from 'google-gax'; +import {Query, QueryProto, IntegerTypeCastOptions} from './query'; +import {PathType} from '.'; +import {protobuf as Protobuf} from 'google-gax'; import * as path from 'path'; -import { google } from '../protos/protos'; -import { type } from 'os'; +import {google} from '../protos/protos'; +import {type} from 'os'; // eslint-disable-next-line @typescript-eslint/no-namespace export namespace entity { @@ -34,7 +34,7 @@ export namespace entity { const errorMessages = { MISSING_KIND: 'A key should contain at least a kind.', MISSING_ANCESTOR_ID: 'Ancestor keys require an id or name.', - } as { [index: string]: string }; + } as {[index: string]: string}; super(errorMessages[opts.code]); this.name = 'InvalidKey'; } @@ -189,7 +189,7 @@ export namespace entity { } toJSON(): Json { - return { type: this.type, value: this.value }; + return {type: this.type, value: this.value}; } } @@ -447,16 +447,16 @@ export namespace entity { if (!Number.isSafeInteger(num)) { throw new Error( 'We attempted to return all of the numeric values, but ' + - (value.propertyName ? value.propertyName + ' ' : '') + - 'value ' + - value.integerValue + - " is out of bounds of 'Number.MAX_SAFE_INTEGER'.\n" + - "To prevent this error, please consider passing 'options.wrapNumbers=true' or\n" + - "'options.wrapNumbers' as\n" + - '{\n' + - ' integerTypeCastFunction: provide \n' + - ' properties: optionally specify property name(s) to be custom casted\n' + - '}\n' + (value.propertyName ? value.propertyName + ' ' : '') + + 'value ' + + value.integerValue + + " is out of bounds of 'Number.MAX_SAFE_INTEGER'.\n" + + "To prevent this error, please consider passing 'options.wrapNumbers=true' or\n" + + "'options.wrapNumbers' as\n" + + '{\n' + + ' integerTypeCastFunction: provide \n' + + ' properties: optionally specify property name(s) to be custom casted\n' + + '}\n' ); } return num; @@ -592,18 +592,19 @@ export namespace entity { } if (typeof value === 'number') { - const integerOutOfBoundsWarning = "'IntegerOutOfBoundsWarning: the value for '" + + const integerOutOfBoundsWarning = + "'IntegerOutOfBoundsWarning: the value for '" + property + "' property is outside of bounds of a JavaScript Number.\n" + - "Use 'Datastore.int()' to preserve accuracy during the upload." + "Use 'Datastore.int()' to preserve accuracy during the upload."; - const typeCastWarning = "TypeCastWarning: the value for '" + + const typeCastWarning = + "TypeCastWarning: the value for '" + property + "' property is a JavaScript Number.\n" + "Use 'Datastore.int()' or " + "'Datastore.double()' to preserve consistent " + - "Datastore types during the upload." - + 'Datastore types during the upload.'; if (Number.isInteger(value)) { if (!Number.isSafeInteger(value)) { @@ -1332,7 +1333,7 @@ export namespace entity { const reference = { app: projectId, namespace: key.namespace, - path: { element: elements }, + path: {element: elements}, }; const buffer = this.protos.Reference.encode(reference).finish(); @@ -1429,7 +1430,7 @@ export interface ValueProto { export interface EntityProto { key?: KeyProto | null; - properties?: { [k: string]: ValueProto }; + properties?: {[k: string]: ValueProto}; excludeFromIndexes?: boolean; } @@ -1453,7 +1454,7 @@ export interface ResponseResult { } export interface EntityObject { - data: { [k: string]: Entity }; + data: {[k: string]: Entity}; excludeFromIndexes: string[]; } diff --git a/test/entity.ts b/test/entity.ts index f3dd0381d..651ff26b1 100644 --- a/test/entity.ts +++ b/test/entity.ts @@ -13,12 +13,12 @@ // limitations under the License. import * as assert from 'assert'; -import { beforeEach, afterEach, describe, it } from 'mocha'; +import {beforeEach, afterEach, describe, it} from 'mocha'; import * as extend from 'extend'; import * as sinon from 'sinon'; -import { Datastore } from '../src'; -import { Entity } from '../src/entity'; -import { IntegerTypeCastOptions } from '../src/query'; +import {Datastore} from '../src'; +import {Entity} from '../src/entity'; +import {IntegerTypeCastOptions} from '../src/query'; export function outOfBoundsError(opts: { propertyName?: string; @@ -26,16 +26,16 @@ export function outOfBoundsError(opts: { }) { return new Error( 'We attempted to return all of the numeric values, but ' + - (opts.propertyName ? opts.propertyName + ' ' : '') + - 'value ' + - opts.integerValue + - " is out of bounds of 'Number.MAX_SAFE_INTEGER'.\n" + - "To prevent this error, please consider passing 'options.wrapNumbers=true' or\n" + - "'options.wrapNumbers' as\n" + - '{\n' + - ' integerTypeCastFunction: provide \n' + - ' properties: optionally specify property name(s) to be custom casted\n' + - '}\n' + (opts.propertyName ? opts.propertyName + ' ' : '') + + 'value ' + + opts.integerValue + + " is out of bounds of 'Number.MAX_SAFE_INTEGER'.\n" + + "To prevent this error, please consider passing 'options.wrapNumbers=true' or\n" + + "'options.wrapNumbers' as\n" + + '{\n' + + ' integerTypeCastFunction: provide \n' + + ' properties: optionally specify property name(s) to be custom casted\n' + + '}\n' ); } @@ -156,12 +156,12 @@ describe('entity', () => { // should throw when Number is passed assert.throws(() => { new entity.Int(largeIntegerValue).valueOf(); - }, outOfBoundsError({ integerValue: largeIntegerValue })); + }, outOfBoundsError({integerValue: largeIntegerValue})); // should throw when string is passed assert.throws(() => { new entity.Int(smallIntegerValue.toString()).valueOf(); - }, outOfBoundsError({ integerValue: smallIntegerValue })); + }, outOfBoundsError({integerValue: smallIntegerValue})); }); it('should not auto throw on initialization', () => { @@ -272,12 +272,12 @@ describe('entity', () => { describe('isDsGeoPoint', () => { it('should correctly identify a GeoPoint', () => { - const geoPoint = new entity.GeoPoint({ latitude: 24, longitude: 88 }); + const geoPoint = new entity.GeoPoint({latitude: 24, longitude: 88}); assert.strictEqual(entity.isDsGeoPoint(geoPoint), true); }); it('should correctly identify a homomorphic non-GeoPoint', () => { - const geoPoint = new entity.GeoPoint({ latitude: 24, longitude: 88 }); + const geoPoint = new entity.GeoPoint({latitude: 24, longitude: 88}); const nonGeoPoint = Object.assign({}, geoPoint); assert.strictEqual(entity.isDsGeoPoint(nonGeoPoint), false); }); @@ -286,47 +286,47 @@ describe('entity', () => { describe('Key', () => { it('should assign the namespace', () => { const namespace = 'NS'; - const key = new entity.Key({ namespace, path: [] }); + const key = new entity.Key({namespace, path: []}); assert.strictEqual(key.namespace, namespace); }); it('should assign the kind', () => { const kind = 'kind'; - const key = new entity.Key({ path: [kind] }); + const key = new entity.Key({path: [kind]}); assert.strictEqual(key.kind, kind); }); it('should assign the ID', () => { const id = 11; - const key = new entity.Key({ path: ['Kind', id] }); + const key = new entity.Key({path: ['Kind', id]}); assert.strictEqual(key.id, id); }); it('should assign the ID from an Int', () => { const id = new entity.Int(11); - const key = new entity.Key({ path: ['Kind', id] }); + const key = new entity.Key({path: ['Kind', id]}); assert.strictEqual(key.id, id.value); }); it('should assign the name', () => { const name = 'name'; - const key = new entity.Key({ path: ['Kind', name] }); + const key = new entity.Key({path: ['Kind', name]}); assert.strictEqual(key.name, name); }); it('should assign a parent', () => { - const key = new entity.Key({ path: ['ParentKind', 1, 'Kind', 1] }); + const key = new entity.Key({path: ['ParentKind', 1, 'Kind', 1]}); assert(key.parent instanceof entity.Key); }); it('should not modify input path', () => { const inputPath = ['ParentKind', 1, 'Kind', 1]; - new entity.Key({ path: inputPath }); + new entity.Key({path: inputPath}); assert.deepStrictEqual(inputPath, ['ParentKind', 1, 'Kind', 1]); }); it('should always compute the correct path', () => { - const key = new entity.Key({ path: ['ParentKind', 1, 'Kind', 1] }); + const key = new entity.Key({path: ['ParentKind', 1, 'Kind', 1]}); assert.deepStrictEqual(key.path, ['ParentKind', 1, 'Kind', 1]); key.parent.kind = 'GrandParentKind'; @@ -373,12 +373,12 @@ describe('entity', () => { describe('isDsKey', () => { it('should correctly identify a Key', () => { - const key = new entity.Key({ path: ['Kind', 1] }); + const key = new entity.Key({path: ['Kind', 1]}); assert.strictEqual(entity.isDsKey(key), true); }); it('should correctly identify a homomorphic non-Key', () => { - const notKey = Object.assign({}, new entity.Key({ path: ['Kind', 1] })); + const notKey = Object.assign({}, new entity.Key({path: ['Kind', 1]})); assert.strictEqual(entity.isDsKey(notKey), false); }); }); @@ -594,7 +594,7 @@ describe('entity', () => { }); it('should wrap ints with wrapNumbers as object', () => { - const wrapNumbers = { integerTypeCastFunction: () => { } }; + const wrapNumbers = {integerTypeCastFunction: () => {}}; const stub = sinon.spy(entity, 'Int'); entity.decodeValueProto(valueProto, wrapNumbers); @@ -602,11 +602,11 @@ describe('entity', () => { }); it('should call #valueOf if integerTypeCastFunction is provided', () => { - Object.assign(valueProto, { integerValue: Number.MAX_SAFE_INTEGER }); + Object.assign(valueProto, {integerValue: Number.MAX_SAFE_INTEGER}); const takeFirstTen = sinon .stub() .callsFake((value: string) => value.toString().substr(0, 10)); - const wrapNumbers = { integerTypeCastFunction: takeFirstTen }; + const wrapNumbers = {integerTypeCastFunction: takeFirstTen}; assert.strictEqual( entity.decodeValueProto(valueProto, wrapNumbers), @@ -751,13 +751,14 @@ describe('entity', () => { integerValue: value, }; - const property = "value" - const expectedWarning = "TypeCastWarning: the value for '" + + const property = 'value'; + const expectedWarning = + "TypeCastWarning: the value for '" + property + "' property is a JavaScript Number.\n" + "Use 'Datastore.int()' or " + "'Datastore.double()' to preserve consistent " + - "Datastore types during the upload." + 'Datastore types during the upload.'; process.on('warning', warning => { assert.strictEqual(warning.message, expectedWarning); done(); @@ -805,13 +806,14 @@ describe('entity', () => { doubleValue: value, }; - const property = "value" - const expectedWarning = "TypeCastWarning: the value for '" + + const property = 'value'; + const expectedWarning = + "TypeCastWarning: the value for '" + property + "' property is a JavaScript Number.\n" + "Use 'Datastore.int()' or " + "'Datastore.double()' to preserve consistent " + - "Datastore types during the upload." + 'Datastore types during the upload.'; process.on('warning', warning => { assert.strictEqual(warning.message, expectedWarning); @@ -1010,7 +1012,7 @@ describe('entity', () => { }); describe('should pass `wrapNumbers` to decodeValueProto', () => { - const entityProto = { properties: { number: {} } }; + const entityProto = {properties: {number: {}}}; let decodeValueProtoStub: sinon.SinonStub; let wrapNumbers: boolean | IntegerTypeCastOptions | undefined; @@ -1042,7 +1044,7 @@ describe('entity', () => { it('should pass `wrapNumbers` to decodeValueProto as IntegerTypeCastOptions', () => { const integerTypeCastOptions = { - integerTypeCastFunction: () => { }, + integerTypeCastFunction: () => {}, properties: 'that', }; @@ -1176,7 +1178,7 @@ describe('entity', () => { nestedArrayVariants: [ { - a: [{ b: value13 }, { c: value14 }], + a: [{b: value13}, {c: value14}], }, { a: null, @@ -1185,7 +1187,7 @@ describe('entity', () => { a: [value15], }, { - a: [{ b: ['nasty', 'array'] }], + a: [{b: ['nasty', 'array']}], }, ], @@ -1540,7 +1542,7 @@ describe('entity', () => { }); describe('should pass `wrapNumbers` to entityFromEntityProto', () => { - const results = [{ entity: {} }]; + const results = [{entity: {}}]; // eslint-disable-next-line @typescript-eslint/no-explicit-any let entityFromEntityProtoStub: any; let wrapNumbers: boolean | IntegerTypeCastOptions | undefined; @@ -1570,7 +1572,7 @@ describe('entity', () => { it('should pass `wrapNumbers` to entityFromEntityProto as IntegerTypeCastOptions', () => { const integerTypeCastOptions = { - integerTypeCastFunction: () => { }, + integerTypeCastFunction: () => {}, properties: 'that', }; @@ -1890,7 +1892,7 @@ describe('entity', () => { path: ['Kind2', 'somename'], }); - const ds = new Datastore({ projectId: 'project-id' }); + const ds = new Datastore({projectId: 'project-id'}); const query = ds .createQuery('Kind1') @@ -1908,7 +1910,7 @@ describe('entity', () => { }); it('should handle buffer start and end values', () => { - const ds = new Datastore({ projectId: 'project-id' }); + const ds = new Datastore({projectId: 'project-id'}); const startVal = Buffer.from('start'); const endVal = Buffer.from('end'); diff --git a/test/index.ts b/test/index.ts index cec680afe..625094a1c 100644 --- a/test/index.ts +++ b/test/index.ts @@ -13,15 +13,15 @@ // limitations under the License. import * as assert from 'assert'; -import { before, beforeEach, after, afterEach, describe, it } from 'mocha'; +import {before, beforeEach, after, afterEach, describe, it} from 'mocha'; import * as gax from 'google-gax'; import * as proxyquire from 'proxyquire'; -import { PassThrough, Readable } from 'stream'; +import {PassThrough, Readable} from 'stream'; import * as ds from '../src'; -import { DatastoreOptions } from '../src'; -import { entity, Entity, EntityProto, EntityObject } from '../src/entity'; -import { RequestConfig } from '../src/request'; +import {DatastoreOptions} from '../src'; +import {entity, Entity, EntityProto, EntityObject} from '../src/entity'; +import {RequestConfig} from '../src/request'; import * as is from 'is'; import * as sinon from 'sinon'; import * as extend from 'extend'; @@ -84,7 +84,7 @@ const fakeEntity: any = { let googleAuthOverride: Function | null; function fakeGoogleAuth(...args: Array<{}>) { - return (googleAuthOverride || (() => { }))(...args); + return (googleAuthOverride || (() => {}))(...args); } let createInsecureOverride: Function | null; @@ -99,7 +99,7 @@ const fakeGoogleGax = { credentials: { // eslint-disable-next-line @typescript-eslint/no-explicit-any createInsecure(...args: any[]) { - return (createInsecureOverride || (() => { }))(...args); + return (createInsecureOverride || (() => {}))(...args); }, }, } as {}) as gax.GrpcModule; @@ -133,7 +133,7 @@ class FakeTransaction { } } -function FakeV1() { } +function FakeV1() {} const sandbox = sinon.createSandbox(); @@ -158,10 +158,10 @@ describe('Datastore', () => { before(() => { Datastore = proxyquire('../src', { - './entity.js': { entity: fakeEntity }, - './index-class.js': { Index: FakeIndex }, - './query.js': { Query: FakeQuery }, - './transaction.js': { Transaction: FakeTransaction }, + './entity.js': {entity: fakeEntity}, + './index-class.js': {Index: FakeIndex}, + './query.js': {Query: FakeQuery}, + './transaction.js': {Transaction: FakeTransaction}, './v1': FakeV1, 'google-auth-library': { GoogleAuth: fakeGoogleAuth, @@ -330,13 +330,13 @@ describe('Datastore', () => { describe('geoPoint', () => { it('should expose GeoPoint builder', () => { - const aGeoPoint = { latitude: 24, longitude: 88 }; + const aGeoPoint = {latitude: 24, longitude: 88}; const geoPoint = Datastore.geoPoint(aGeoPoint); assert.strictEqual(geoPoint.value, aGeoPoint); }); it('should also be on the prototype', () => { - const aGeoPoint = { latitude: 24, longitude: 88 }; + const aGeoPoint = {latitude: 24, longitude: 88}; const geoPoint = datastore.geoPoint(aGeoPoint); assert.strictEqual(geoPoint.value, aGeoPoint); }); @@ -380,7 +380,7 @@ describe('Datastore', () => { describe('isGeoPoint', () => { it('should pass value to entity', () => { - const value = { fakeLatitude: 1, fakeLongitude: 2 }; + const value = {fakeLatitude: 1, fakeLongitude: 2}; let called = false; const saved = fakeEntity.isDsGeoPoint; fakeEntity.isDsGeoPoint = (arg: {}) => { @@ -424,7 +424,7 @@ describe('Datastore', () => { describe('isKey', () => { it('should pass value to entity', () => { - const value = { zz: true }; + const value = {zz: true}; let called = false; const saved = fakeEntity.isDsKey; fakeEntity.isDsKey = (arg: {}) => { @@ -538,7 +538,7 @@ describe('Datastore', () => { done(); }; - datastore.export({ bucket }, assert.ifError); + datastore.export({bucket}, assert.ifError); }); it('should remove extraneous gs:// prefix from input', done => { @@ -550,11 +550,11 @@ describe('Datastore', () => { done(); }; - datastore.export({ bucket }, assert.ifError); + datastore.export({bucket}, assert.ifError); }); it('should accept a Bucket object destination', done => { - const bucket = { name: 'bucket' }; + const bucket = {name: 'bucket'}; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any) => { @@ -565,7 +565,7 @@ describe('Datastore', () => { done(); }; - datastore.export({ bucket }, assert.ifError); + datastore.export({bucket}, assert.ifError); }); it('should throw if a destination is not provided', () => { @@ -588,7 +588,7 @@ describe('Datastore', () => { it('should accept kinds', done => { const kinds = ['kind1', 'kind2']; - const config = { bucket: 'bucket', kinds }; + const config = {bucket: 'bucket', kinds}; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any) => { @@ -614,7 +614,7 @@ describe('Datastore', () => { it('should accept namespaces', done => { const namespaces = ['ns1', 'n2']; - const config = { bucket: 'bucket', namespaces }; + const config = {bucket: 'bucket', namespaces}; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any) => { @@ -663,7 +663,7 @@ describe('Datastore', () => { it('should send any user input to API', done => { const userProperty = 'abc'; - const config = { bucket: 'bucket', userProperty }; + const config = {bucket: 'bucket', userProperty}; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any) => { @@ -675,7 +675,7 @@ describe('Datastore', () => { }); it('should send correct request', done => { - const config = { bucket: 'bucket' }; + const config = {bucket: 'bucket'}; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any) => { @@ -690,7 +690,7 @@ describe('Datastore', () => { it('should accept gaxOptions', done => { const gaxOptions = {}; - const config = { bucket: 'bucket', gaxOptions }; + const config = {bucket: 'bucket', gaxOptions}; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any) => { @@ -704,7 +704,7 @@ describe('Datastore', () => { describe('getIndexes', () => { it('should send the correct request', done => { - const options = { a: 'b' }; + const options = {a: 'b'}; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any) => { @@ -789,7 +789,7 @@ describe('Datastore', () => { it('should accept gaxOptions', done => { const options = { - gaxOptions: { a: 'b' }, + gaxOptions: {a: 'b'}, }; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -804,7 +804,7 @@ describe('Datastore', () => { it('should not send gaxOptions as request options', done => { const options = { - gaxOptions: { a: 'b' }, + gaxOptions: {a: 'b'}, }; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -868,7 +868,7 @@ describe('Datastore', () => { }); it('should execute callback with Index instances', done => { - const rawIndex = { indexId: 'name', a: 'b' }; + const rawIndex = {indexId: 'name', a: 'b'}; const indexInstance = {}; datastore.index = (id: string) => { @@ -892,8 +892,8 @@ describe('Datastore', () => { }); it('should execute callback with prepared nextQuery', done => { - const options = { pageToken: '1' }; - const nextQuery = { pageToken: '2' }; + const options = {pageToken: '1'}; + const nextQuery = {pageToken: '2'}; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any, callback: Function) => { @@ -913,7 +913,7 @@ describe('Datastore', () => { describe('getIndexesStream', () => { it('should make correct request', done => { - const options = { a: 'b' }; + const options = {a: 'b'}; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.requestStream_ = (config: any) => { @@ -931,7 +931,7 @@ describe('Datastore', () => { }); it('should accept gaxOptions', done => { - const options = { gaxOptions: {} }; + const options = {gaxOptions: {}}; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.requestStream_ = (config: any) => { @@ -944,7 +944,7 @@ describe('Datastore', () => { }); it('should transform response indexes into Index objects', done => { - const rawIndex = { indexId: 'name', a: 'b' }; + const rawIndex = {indexId: 'name', a: 'b'}; const indexInstance = {}; const requestStream = new Readable({ objectMode: true, @@ -996,7 +996,7 @@ describe('Datastore', () => { done(); }; - datastore.import({ file }, assert.ifError); + datastore.import({file}, assert.ifError); }); it('should remove extraneous gs:// prefix from input', done => { @@ -1008,11 +1008,11 @@ describe('Datastore', () => { done(); }; - datastore.import({ file }, assert.ifError); + datastore.import({file}, assert.ifError); }); it('should accept a File object source', done => { - const file = { bucket: { name: 'bucket' }, name: 'file' }; + const file = {bucket: {name: 'bucket'}, name: 'file'}; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any) => { @@ -1023,7 +1023,7 @@ describe('Datastore', () => { done(); }; - datastore.import({ file }, assert.ifError); + datastore.import({file}, assert.ifError); }); it('should throw if a source is not provided', () => { @@ -1034,7 +1034,7 @@ describe('Datastore', () => { it('should accept kinds', done => { const kinds = ['kind1', 'kind2']; - const config = { file: 'file', kinds }; + const config = {file: 'file', kinds}; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any) => { @@ -1060,7 +1060,7 @@ describe('Datastore', () => { it('should accept namespaces', done => { const namespaces = ['ns1', 'n2']; - const config = { file: 'file', namespaces }; + const config = {file: 'file', namespaces}; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any) => { @@ -1109,7 +1109,7 @@ describe('Datastore', () => { it('should send any user input to API', done => { const userProperty = 'abc'; - const config = { file: 'file', userProperty }; + const config = {file: 'file', userProperty}; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any) => { @@ -1121,7 +1121,7 @@ describe('Datastore', () => { }); it('should send correct request', done => { - const config = { file: 'file' }; + const config = {file: 'file'}; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any) => { @@ -1136,7 +1136,7 @@ describe('Datastore', () => { it('should accept gaxOptions', done => { const gaxOptions = {}; - const config = { file: 'file', gaxOptions }; + const config = {file: 'file', gaxOptions}; // eslint-disable-next-line @typescript-eslint/no-explicit-any datastore.request_ = (config: any) => { @@ -1167,7 +1167,7 @@ describe('Datastore', () => { it('should prepare entity objects', done => { const entityObject = {}; - const preparedEntityObject = { prepared: true }; + const preparedEntityObject = {prepared: true}; const expectedEntityObject = Object.assign({}, preparedEntityObject, { method: 'insert', }); @@ -1202,8 +1202,8 @@ describe('Datastore', () => { ]); callback(); }; - const key = new entity.Key({ namespace: 'ns', path: ['Company'] }); - datastore.insert({ key, data: {} }, done); + const key = new entity.Key({namespace: 'ns', path: ['Company']}); + datastore.insert({key, data: {}}, done); }); }); @@ -1297,8 +1297,8 @@ describe('Datastore', () => { }; datastore.save( [ - { key, data: { k: 'v' } }, - { key, data: { k: 'v' } }, + {key, data: {k: 'v'}}, + {key, data: {k: 'v'}}, ], done ); @@ -1380,7 +1380,7 @@ describe('Datastore', () => { return { key, method: 'insert', - data: { k: 'v' }, + data: {k: 'v'}, } as {}; }); @@ -1400,22 +1400,22 @@ describe('Datastore', () => { assert(is.object(config.reqOpts!.mutations![2].upsert)); const insert = config.reqOpts!.mutations![0].insert!; - assert.deepStrictEqual(insert.properties!.k, { stringValue: 'v' }); + assert.deepStrictEqual(insert.properties!.k, {stringValue: 'v'}); const update = config.reqOpts!.mutations![1].update!; - assert.deepStrictEqual(update.properties!.k2, { stringValue: 'v2' }); + assert.deepStrictEqual(update.properties!.k2, {stringValue: 'v2'}); const upsert = config.reqOpts!.mutations![2].upsert!; - assert.deepStrictEqual(upsert.properties!.k3, { stringValue: 'v3' }); + assert.deepStrictEqual(upsert.properties!.k3, {stringValue: 'v3'}); callback(); }; datastore.save( [ - { key, method: 'insert', data: { k: 'v' } }, - { key, method: 'update', data: { k2: 'v2' } }, - { key, method: 'upsert', data: { k3: 'v3' } }, + {key, method: 'insert', data: {k: 'v'}}, + {key, method: 'update', data: {k2: 'v2'}}, + {key, method: 'upsert', data: {k3: 'v3'}}, ], done ); @@ -1463,13 +1463,13 @@ describe('Datastore', () => { }); it('should return apiResponse in callback', done => { - const key = new entity.Key({ namespace: 'ns', path: ['Company'] }); + const key = new entity.Key({namespace: 'ns', path: ['Company']}); const mockCommitResponse = {}; datastore.request_ = (config: RequestConfig, callback: Function) => { callback(null, mockCommitResponse); }; datastore.save( - { key, data: {} }, + {key, data: {}}, (err: Error | null, apiResponse: Entity) => { assert.ifError(err); assert.strictEqual(mockCommitResponse, apiResponse); @@ -1820,9 +1820,9 @@ describe('Datastore', () => { }); it('should assign ID on keys without them', done => { - const incompleteKey = new entity.Key({ path: ['Incomplete'] }); - const incompleteKey2 = new entity.Key({ path: ['Incomplete'] }); - const completeKey = new entity.Key({ path: ['Complete', 'Key'] }); + const incompleteKey = new entity.Key({path: ['Incomplete']}); + const incompleteKey2 = new entity.Key({path: ['Incomplete']}); + const completeKey = new entity.Key({path: ['Complete', 'Key']}); const keyProtos: Array<{}> = []; const ids = [1, 2]; @@ -1852,9 +1852,9 @@ describe('Datastore', () => { datastore.save( [ - { key: incompleteKey, data: {} }, - { key: incompleteKey2, data: {} }, - { key: completeKey, data: {} }, + {key: incompleteKey, data: {}}, + {key: incompleteKey2, data: {}}, + {key: completeKey, data: {}}, ], (err: Error) => { assert.ifError(err); @@ -1882,7 +1882,7 @@ describe('Datastore', () => { it('should queue request & callback', () => { datastore.save({ key, - data: [{ name: 'name', value: 'value' }], + data: [{name: 'name', value: 'value'}], }); assert.strictEqual(typeof datastore.requestCallbacks_[0], 'function'); @@ -1898,7 +1898,7 @@ describe('Datastore', () => { it('should prepare entity objects', done => { const entityObject = {}; - const preparedEntityObject = { prepared: true }; + const preparedEntityObject = {prepared: true}; const expectedEntityObject = Object.assign({}, preparedEntityObject, { method: 'update', }); @@ -1934,8 +1934,8 @@ describe('Datastore', () => { callback(); }; - const key = new entity.Key({ namespace: 'ns', path: ['Company'] }); - datastore.update({ key, data: {} }, done); + const key = new entity.Key({namespace: 'ns', path: ['Company']}); + datastore.update({key, data: {}}, done); }); }); @@ -1946,7 +1946,7 @@ describe('Datastore', () => { it('should prepare entity objects', done => { const entityObject = {}; - const preparedEntityObject = { prepared: true }; + const preparedEntityObject = {prepared: true}; const expectedEntityObject = Object.assign({}, preparedEntityObject, { method: 'upsert', }); @@ -1983,8 +1983,8 @@ describe('Datastore', () => { callback(); }; - const key = new entity.Key({ namespace: 'ns', path: ['Company'] }); - datastore.upsert({ key, data: {} }, done); + const key = new entity.Key({namespace: 'ns', path: ['Company']}); + datastore.upsert({key, data: {}}, done); }); }); @@ -2047,7 +2047,7 @@ describe('Datastore', () => { }); it('should not set customEndpoint_ when using default baseurl', () => { - const datastore = new Datastore({ projectId: PROJECT_ID }); + const datastore = new Datastore({projectId: PROJECT_ID}); datastore.determineBaseUrl_(); assert.strictEqual(datastore.customEndpoint_, undefined); }); From a7152f5e010e4e3a88ac90eeb234823ea6502411 Mon Sep 17 00:00:00 2001 From: Chris Wilcox Date: Tue, 8 Dec 2020 12:21:35 -0800 Subject: [PATCH 04/36] fix: modify warnings to be phrased better --- src/entity.ts | 5 +++-- test/entity.ts | 15 ++++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/entity.ts b/src/entity.ts index 17bde8bd0..44def1af3 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -596,7 +596,8 @@ export namespace entity { "'IntegerOutOfBoundsWarning: the value for '" + property + "' property is outside of bounds of a JavaScript Number.\n" + - "Use 'Datastore.int()' to preserve accuracy during the upload."; + "Use 'Datastore.int()' to preserve accuracy " + + 'in your database.'; const typeCastWarning = "TypeCastWarning: the value for '" + @@ -604,7 +605,7 @@ export namespace entity { "' property is a JavaScript Number.\n" + "Use 'Datastore.int()' or " + "'Datastore.double()' to preserve consistent " + - 'Datastore types during the upload.'; + 'Datastore types in your database.'; if (Number.isInteger(value)) { if (!Number.isSafeInteger(value)) { diff --git a/test/entity.ts b/test/entity.ts index 651ff26b1..3ad3485c9 100644 --- a/test/entity.ts +++ b/test/entity.ts @@ -744,21 +744,21 @@ describe('entity', () => { assert.deepStrictEqual(entity.encodeValue(value), expectedValueProto); }); - it('should encode an int', done => { + it.only('should encode an int', done => { const value = 8; const expectedValueProto = { integerValue: value, }; - const property = 'value'; + const property = 'undefined'; const expectedWarning = "TypeCastWarning: the value for '" + property + "' property is a JavaScript Number.\n" + "Use 'Datastore.int()' or " + "'Datastore.double()' to preserve consistent " + - 'Datastore types during the upload.'; + 'Datastore types in your database.'; process.on('warning', warning => { assert.strictEqual(warning.message, expectedWarning); done(); @@ -780,7 +780,8 @@ describe('entity', () => { "the value for '" + property + "' property is outside of bounds of a JavaScript Number.\n" + - "Use 'Datastore.int()' to preserve accuracy during the upload."; + "Use 'Datastore.int()' to preserve accuracy " + + 'in your database.'; process.on('warning', warning => { assert.strictEqual(warning.message, expectedWarning); @@ -799,21 +800,21 @@ describe('entity', () => { assert.deepStrictEqual(entity.encodeValue(value), expectedValueProto); }); - it('should encode a double', done => { + it.only('should encode a double', done => { const value = 8.3; const expectedValueProto = { doubleValue: value, }; - const property = 'value'; + const property = 'undefined'; const expectedWarning = "TypeCastWarning: the value for '" + property + "' property is a JavaScript Number.\n" + "Use 'Datastore.int()' or " + "'Datastore.double()' to preserve consistent " + - 'Datastore types during the upload.'; + 'Datastore types in your database.'; process.on('warning', warning => { assert.strictEqual(warning.message, expectedWarning); From b1255534f9eb7792dae545e14bb27ae9051c0de8 Mon Sep 17 00:00:00 2001 From: Chris Wilcox Date: Tue, 8 Dec 2020 15:02:36 -0800 Subject: [PATCH 05/36] fix: process.once to avoid debouncing type issues --- src/entity.ts | 2 +- test/entity.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/entity.ts b/src/entity.ts index 44def1af3..c6b7d26a7 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -593,7 +593,7 @@ export namespace entity { if (typeof value === 'number') { const integerOutOfBoundsWarning = - "'IntegerOutOfBoundsWarning: the value for '" + + "IntegerOutOfBoundsWarning: the value for '" + property + "' property is outside of bounds of a JavaScript Number.\n" + "Use 'Datastore.int()' to preserve accuracy " + diff --git a/test/entity.ts b/test/entity.ts index 3ad3485c9..cbf3f87d6 100644 --- a/test/entity.ts +++ b/test/entity.ts @@ -759,7 +759,7 @@ describe('entity', () => { "Use 'Datastore.int()' or " + "'Datastore.double()' to preserve consistent " + 'Datastore types in your database.'; - process.on('warning', warning => { + process.once('warning', warning => { assert.strictEqual(warning.message, expectedWarning); done(); }); @@ -772,7 +772,7 @@ describe('entity', () => { assert.deepStrictEqual(entity.encodeValue(value), expectedValueProto); }); - it('should emit warning on out of bounds int', done => { + it.only('should emit warning on out of bounds int', done => { const largeIntValue = 9223372036854775807; const property = 'largeInt'; const expectedWarning = @@ -783,7 +783,7 @@ describe('entity', () => { "Use 'Datastore.int()' to preserve accuracy " + 'in your database.'; - process.on('warning', warning => { + process.once('warning', warning => { assert.strictEqual(warning.message, expectedWarning); done(); }); @@ -816,7 +816,7 @@ describe('entity', () => { "'Datastore.double()' to preserve consistent " + 'Datastore types in your database.'; - process.on('warning', warning => { + process.once('warning', warning => { assert.strictEqual(warning.message, expectedWarning); done(); }); From 2d31c0391a3fcd2a3f4b91ef93b1b8e94c6a65b5 Mon Sep 17 00:00:00 2001 From: Chris Wilcox Date: Tue, 8 Dec 2020 15:42:41 -0800 Subject: [PATCH 06/36] fix: debounce warning of typing to occur only once --- src/entity.ts | 52 +++++++++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/src/entity.ts b/src/entity.ts index c6b7d26a7..0c99c1d11 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -16,12 +16,11 @@ import arrify = require('arrify'); import * as extend from 'extend'; import * as is from 'is'; -import {Query, QueryProto, IntegerTypeCastOptions} from './query'; -import {PathType} from '.'; -import {protobuf as Protobuf} from 'google-gax'; +import { Query, QueryProto, IntegerTypeCastOptions } from './query'; +import { PathType } from '.'; +import { protobuf as Protobuf } from 'google-gax'; import * as path from 'path'; -import {google} from '../protos/protos'; -import {type} from 'os'; +import { google } from '../protos/protos'; // eslint-disable-next-line @typescript-eslint/no-namespace export namespace entity { @@ -34,7 +33,7 @@ export namespace entity { const errorMessages = { MISSING_KIND: 'A key should contain at least a kind.', MISSING_ANCESTOR_ID: 'Ancestor keys require an id or name.', - } as {[index: string]: string}; + } as { [index: string]: string }; super(errorMessages[opts.code]); this.name = 'InvalidKey'; } @@ -189,7 +188,7 @@ export namespace entity { } toJSON(): Json { - return {type: this.type, value: this.value}; + return { type: this.type, value: this.value }; } } @@ -447,16 +446,16 @@ export namespace entity { if (!Number.isSafeInteger(num)) { throw new Error( 'We attempted to return all of the numeric values, but ' + - (value.propertyName ? value.propertyName + ' ' : '') + - 'value ' + - value.integerValue + - " is out of bounds of 'Number.MAX_SAFE_INTEGER'.\n" + - "To prevent this error, please consider passing 'options.wrapNumbers=true' or\n" + - "'options.wrapNumbers' as\n" + - '{\n' + - ' integerTypeCastFunction: provide \n' + - ' properties: optionally specify property name(s) to be custom casted\n' + - '}\n' + (value.propertyName ? value.propertyName + ' ' : '') + + 'value ' + + value.integerValue + + " is out of bounds of 'Number.MAX_SAFE_INTEGER'.\n" + + "To prevent this error, please consider passing 'options.wrapNumbers=true' or\n" + + "'options.wrapNumbers' as\n" + + '{\n' + + ' integerTypeCastFunction: provide \n' + + ' properties: optionally specify property name(s) to be custom casted\n' + + '}\n' ); } return num; @@ -611,13 +610,13 @@ export namespace entity { if (!Number.isSafeInteger(value)) { process.emitWarning(integerOutOfBoundsWarning); } else { - process.emitWarning(typeCastWarning); + warn('TypeCastWarning', typeCastWarning); } value = new entity.Int(value); valueProto.integerValue = value.value; return valueProto; } else { - process.emitWarning(typeCastWarning); + warn('TypeCastWarning', typeCastWarning); value = new entity.Double(value); valueProto.doubleValue = value.value; return valueProto; @@ -684,6 +683,15 @@ export namespace entity { throw new Error('Unsupported field value, ' + value + ', was provided.'); } + const warningTypesIssued = new Set(); + const warn = (warningName: string, warningMessage: string) => { + console.log(warningName); + if (!warningTypesIssued.has(warningName)) { + warningTypesIssued.add(warningName); + process.emitWarning(warningMessage); + } + } + /** * Convert any entity protocol to a plain object. * @@ -1334,7 +1342,7 @@ export namespace entity { const reference = { app: projectId, namespace: key.namespace, - path: {element: elements}, + path: { element: elements }, }; const buffer = this.protos.Reference.encode(reference).finish(); @@ -1431,7 +1439,7 @@ export interface ValueProto { export interface EntityProto { key?: KeyProto | null; - properties?: {[k: string]: ValueProto}; + properties?: { [k: string]: ValueProto }; excludeFromIndexes?: boolean; } @@ -1455,7 +1463,7 @@ export interface ResponseResult { } export interface EntityObject { - data: {[k: string]: Entity}; + data: { [k: string]: Entity }; excludeFromIndexes: string[]; } From 5b62746277bf0e5fa121ea202fde6e0a518f282c Mon Sep 17 00:00:00 2001 From: Chris Wilcox Date: Tue, 8 Dec 2020 16:09:48 -0800 Subject: [PATCH 07/36] chore: gts fix --- src/entity.ts | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/entity.ts b/src/entity.ts index 0c99c1d11..867b63f93 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -16,11 +16,11 @@ import arrify = require('arrify'); import * as extend from 'extend'; import * as is from 'is'; -import { Query, QueryProto, IntegerTypeCastOptions } from './query'; -import { PathType } from '.'; -import { protobuf as Protobuf } from 'google-gax'; +import {Query, QueryProto, IntegerTypeCastOptions} from './query'; +import {PathType} from '.'; +import {protobuf as Protobuf} from 'google-gax'; import * as path from 'path'; -import { google } from '../protos/protos'; +import {google} from '../protos/protos'; // eslint-disable-next-line @typescript-eslint/no-namespace export namespace entity { @@ -33,7 +33,7 @@ export namespace entity { const errorMessages = { MISSING_KIND: 'A key should contain at least a kind.', MISSING_ANCESTOR_ID: 'Ancestor keys require an id or name.', - } as { [index: string]: string }; + } as {[index: string]: string}; super(errorMessages[opts.code]); this.name = 'InvalidKey'; } @@ -188,7 +188,7 @@ export namespace entity { } toJSON(): Json { - return { type: this.type, value: this.value }; + return {type: this.type, value: this.value}; } } @@ -446,16 +446,16 @@ export namespace entity { if (!Number.isSafeInteger(num)) { throw new Error( 'We attempted to return all of the numeric values, but ' + - (value.propertyName ? value.propertyName + ' ' : '') + - 'value ' + - value.integerValue + - " is out of bounds of 'Number.MAX_SAFE_INTEGER'.\n" + - "To prevent this error, please consider passing 'options.wrapNumbers=true' or\n" + - "'options.wrapNumbers' as\n" + - '{\n' + - ' integerTypeCastFunction: provide \n' + - ' properties: optionally specify property name(s) to be custom casted\n' + - '}\n' + (value.propertyName ? value.propertyName + ' ' : '') + + 'value ' + + value.integerValue + + " is out of bounds of 'Number.MAX_SAFE_INTEGER'.\n" + + "To prevent this error, please consider passing 'options.wrapNumbers=true' or\n" + + "'options.wrapNumbers' as\n" + + '{\n' + + ' integerTypeCastFunction: provide \n' + + ' properties: optionally specify property name(s) to be custom casted\n' + + '}\n' ); } return num; @@ -690,7 +690,7 @@ export namespace entity { warningTypesIssued.add(warningName); process.emitWarning(warningMessage); } - } + }; /** * Convert any entity protocol to a plain object. @@ -1342,7 +1342,7 @@ export namespace entity { const reference = { app: projectId, namespace: key.namespace, - path: { element: elements }, + path: {element: elements}, }; const buffer = this.protos.Reference.encode(reference).finish(); @@ -1439,7 +1439,7 @@ export interface ValueProto { export interface EntityProto { key?: KeyProto | null; - properties?: { [k: string]: ValueProto }; + properties?: {[k: string]: ValueProto}; excludeFromIndexes?: boolean; } @@ -1463,7 +1463,7 @@ export interface ResponseResult { } export interface EntityObject { - data: { [k: string]: Entity }; + data: {[k: string]: Entity}; excludeFromIndexes: string[]; } From 39e618c2986bfe48cfefcad7c4eabdea4019e7c8 Mon Sep 17 00:00:00 2001 From: Chris Wilcox Date: Tue, 8 Dec 2020 17:29:13 -0800 Subject: [PATCH 08/36] chore: don't use it.only --- test/entity.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/entity.ts b/test/entity.ts index cbf3f87d6..6e4923189 100644 --- a/test/entity.ts +++ b/test/entity.ts @@ -744,7 +744,7 @@ describe('entity', () => { assert.deepStrictEqual(entity.encodeValue(value), expectedValueProto); }); - it.only('should encode an int', done => { + it('should encode an int', done => { const value = 8; const expectedValueProto = { @@ -772,7 +772,7 @@ describe('entity', () => { assert.deepStrictEqual(entity.encodeValue(value), expectedValueProto); }); - it.only('should emit warning on out of bounds int', done => { + it('should emit warning on out of bounds int', done => { const largeIntValue = 9223372036854775807; const property = 'largeInt'; const expectedWarning = @@ -800,7 +800,7 @@ describe('entity', () => { assert.deepStrictEqual(entity.encodeValue(value), expectedValueProto); }); - it.only('should encode a double', done => { + it('should encode a double', done => { const value = 8.3; const expectedValueProto = { From 573880dacf864159e32985e62dc37e07b35a738c Mon Sep 17 00:00:00 2001 From: Chris Wilcox Date: Wed, 9 Dec 2020 10:13:15 -0800 Subject: [PATCH 09/36] test: add test to verify typecast warning is debounced --- test/entity.ts | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/entity.ts b/test/entity.ts index 6e4923189..c6f24e7ae 100644 --- a/test/entity.ts +++ b/test/entity.ts @@ -772,6 +772,37 @@ describe('entity', () => { assert.deepStrictEqual(entity.encodeValue(value), expectedValueProto); }); + it('should encode an int but only warn once', done => { + const value = 8; + + const expectedValueProto = { + integerValue: value, + }; + + const property = 'undefined'; + const expectedWarning = + "TypeCastWarning: the value for '" + + property + + "' property is a JavaScript Number.\n" + + "Use 'Datastore.int()' or " + + "'Datastore.double()' to preserve consistent " + + 'Datastore types in your database.'; + process.once('warning', warning => { + assert.strictEqual(warning.message, expectedWarning); + done(); + }); + + entity.Int = function (value_: {}) { + assert.strictEqual(value_, value); + this.value = value_; + }; + + assert.deepStrictEqual(entity.encodeValue(value), expectedValueProto); + // if an error is reported on this, done() is called more than once and + // should cause failure. + assert.deepStrictEqual(entity.encodeValue(value), expectedValueProto); + }); + it('should emit warning on out of bounds int', done => { const largeIntValue = 9223372036854775807; const property = 'largeInt'; From a2ba674a1536e8f3eef39d983043fd629b29333f Mon Sep 17 00:00:00 2001 From: Chris Wilcox Date: Thu, 10 Dec 2020 13:22:19 -0800 Subject: [PATCH 10/36] fix: errant console log committed --- src/entity.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/entity.ts b/src/entity.ts index 867b63f93..1ffd2c80c 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -685,7 +685,6 @@ export namespace entity { const warningTypesIssued = new Set(); const warn = (warningName: string, warningMessage: string) => { - console.log(warningName); if (!warningTypesIssued.has(warningName)) { warningTypesIssued.add(warningName); process.emitWarning(warningMessage); From beab3987f76fb03ee1e455777fb8ff65b7e4041f Mon Sep 17 00:00:00 2001 From: Chris Wilcox Date: Thu, 10 Dec 2020 13:38:14 -0800 Subject: [PATCH 11/36] test: alter expected properties to match property --- test/index.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/index.ts b/test/index.ts index 625094a1c..c7e885350 100644 --- a/test/index.ts +++ b/test/index.ts @@ -1316,7 +1316,13 @@ describe('Datastore', () => { arrayValue: { values: [ { - integerValue: '0', + entityValue: { + properties: { + value: { + integerValue: '0', + }, + }, + }, }, { nullValue: 0, From aa86a2f4a13026898677b63659d4ff7f300ddce7 Mon Sep 17 00:00:00 2001 From: Chris Wilcox Date: Thu, 10 Dec 2020 14:45:26 -0800 Subject: [PATCH 12/36] test: add test that captures current, not desired, behavior --- system-test/datastore.ts | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/system-test/datastore.ts b/system-test/datastore.ts index f9398ffb9..cb4d64f97 100644 --- a/system-test/datastore.ts +++ b/system-test/datastore.ts @@ -268,6 +268,35 @@ describe('Datastore', () => { await datastore.delete(postKey); }); + it('should save/get an int-able double value via Datastore.', async () => { + const postKey = datastore.key('Team'); + const points = Datastore.double(2); + await datastore.save({key: postKey, data: {points}}); + let [entity] = await datastore.get(postKey, {wrapNumbers: true}); + // Expect content is stored in datastore as a double, returned as int + // as doubles can safely be brought to local types. + // NOTE: this is a bit awkward as is since you can't prevent decoding of doubles + // assert.strictEqual(entity.points.type, 'DatastoreDouble'); + // assert.strictEqual(entity.points.value, '2'); + assert.strictEqual(entity.points, 2); + + [entity] = await datastore.get(postKey); + // without wrap numbers, expect to get a number '2' + assert.strictEqual(entity.points, 2); + + // If we re-save here, without numbers wrapped, it will raise a warning + // we have an unsafe to convert representation + // NOTE: the data has been reuploaded in a different state than what was + // retrieved + await datastore.save(entity); + [entity] = await datastore.get(postKey, {wrapNumbers: true}); + // expect as we saved, that this has been made into a a datastore int now. + assert.strictEqual(entity.points.type, 'DatastoreInt'); + assert.strictEqual(entity.points.value, '2'); + + await datastore.delete(postKey); + }); + it('should wrap specified properties via IntegerTypeCastOptions.properties', async () => { const postKey = datastore.key('Scores'); const largeIntValueAsString = '9223372036854775807'; From 1d80aeac4611c88e869a2c9a82f09e81ae9bb521 Mon Sep 17 00:00:00 2001 From: Chris Wilcox Date: Thu, 10 Dec 2020 17:37:15 -0800 Subject: [PATCH 13/36] feat!: do not automatically unwrap numbers for users, though allow them to specify it --- src/entity.ts | 13 ++++++++++++- src/query.ts | 2 +- src/request.ts | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/entity.ts b/src/entity.ts index 1ffd2c80c..42e8a4d7d 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -475,7 +475,7 @@ export namespace entity { * * @private * @param {object} valueProto The protobuf Value message to convert. - * @param {boolean | IntegerTypeCastOptions} [wrapNumbers=false] Wrap values of integerValue type in + * @param {boolean | IntegerTypeCastOptions} [wrapNumbers=true] Wrap values of integerValue type in * {@link Datastore#Int} objects. * If a `boolean`, this will wrap values in {@link Datastore#Int} objects. * If an `object`, this will return a value returned by @@ -523,10 +523,21 @@ export namespace entity { } case 'doubleValue': { + if (wrapNumbers === undefined) { + wrapNumbers = true; + } + + if (wrapNumbers) { + return new entity.Double(value); + } return Number(value); } case 'integerValue': { + if (wrapNumbers === undefined) { + wrapNumbers = true; + } + return wrapNumbers ? typeof wrapNumbers === 'object' ? new entity.Int(valueProto, wrapNumbers).valueOf() diff --git a/src/query.ts b/src/query.ts index 0c5c20646..6cebaab00 100644 --- a/src/query.ts +++ b/src/query.ts @@ -400,7 +400,7 @@ class Query { * [here](https://cloud.google.com/datastore/docs/articles/balancing-strong-and-eventual-consistency-with-google-cloud-datastore). * @param {object} [options.gaxOptions] Request configuration options, outlined * here: https://googleapis.github.io/gax-nodejs/global.html#CallOptions. - * @param {boolean | IntegerTypeCastOptions} [options.wrapNumbers=false] + * @param {boolean | IntegerTypeCastOptions} [options.wrapNumbers=True] * Wrap values of integerValue type in {@link Datastore#Int} objects. * If a `boolean`, this will wrap values in {@link Datastore#Int} objects. * If an `object`, this will return a value returned by diff --git a/src/request.ts b/src/request.ts index 2efa50a02..e5196559d 100644 --- a/src/request.ts +++ b/src/request.ts @@ -447,7 +447,7 @@ class DatastoreRequest { * [here](https://cloud.google.com/datastore/docs/articles/balancing-strong-and-eventual-consistency-with-google-cloud-datastore). * @param {object} [options.gaxOptions] Request configuration options, outlined * here: https://googleapis.github.io/gax-nodejs/global.html#CallOptions. - * @param {boolean | IntegerTypeCastOptions} [options.wrapNumbers=false] + * @param {boolean | IntegerTypeCastOptions} [options.wrapNumbers=true] * Wrap values of integerValue type in {@link Datastore#Int} objects. * If a `boolean`, this will wrap values in {@link Datastore#Int} objects. * If an `object`, this will return a value returned by From b2f4e4de7d9f237c665e9a66d4af15019f4b0dbb Mon Sep 17 00:00:00 2001 From: Chris Wilcox Date: Thu, 10 Dec 2020 17:48:52 -0800 Subject: [PATCH 14/36] test: modify test to capture now current wrap behavior. other tests need altering still --- system-test/datastore.ts | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/system-test/datastore.ts b/system-test/datastore.ts index cb4d64f97..8657e1475 100644 --- a/system-test/datastore.ts +++ b/system-test/datastore.ts @@ -273,26 +273,28 @@ describe('Datastore', () => { const points = Datastore.double(2); await datastore.save({key: postKey, data: {points}}); let [entity] = await datastore.get(postKey, {wrapNumbers: true}); - // Expect content is stored in datastore as a double, returned as int - // as doubles can safely be brought to local types. - // NOTE: this is a bit awkward as is since you can't prevent decoding of doubles - // assert.strictEqual(entity.points.type, 'DatastoreDouble'); - // assert.strictEqual(entity.points.value, '2'); + // Expect content is stored in datastore as a double with wrapping to + // return a wrapped double + assert.strictEqual(entity.points.type, 'DatastoreDouble'); + assert.strictEqual(entity.points.value, 2); + + [entity] = await datastore.get(postKey, {wrapNumbers: false}); + // Verify that when requested with wrapNumbers false, we get a plain + // javascript Number 2. assert.strictEqual(entity.points, 2); [entity] = await datastore.get(postKey); - // without wrap numbers, expect to get a number '2' - assert.strictEqual(entity.points, 2); + // Expect without any options, a wrapped double to be returned. + assert.strictEqual(entity.points.type, 'DatastoreDouble'); + assert.strictEqual(entity.points.value, 2); - // If we re-save here, without numbers wrapped, it will raise a warning - // we have an unsafe to convert representation - // NOTE: the data has been reuploaded in a different state than what was - // retrieved + // Save the data again, reget, ensuring that along the way it isn't + // somehow changed to another numeric type. await datastore.save(entity); - [entity] = await datastore.get(postKey, {wrapNumbers: true}); - // expect as we saved, that this has been made into a a datastore int now. - assert.strictEqual(entity.points.type, 'DatastoreInt'); - assert.strictEqual(entity.points.value, '2'); + [entity] = await datastore.get(postKey); + // expect as we saved, that this property is still a DatastoreDouble. + assert.strictEqual(entity.points.type, 'DatastoreDouble'); + assert.strictEqual(entity.points.value, 2); await datastore.delete(postKey); }); From 3c6eb03166a353adb2943bf0b10e59ad7246d422 Mon Sep 17 00:00:00 2001 From: Chris Wilcox Date: Fri, 11 Dec 2020 10:37:19 -0800 Subject: [PATCH 15/36] feat: extend Number on Double class --- src/entity.ts | 31 +++++++++++++++++++++++++------ system-test/datastore.ts | 9 ++++++--- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/entity.ts b/src/entity.ts index 42e8a4d7d..269d47924 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -52,27 +52,46 @@ export namespace entity { * provided. * * @class - * @param {number} value The double value. + * @param {number|string} value The double value. * * @example * const {Datastore} = require('@google-cloud/datastore'); * const datastore = new Datastore(); * const aDouble = datastore.double(7.3); */ - export class Double { + export class Double extends Number { + private _entityPropertyName: string | undefined; type: string; - value: number; - constructor(value: number) { + value: string; + constructor(value: number | string | ValueProto) { + super(typeof value === 'object' ? value.doubleValue : value); + /** * @name Double#type * @type {string} */ this.type = 'DatastoreDouble'; + + this._entityPropertyName = + typeof value === 'object' ? value.propertyName : undefined; + /** * @name Double#value - * @type {number} + * @type {string} */ - this.value = value; + this.value = + typeof value === 'object' + ? value.doubleValue.toString() + : value.toString(); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + valueOf(): any { + return Number(this.value); + } + + toJSON(): Json { + return {type: this.type, value: this.value}; } } diff --git a/system-test/datastore.ts b/system-test/datastore.ts index 8657e1475..76d222204 100644 --- a/system-test/datastore.ts +++ b/system-test/datastore.ts @@ -276,7 +276,7 @@ describe('Datastore', () => { // Expect content is stored in datastore as a double with wrapping to // return a wrapped double assert.strictEqual(entity.points.type, 'DatastoreDouble'); - assert.strictEqual(entity.points.value, 2); + assert.strictEqual(entity.points.value, '2'); [entity] = await datastore.get(postKey, {wrapNumbers: false}); // Verify that when requested with wrapNumbers false, we get a plain @@ -286,7 +286,7 @@ describe('Datastore', () => { [entity] = await datastore.get(postKey); // Expect without any options, a wrapped double to be returned. assert.strictEqual(entity.points.type, 'DatastoreDouble'); - assert.strictEqual(entity.points.value, 2); + assert.strictEqual(entity.points.value, '2'); // Save the data again, reget, ensuring that along the way it isn't // somehow changed to another numeric type. @@ -294,7 +294,10 @@ describe('Datastore', () => { [entity] = await datastore.get(postKey); // expect as we saved, that this property is still a DatastoreDouble. assert.strictEqual(entity.points.type, 'DatastoreDouble'); - assert.strictEqual(entity.points.value, 2); + assert.strictEqual(entity.points.value, '2'); + + // Verify that DatastoreDouble implement Number behavior + assert.strictEqual(entity.points + 1, 3); await datastore.delete(postKey); }); From 62f4f3e4eb4e8dc7745889ce89b225853cb406fc Mon Sep 17 00:00:00 2001 From: Chris Wilcox Date: Fri, 11 Dec 2020 15:05:48 -0800 Subject: [PATCH 16/36] test: fix system tests --- src/entity.ts | 2 +- system-test/datastore.ts | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/entity.ts b/src/entity.ts index 269d47924..f9af939c2 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -547,7 +547,7 @@ export namespace entity { } if (wrapNumbers) { - return new entity.Double(value); + return new entity.Double(valueProto); } return Number(value); } diff --git a/system-test/datastore.ts b/system-test/datastore.ts index 76d222204..35d64dd8c 100644 --- a/system-test/datastore.ts +++ b/system-test/datastore.ts @@ -20,6 +20,7 @@ import * as yaml from 'js-yaml'; import {Datastore, Index} from '../src'; import {google} from '../protos/protos'; import {Storage} from '@google-cloud/storage'; +import {entity} from '../src/entity'; describe('Datastore', () => { const testKinds: string[] = []; @@ -67,11 +68,11 @@ describe('Datastore', () => { publishedAt: new Date(), author: 'Silvano', isDraft: false, - wordCount: 400, - rating: 5.0, + wordCount: new entity.Int({propertyName: 'wordCount', integerValue: 400}), + rating: new entity.Double({propertyName: 'rating', doubleValue: 5.0}), likes: null, metadata: { - views: 100, + views: new entity.Int({propertyName: 'views', integerValue: 100}), }, }; @@ -547,7 +548,7 @@ describe('Datastore', () => { }, }); const [entity] = await datastore.get(key); - assert.strictEqual(entity.year, integerValue); + assert.strictEqual(entity.year.valueOf(), integerValue); }); it('should save and decode a double', async () => { @@ -561,7 +562,7 @@ describe('Datastore', () => { }, }); const [entity] = await datastore.get(key); - assert.strictEqual(entity.nines, doubleValue); + assert.strictEqual(entity.nines.valueOf(), doubleValue); }); it('should save and decode a geo point', async () => { @@ -919,7 +920,7 @@ describe('Datastore', () => { datastore.get(key), ]); assert.strictEqual(typeof deletedEntity, 'undefined'); - assert.strictEqual(fetchedEntity.rating, 10); + assert.strictEqual(fetchedEntity.rating.valueOf(), 10); }); it('should use the last modification to a key', async () => { From 2085357c736d01426fbf27263ddca099886e001a Mon Sep 17 00:00:00 2001 From: Chris Wilcox Date: Fri, 11 Dec 2020 17:20:42 -0800 Subject: [PATCH 17/36] test: fix unit tests, revert to using number for storing value of double --- src/entity.ts | 10 ++++------ test/entity.ts | 42 ++++++++++++++++++++++++++++++++---------- test/request.ts | 26 ++++++++++++++++++++++++-- 3 files changed, 60 insertions(+), 18 deletions(-) diff --git a/src/entity.ts b/src/entity.ts index f9af939c2..05379102d 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -62,7 +62,7 @@ export namespace entity { export class Double extends Number { private _entityPropertyName: string | undefined; type: string; - value: string; + value: number; constructor(value: number | string | ValueProto) { super(typeof value === 'object' ? value.doubleValue : value); @@ -77,12 +77,10 @@ export namespace entity { /** * @name Double#value - * @type {string} + * @type {number} */ this.value = - typeof value === 'object' - ? value.doubleValue.toString() - : value.toString(); + typeof value === 'object' ? Number(value.doubleValue) : Number(value); } // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -90,7 +88,7 @@ export namespace entity { return Number(this.value); } - toJSON(): Json { + toJSON(): any { return {type: this.type, value: this.value}; } } diff --git a/test/entity.ts b/test/entity.ts index c6f24e7ae..a429aafea 100644 --- a/test/entity.ts +++ b/test/entity.ts @@ -19,6 +19,7 @@ import * as sinon from 'sinon'; import {Datastore} from '../src'; import {Entity} from '../src/entity'; import {IntegerTypeCastOptions} from '../src/query'; +import {decode} from 'punycode'; export function outOfBoundsError(opts: { propertyName?: string; @@ -428,7 +429,7 @@ describe('entity', () => { ); }); - it('should not wrap numbers by default', () => { + it('should wrap numbers by default', () => { const decodeValueProto = entity.decodeValueProto; entity.decodeValueProto = ( valueProto: {}, @@ -439,7 +440,25 @@ describe('entity', () => { return decodeValueProto(valueProto, wrapNumbers); }; - assert.deepStrictEqual(entity.decodeValueProto(valueProto), [intValue]); + assert.deepStrictEqual(entity.decodeValueProto(valueProto), [ + new entity.Int(intValue), + ]); + }); + + it('should not wrap numbers with option', () => { + const decodeValueProto = entity.decodeValueProto; + entity.decodeValueProto = ( + valueProto: {}, + wrapNumbers?: boolean | {} + ) => { + assert.strictEqual(wrapNumbers, false); + + return decodeValueProto(valueProto, wrapNumbers); + }; + + assert.deepStrictEqual(entity.decodeValueProto(valueProto, false), [ + intValue, + ]); }); it('should wrap numbers with an option', () => { @@ -552,13 +571,13 @@ describe('entity', () => { describe('default `wrapNumbers: undefined`', () => { it('should not wrap ints by default', () => { - assert.strictEqual( - typeof entity.decodeValueProto(valueProto), - 'number' - ); + const decoded = entity.decodeValueProto(valueProto); + assert.strictEqual(typeof decoded, 'object'); + assert.strictEqual(typeof decoded.value, 'string'); + assert.strictEqual(typeof decoded.valueOf(), 'number'); }); - it('should throw if integer value is outside of bounds', () => { + it('should throw if integer value is outside of bounds and unwrapping', () => { const largeIntegerValue = Number.MAX_SAFE_INTEGER + 1; const smallIntegerValue = Number.MIN_SAFE_INTEGER - 1; @@ -575,11 +594,11 @@ describe('entity', () => { }; assert.throws(() => { - entity.decodeValueProto(valueProto); + entity.decodeValueProto(valueProto, false); }, outOfBoundsError(valueProto)); assert.throws(() => { - entity.decodeValueProto(valueProto2); + entity.decodeValueProto(valueProto2, false); }, outOfBoundsError(valueProto2)); }); }); @@ -670,7 +689,10 @@ describe('entity', () => { doubleValue: expectedValue, }; - assert.strictEqual(entity.decodeValueProto(valueProto), expectedValue); + assert.strictEqual( + entity.decodeValueProto(valueProto).value, + expectedValue + ); }); it('should decode keys', () => { diff --git a/test/request.ts b/test/request.ts index 830c6f605..1ed69e2ec 100644 --- a/test/request.ts +++ b/test/request.ts @@ -370,6 +370,17 @@ describe('Request', () => { found: [ { entity: { + key: { + partitionId: { + projectId: 'grape-spaceship-123', + }, + path: [ + { + kind: 'Post', + name: 'post1', + }, + ], + }, properties: { [propertyName]: { integerValue: largeInt, @@ -382,7 +393,7 @@ describe('Request', () => { }); }; - const stream = request.createReadStream(key); + const stream = request.createReadStream(key, {wrapNumbers: false}); stream .on('data', () => {}) @@ -928,6 +939,17 @@ describe('Request', () => { entityResults: [ { entity: { + key: { + partitionId: { + projectId: 'grape-spaceship-123', + }, + path: [ + { + kind: 'Post', + name: 'post1', + }, + ], + }, properties: { [propertyName]: { integerValue: largeInt, @@ -941,7 +963,7 @@ describe('Request', () => { }); }; - const stream = request.runQueryStream({}); + const stream = request.runQueryStream({}, {wrapNumbers: false}); stream .on('error', (err: Error) => { From b28df5f4541cc921ad2685b033d25f25ba2eeafe Mon Sep 17 00:00:00 2001 From: Chris Wilcox Date: Fri, 11 Dec 2020 17:44:35 -0800 Subject: [PATCH 18/36] test: fix system test for number/double string/number change. --- system-test/datastore.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/system-test/datastore.ts b/system-test/datastore.ts index 35d64dd8c..00b536c9f 100644 --- a/system-test/datastore.ts +++ b/system-test/datastore.ts @@ -277,7 +277,7 @@ describe('Datastore', () => { // Expect content is stored in datastore as a double with wrapping to // return a wrapped double assert.strictEqual(entity.points.type, 'DatastoreDouble'); - assert.strictEqual(entity.points.value, '2'); + assert.strictEqual(entity.points.value, 2); [entity] = await datastore.get(postKey, {wrapNumbers: false}); // Verify that when requested with wrapNumbers false, we get a plain @@ -287,7 +287,7 @@ describe('Datastore', () => { [entity] = await datastore.get(postKey); // Expect without any options, a wrapped double to be returned. assert.strictEqual(entity.points.type, 'DatastoreDouble'); - assert.strictEqual(entity.points.value, '2'); + assert.strictEqual(entity.points.value, 2); // Save the data again, reget, ensuring that along the way it isn't // somehow changed to another numeric type. @@ -295,7 +295,7 @@ describe('Datastore', () => { [entity] = await datastore.get(postKey); // expect as we saved, that this property is still a DatastoreDouble. assert.strictEqual(entity.points.type, 'DatastoreDouble'); - assert.strictEqual(entity.points.value, '2'); + assert.strictEqual(entity.points.value, 2); // Verify that DatastoreDouble implement Number behavior assert.strictEqual(entity.points + 1, 3); From 1713a9a13082c8d8422e430fb05aa27415c9fccb Mon Sep 17 00:00:00 2001 From: Daniel Bruce Date: Tue, 3 May 2022 16:04:00 -0400 Subject: [PATCH 19/36] tests for wrapping ints --- test/entity.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/entity.ts b/test/entity.ts index a429aafea..03c8fce29 100644 --- a/test/entity.ts +++ b/test/entity.ts @@ -570,12 +570,16 @@ describe('entity', () => { }; describe('default `wrapNumbers: undefined`', () => { - it('should not wrap ints by default', () => { + it('should wrap ints by default', () => { const decoded = entity.decodeValueProto(valueProto); assert.strictEqual(typeof decoded, 'object'); assert.strictEqual(typeof decoded.value, 'string'); assert.strictEqual(typeof decoded.valueOf(), 'number'); }); + it('should not wrap ints', () => { + const decoded = entity.decodeValueProto(valueProto, false); + assert.strictEqual(decoded, 8); + }); it('should throw if integer value is outside of bounds and unwrapping', () => { const largeIntegerValue = Number.MAX_SAFE_INTEGER + 1; From b32d3cc4da913f966cab2aafe24e6a3883170fc2 Mon Sep 17 00:00:00 2001 From: Daniel Bruce Date: Tue, 3 May 2022 16:38:19 -0400 Subject: [PATCH 20/36] Double values test --- test/entity.ts | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/test/entity.ts b/test/entity.ts index 03c8fce29..8763c8df2 100644 --- a/test/entity.ts +++ b/test/entity.ts @@ -684,21 +684,31 @@ describe('entity', () => { const decodedValue = entity.decodeValueProto(valueProto); assert.deepStrictEqual(decodedValue, expectedValue); }); - - it('should decode doubles', () => { - const expectedValue = 8.3; + describe('doubleValues', () => { + const expectedDecodedValue = 8.3; const valueProto = { valueType: 'doubleValue', - doubleValue: expectedValue, + doubleValue: expectedDecodedValue, }; - assert.strictEqual( - entity.decodeValueProto(valueProto).value, - expectedValue - ); + it('should wrap doubles by default', () => { + const decoded = entity.decodeValueProto(valueProto); + const decodedWithWrapping = entity.decodeValueProto(valueProto, true); + assert.deepStrictEqual(decoded, decodedWithWrapping); + assert.strictEqual(typeof decoded, 'object'); + assert.strictEqual(decoded.valueOf(), expectedDecodedValue); + assert.strictEqual(decoded.value, expectedDecodedValue); + }); + it('should not wrap doubles', () => { + assert.strictEqual( + entity.decodeValueProto(valueProto, false), + expectedDecodedValue + ); + }); }); + it('should decode keys', () => { const expectedValue = {}; From 5e1b0f804e714a7cb715616ff947a71fef2aaaa0 Mon Sep 17 00:00:00 2001 From: Daniel Bruce Date: Tue, 3 May 2022 16:47:36 -0400 Subject: [PATCH 21/36] nit: clarify comment --- system-test/datastore.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system-test/datastore.ts b/system-test/datastore.ts index 00b536c9f..bd784e18f 100644 --- a/system-test/datastore.ts +++ b/system-test/datastore.ts @@ -289,7 +289,7 @@ describe('Datastore', () => { assert.strictEqual(entity.points.type, 'DatastoreDouble'); assert.strictEqual(entity.points.value, 2); - // Save the data again, reget, ensuring that along the way it isn't + // Save the data again, get again, ensuring that along the way it isn't // somehow changed to another numeric type. await datastore.save(entity); [entity] = await datastore.get(postKey); From dd9e7119011427921ec4fd1a2757d59ee704b8b3 Mon Sep 17 00:00:00 2001 From: Daniel Bruce Date: Wed, 4 May 2022 11:32:52 -0400 Subject: [PATCH 22/36] Remove dependency. --- test/entity.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test/entity.ts b/test/entity.ts index 8763c8df2..87726469e 100644 --- a/test/entity.ts +++ b/test/entity.ts @@ -19,7 +19,6 @@ import * as sinon from 'sinon'; import {Datastore} from '../src'; import {Entity} from '../src/entity'; import {IntegerTypeCastOptions} from '../src/query'; -import {decode} from 'punycode'; export function outOfBoundsError(opts: { propertyName?: string; From e01fec084039c4647c398fccc776004fb4df0748 Mon Sep 17 00:00:00 2001 From: Daniel Bruce Date: Thu, 5 May 2022 11:41:40 -0400 Subject: [PATCH 23/36] Add a test to ensure that doubles are received --- test/entity.ts | 43 ++++++++++++++++++++++++++++++++++++++++++- test/example.ts | 0 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 test/example.ts diff --git a/test/entity.ts b/test/entity.ts index 87726469e..f359a59b7 100644 --- a/test/entity.ts +++ b/test/entity.ts @@ -17,7 +17,7 @@ import {beforeEach, afterEach, describe, it} from 'mocha'; import * as extend from 'extend'; import * as sinon from 'sinon'; import {Datastore} from '../src'; -import {Entity} from '../src/entity'; +import {entity, Entity} from '../src/entity'; import {IntegerTypeCastOptions} from '../src/query'; export function outOfBoundsError(opts: { @@ -758,6 +758,47 @@ describe('entity', () => { }); }); + describe('encode for request', () => { + it('request should receive doubleValue', done => { + const datastore = new Datastore({}); + const key = new entity.Key({ + namespace: 'namespace', + path: ['Company', 123], + }); + const points = Datastore.double(2); + const entities = [{ + key, + data: { points } + }]; + + datastore.request_ = (data: any) => { + // By the time the request is made, the original object has already been + // transformed into a raw request. + assert.deepStrictEqual(data, { + client: 'DatastoreClient', + method: 'commit', + reqOpts: { + mutations: [{ + upsert: { + key: { + path: [{ kind: 'Company', id: 123}], + partitionId: { namespaceId: 'namespace'} + }, + properties: { + points: {doubleValue: 2} + } + }, + }] + }, + gaxOpts: {} + }); + done(); + }; + + datastore.save(entities, assert.ifError); + }); + }); + describe('encodeValue', () => { it('should encode a boolean', () => { const value = true; diff --git a/test/example.ts b/test/example.ts new file mode 100644 index 000000000..e69de29bb From 8aa7c316b681ef9bd47d85919d99ed8a7ecd4082 Mon Sep 17 00:00:00 2001 From: Daniel Bruce Date: Thu, 5 May 2022 13:40:36 -0400 Subject: [PATCH 24/36] Added more tests for Double --- test/entity.ts | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/test/entity.ts b/test/entity.ts index f359a59b7..793efd1b0 100644 --- a/test/entity.ts +++ b/test/entity.ts @@ -54,11 +54,44 @@ describe('entity', () => { }); describe('Double', () => { + function checkDouble(double: entity.Double, comparisonValue: number) { + assert.strictEqual(double.value, comparisonValue); + assert.strictEqual(double.valueOf(), comparisonValue); + assert.deepStrictEqual(double.toJSON(), { + type: 'DatastoreDouble', + value: comparisonValue + }); + } it('should store the value', () => { const value = 8.3; - const double = new entity.Double(value); - assert.strictEqual(double.value, value); + checkDouble(double, value); + }); + it('should store the rounded value', () => { + const value = 8.0; + const double = new entity.Double(value); + checkDouble(double, value); + }); + it('should store the stringified value', () => { + const value = "8.3"; + const double = new entity.Double(value); + checkDouble(double, 8.3); + }); + it('should store from a value proto', () => { + const valueProto = { + valueType: 'doubleValue', + doubleValue: 8, + }; + const double = new entity.Double(valueProto); + checkDouble(double, 8); + }); + it('should store from a value proto with a stringified value', () => { + const valueProto = { + valueType: 'doubleValue', + doubleValue: '8', + }; + const double = new entity.Double(valueProto); + checkDouble(double, 8); }); }); From 4d15a47b37bdc8588abdcdcf992713f01548caca Mon Sep 17 00:00:00 2001 From: Daniel Bruce Date: Thu, 5 May 2022 13:55:19 -0400 Subject: [PATCH 25/36] remove repeated code fragment --- src/entity.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/entity.ts b/src/entity.ts index 05379102d..62ecb4511 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -64,7 +64,8 @@ export namespace entity { type: string; value: number; constructor(value: number | string | ValueProto) { - super(typeof value === 'object' ? value.doubleValue : value); + const inferredValue = typeof value === 'object' ? value.doubleValue : value; + super(inferredValue); /** * @name Double#type @@ -79,8 +80,7 @@ export namespace entity { * @name Double#value * @type {number} */ - this.value = - typeof value === 'object' ? Number(value.doubleValue) : Number(value); + this.value = Number(inferredValue); } // eslint-disable-next-line @typescript-eslint/no-explicit-any From 8dc7b626ec4abf8d769a54ff9c0639dcf54d0895 Mon Sep 17 00:00:00 2001 From: Daniel Bruce Date: Thu, 5 May 2022 17:40:43 -0400 Subject: [PATCH 26/36] Changed warning message --- src/entity.ts | 5 +++-- test/entity.ts | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/entity.ts b/src/entity.ts index 62ecb4511..170491909 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -623,8 +623,9 @@ export namespace entity { "IntegerOutOfBoundsWarning: the value for '" + property + "' property is outside of bounds of a JavaScript Number.\n" + - "Use 'Datastore.int()' to preserve accuracy " + - 'in your database.'; + "Use 'Datastore.int()' or " + + "'Datastore.double()' to preserve consistent " + + 'Datastore types in your database.'; const typeCastWarning = "TypeCastWarning: the value for '" + diff --git a/test/entity.ts b/test/entity.ts index 793efd1b0..0c912c39c 100644 --- a/test/entity.ts +++ b/test/entity.ts @@ -916,12 +916,12 @@ describe('entity', () => { const largeIntValue = 9223372036854775807; const property = 'largeInt'; const expectedWarning = - 'IntegerOutOfBoundsWarning: ' + - "the value for '" + + "IntegerOutOfBoundsWarning: the value for '" + property + "' property is outside of bounds of a JavaScript Number.\n" + - "Use 'Datastore.int()' to preserve accuracy " + - 'in your database.'; + "Use 'Datastore.int()' or " + + "'Datastore.double()' to preserve consistent " + + 'Datastore types in your database.'; process.once('warning', warning => { assert.strictEqual(warning.message, expectedWarning); From d6eff51341e7568e474d447aa4693086d67dd853 Mon Sep 17 00:00:00 2001 From: Daniel Bruce Date: Fri, 6 May 2022 10:53:52 -0400 Subject: [PATCH 27/36] linting fix --- src/entity.ts | 5 +- src/request.ts | 5 +- src/v1/datastore_admin_client.ts | 71 ++++++++++++++--------------- src/v1/datastore_client.ts | 78 +++++++++++++++----------------- system-test/datastore.ts | 5 +- test/entity.ts | 41 +++++++++-------- test/gapic_datastore_admin_v1.ts | 47 ++++++++----------- test/gapic_datastore_v1.ts | 42 +++++++---------- test/index.ts | 10 ++-- test/query.ts | 2 +- test/request.ts | 21 ++++----- test/transaction.ts | 8 ++-- 12 files changed, 155 insertions(+), 180 deletions(-) diff --git a/src/entity.ts b/src/entity.ts index 170491909..becbbbdf0 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -64,7 +64,8 @@ export namespace entity { type: string; value: number; constructor(value: number | string | ValueProto) { - const inferredValue = typeof value === 'object' ? value.doubleValue : value; + const inferredValue = + typeof value === 'object' ? value.doubleValue : value; super(inferredValue); /** @@ -381,7 +382,7 @@ export namespace entity { isDsInt(identifier) || isDsIntLike(identifier) ) { - this.id = (((identifier as {}) as Int).value || identifier) as string; + this.id = ((identifier as {} as Int).value || identifier) as string; } else if (is.string(identifier)) { this.name = identifier as string; } diff --git a/src/request.ts b/src/request.ts index e5196559d..dd2e475a1 100644 --- a/src/request.ts +++ b/src/request.ts @@ -849,9 +849,8 @@ class DatastoreRequest { try { await Promise.all( arrify(entities).map(async (objEntity: Entity) => { - const obj: Entity = DatastoreRequest.prepareEntityObject_( - objEntity - ); + const obj: Entity = + DatastoreRequest.prepareEntityObject_(objEntity); const [data] = await transaction.get(obj.key); obj.method = 'upsert'; obj.data = Object.assign({}, data, obj.data); diff --git a/src/v1/datastore_admin_client.ts b/src/v1/datastore_admin_client.ts index 266aea593..d04092078 100644 --- a/src/v1/datastore_admin_client.ts +++ b/src/v1/datastore_admin_client.ts @@ -329,13 +329,14 @@ export class DatastoreAdminClient { ]; for (const methodName of datastoreAdminStubMethods) { const callPromise = this.datastoreAdminStub.then( - stub => (...args: Array<{}>) => { - if (this._terminated) { - return Promise.reject('The client has already been closed.'); - } - const func = stub[methodName]; - return func.apply(stub, args); - }, + stub => + (...args: Array<{}>) => { + if (this._terminated) { + return Promise.reject('The client has already been closed.'); + } + const func = stub[methodName]; + return func.apply(stub, args); + }, (err: Error | null | undefined) => () => { throw err; } @@ -491,11 +492,10 @@ export class DatastoreAdminClient { options = options || {}; options.otherArgs = options.otherArgs || {}; options.otherArgs.headers = options.otherArgs.headers || {}; - options.otherArgs.headers[ - 'x-goog-request-params' - ] = gax.routingHeader.fromParams({ - project_id: request.projectId || '', - }); + options.otherArgs.headers['x-goog-request-params'] = + gax.routingHeader.fromParams({ + project_id: request.projectId || '', + }); this.initialize(); return this.innerApiCalls.getIndex(request, options, callback); } @@ -627,11 +627,10 @@ export class DatastoreAdminClient { options = options || {}; options.otherArgs = options.otherArgs || {}; options.otherArgs.headers = options.otherArgs.headers || {}; - options.otherArgs.headers[ - 'x-goog-request-params' - ] = gax.routingHeader.fromParams({ - project_id: request.projectId || '', - }); + options.otherArgs.headers['x-goog-request-params'] = + gax.routingHeader.fromParams({ + project_id: request.projectId || '', + }); this.initialize(); return this.innerApiCalls.exportEntities(request, options, callback); } @@ -794,11 +793,10 @@ export class DatastoreAdminClient { options = options || {}; options.otherArgs = options.otherArgs || {}; options.otherArgs.headers = options.otherArgs.headers || {}; - options.otherArgs.headers[ - 'x-goog-request-params' - ] = gax.routingHeader.fromParams({ - project_id: request.projectId || '', - }); + options.otherArgs.headers['x-goog-request-params'] = + gax.routingHeader.fromParams({ + project_id: request.projectId || '', + }); this.initialize(); return this.innerApiCalls.importEntities(request, options, callback); } @@ -928,11 +926,10 @@ export class DatastoreAdminClient { options = options || {}; options.otherArgs = options.otherArgs || {}; options.otherArgs.headers = options.otherArgs.headers || {}; - options.otherArgs.headers[ - 'x-goog-request-params' - ] = gax.routingHeader.fromParams({ - project_id: request.projectId || '', - }); + options.otherArgs.headers['x-goog-request-params'] = + gax.routingHeader.fromParams({ + project_id: request.projectId || '', + }); this.initialize(); return this.innerApiCalls.listIndexes(request, options, callback); } @@ -969,11 +966,10 @@ export class DatastoreAdminClient { options = options || {}; options.otherArgs = options.otherArgs || {}; options.otherArgs.headers = options.otherArgs.headers || {}; - options.otherArgs.headers[ - 'x-goog-request-params' - ] = gax.routingHeader.fromParams({ - project_id: request.projectId || '', - }); + options.otherArgs.headers['x-goog-request-params'] = + gax.routingHeader.fromParams({ + project_id: request.projectId || '', + }); const callSettings = new gax.CallSettings(options); this.initialize(); return this.descriptors.page.listIndexes.createStream( @@ -1021,17 +1017,16 @@ export class DatastoreAdminClient { options = options || {}; options.otherArgs = options.otherArgs || {}; options.otherArgs.headers = options.otherArgs.headers || {}; - options.otherArgs.headers[ - 'x-goog-request-params' - ] = gax.routingHeader.fromParams({ - project_id: request.projectId || '', - }); + options.otherArgs.headers['x-goog-request-params'] = + gax.routingHeader.fromParams({ + project_id: request.projectId || '', + }); options = options || {}; const callSettings = new gax.CallSettings(options); this.initialize(); return this.descriptors.page.listIndexes.asyncIterate( this.innerApiCalls['listIndexes'] as GaxCall, - (request as unknown) as RequestType, + request as unknown as RequestType, callSettings ) as AsyncIterable; } diff --git a/src/v1/datastore_client.ts b/src/v1/datastore_client.ts index 26e74e710..29c630013 100644 --- a/src/v1/datastore_client.ts +++ b/src/v1/datastore_client.ts @@ -214,13 +214,14 @@ export class DatastoreClient { ]; for (const methodName of datastoreStubMethods) { const callPromise = this.datastoreStub.then( - stub => (...args: Array<{}>) => { - if (this._terminated) { - return Promise.reject('The client has already been closed.'); - } - const func = stub[methodName]; - return func.apply(stub, args); - }, + stub => + (...args: Array<{}>) => { + if (this._terminated) { + return Promise.reject('The client has already been closed.'); + } + const func = stub[methodName]; + return func.apply(stub, args); + }, (err: Error | null | undefined) => () => { throw err; } @@ -375,11 +376,10 @@ export class DatastoreClient { options = options || {}; options.otherArgs = options.otherArgs || {}; options.otherArgs.headers = options.otherArgs.headers || {}; - options.otherArgs.headers[ - 'x-goog-request-params' - ] = gax.routingHeader.fromParams({ - project_id: request.projectId || '', - }); + options.otherArgs.headers['x-goog-request-params'] = + gax.routingHeader.fromParams({ + project_id: request.projectId || '', + }); this.initialize(); return this.innerApiCalls.lookup(request, options, callback); } @@ -470,11 +470,10 @@ export class DatastoreClient { options = options || {}; options.otherArgs = options.otherArgs || {}; options.otherArgs.headers = options.otherArgs.headers || {}; - options.otherArgs.headers[ - 'x-goog-request-params' - ] = gax.routingHeader.fromParams({ - project_id: request.projectId || '', - }); + options.otherArgs.headers['x-goog-request-params'] = + gax.routingHeader.fromParams({ + project_id: request.projectId || '', + }); this.initialize(); return this.innerApiCalls.runQuery(request, options, callback); } @@ -558,11 +557,10 @@ export class DatastoreClient { options = options || {}; options.otherArgs = options.otherArgs || {}; options.otherArgs.headers = options.otherArgs.headers || {}; - options.otherArgs.headers[ - 'x-goog-request-params' - ] = gax.routingHeader.fromParams({ - project_id: request.projectId || '', - }); + options.otherArgs.headers['x-goog-request-params'] = + gax.routingHeader.fromParams({ + project_id: request.projectId || '', + }); this.initialize(); return this.innerApiCalls.beginTransaction(request, options, callback); } @@ -663,11 +661,10 @@ export class DatastoreClient { options = options || {}; options.otherArgs = options.otherArgs || {}; options.otherArgs.headers = options.otherArgs.headers || {}; - options.otherArgs.headers[ - 'x-goog-request-params' - ] = gax.routingHeader.fromParams({ - project_id: request.projectId || '', - }); + options.otherArgs.headers['x-goog-request-params'] = + gax.routingHeader.fromParams({ + project_id: request.projectId || '', + }); this.initialize(); return this.innerApiCalls.commit(request, options, callback); } @@ -750,11 +747,10 @@ export class DatastoreClient { options = options || {}; options.otherArgs = options.otherArgs || {}; options.otherArgs.headers = options.otherArgs.headers || {}; - options.otherArgs.headers[ - 'x-goog-request-params' - ] = gax.routingHeader.fromParams({ - project_id: request.projectId || '', - }); + options.otherArgs.headers['x-goog-request-params'] = + gax.routingHeader.fromParams({ + project_id: request.projectId || '', + }); this.initialize(); return this.innerApiCalls.rollback(request, options, callback); } @@ -838,11 +834,10 @@ export class DatastoreClient { options = options || {}; options.otherArgs = options.otherArgs || {}; options.otherArgs.headers = options.otherArgs.headers || {}; - options.otherArgs.headers[ - 'x-goog-request-params' - ] = gax.routingHeader.fromParams({ - project_id: request.projectId || '', - }); + options.otherArgs.headers['x-goog-request-params'] = + gax.routingHeader.fromParams({ + project_id: request.projectId || '', + }); this.initialize(); return this.innerApiCalls.allocateIds(request, options, callback); } @@ -928,11 +923,10 @@ export class DatastoreClient { options = options || {}; options.otherArgs = options.otherArgs || {}; options.otherArgs.headers = options.otherArgs.headers || {}; - options.otherArgs.headers[ - 'x-goog-request-params' - ] = gax.routingHeader.fromParams({ - project_id: request.projectId || '', - }); + options.otherArgs.headers['x-goog-request-params'] = + gax.routingHeader.fromParams({ + project_id: request.projectId || '', + }); this.initialize(); return this.innerApiCalls.reserveIds(request, options, callback); } diff --git a/system-test/datastore.ts b/system-test/datastore.ts index bd784e18f..d8230249d 100644 --- a/system-test/datastore.ts +++ b/system-test/datastore.ts @@ -1057,8 +1057,9 @@ describe('Datastore', () => { // This is a >20 minute operation, so we're just going to make sure the // right type of operation was started. assert.strictEqual( - (importOperation.metadata as google.datastore.admin.v1.IImportEntitiesMetadata) - .inputUrl, + ( + importOperation.metadata as google.datastore.admin.v1.IImportEntitiesMetadata + ).inputUrl, `gs://${exportedFile.bucket.name}/${exportedFile.name}` ); diff --git a/test/entity.ts b/test/entity.ts index 0c912c39c..627f95905 100644 --- a/test/entity.ts +++ b/test/entity.ts @@ -59,7 +59,7 @@ describe('entity', () => { assert.strictEqual(double.valueOf(), comparisonValue); assert.deepStrictEqual(double.toJSON(), { type: 'DatastoreDouble', - value: comparisonValue + value: comparisonValue, }); } it('should store the value', () => { @@ -73,7 +73,7 @@ describe('entity', () => { checkDouble(double, value); }); it('should store the stringified value', () => { - const value = "8.3"; + const value = '8.3'; const double = new entity.Double(value); checkDouble(double, 8.3); }); @@ -734,13 +734,12 @@ describe('entity', () => { }); it('should not wrap doubles', () => { assert.strictEqual( - entity.decodeValueProto(valueProto, false), - expectedDecodedValue + entity.decodeValueProto(valueProto, false), + expectedDecodedValue ); }); }); - it('should decode keys', () => { const expectedValue = {}; @@ -799,10 +798,12 @@ describe('entity', () => { path: ['Company', 123], }); const points = Datastore.double(2); - const entities = [{ - key, - data: { points } - }]; + const entities = [ + { + key, + data: {points}, + }, + ]; datastore.request_ = (data: any) => { // By the time the request is made, the original object has already been @@ -811,19 +812,21 @@ describe('entity', () => { client: 'DatastoreClient', method: 'commit', reqOpts: { - mutations: [{ - upsert: { - key: { - path: [{ kind: 'Company', id: 123}], - partitionId: { namespaceId: 'namespace'} + mutations: [ + { + upsert: { + key: { + path: [{kind: 'Company', id: 123}], + partitionId: {namespaceId: 'namespace'}, + }, + properties: { + points: {doubleValue: 2}, + }, }, - properties: { - points: {doubleValue: 2} - } }, - }] + ], }, - gaxOpts: {} + gaxOpts: {}, }); done(); }; diff --git a/test/gapic_datastore_admin_v1.ts b/test/gapic_datastore_admin_v1.ts index e424f18e8..da6a1aeca 100644 --- a/test/gapic_datastore_admin_v1.ts +++ b/test/gapic_datastore_admin_v1.ts @@ -28,10 +28,9 @@ import {PassThrough} from 'stream'; import {protobuf, LROperation, operationsProtos} from 'google-gax'; function generateSampleMessage(instance: T) { - const filledObject = (instance.constructor as typeof protobuf.Message).toObject( - instance as protobuf.Message, - {defaults: true} - ); + const filledObject = ( + instance.constructor as typeof protobuf.Message + ).toObject(instance as protobuf.Message, {defaults: true}); return (instance.constructor as typeof protobuf.Message).fromObject( filledObject ) as T; @@ -281,9 +280,8 @@ describe('v1.DatastoreAdminClient', () => { const expectedResponse = generateSampleMessage( new protos.google.datastore.admin.v1.Index() ); - client.innerApiCalls.getIndex = stubSimpleCallWithCallback( - expectedResponse - ); + client.innerApiCalls.getIndex = + stubSimpleCallWithCallback(expectedResponse); const promise = new Promise((resolve, reject) => { client.getIndex( request, @@ -359,9 +357,8 @@ describe('v1.DatastoreAdminClient', () => { const expectedResponse = generateSampleMessage( new protos.google.longrunning.Operation() ); - client.innerApiCalls.exportEntities = stubLongRunningCall( - expectedResponse - ); + client.innerApiCalls.exportEntities = + stubLongRunningCall(expectedResponse); const [operation] = await client.exportEntities(request); const [response] = await operation.promise(); assert.deepStrictEqual(response, expectedResponse); @@ -393,9 +390,8 @@ describe('v1.DatastoreAdminClient', () => { const expectedResponse = generateSampleMessage( new protos.google.longrunning.Operation() ); - client.innerApiCalls.exportEntities = stubLongRunningCallWithCallback( - expectedResponse - ); + client.innerApiCalls.exportEntities = + stubLongRunningCallWithCallback(expectedResponse); const promise = new Promise((resolve, reject) => { client.exportEntities( request, @@ -555,9 +551,8 @@ describe('v1.DatastoreAdminClient', () => { const expectedResponse = generateSampleMessage( new protos.google.longrunning.Operation() ); - client.innerApiCalls.importEntities = stubLongRunningCall( - expectedResponse - ); + client.innerApiCalls.importEntities = + stubLongRunningCall(expectedResponse); const [operation] = await client.importEntities(request); const [response] = await operation.promise(); assert.deepStrictEqual(response, expectedResponse); @@ -589,9 +584,8 @@ describe('v1.DatastoreAdminClient', () => { const expectedResponse = generateSampleMessage( new protos.google.longrunning.Operation() ); - client.innerApiCalls.importEntities = stubLongRunningCallWithCallback( - expectedResponse - ); + client.innerApiCalls.importEntities = + stubLongRunningCallWithCallback(expectedResponse); const promise = new Promise((resolve, reject) => { client.importEntities( request, @@ -786,9 +780,8 @@ describe('v1.DatastoreAdminClient', () => { generateSampleMessage(new protos.google.datastore.admin.v1.Index()), generateSampleMessage(new protos.google.datastore.admin.v1.Index()), ]; - client.innerApiCalls.listIndexes = stubSimpleCallWithCallback( - expectedResponse - ); + client.innerApiCalls.listIndexes = + stubSimpleCallWithCallback(expectedResponse); const promise = new Promise((resolve, reject) => { client.listIndexes( request, @@ -860,9 +853,8 @@ describe('v1.DatastoreAdminClient', () => { generateSampleMessage(new protos.google.datastore.admin.v1.Index()), generateSampleMessage(new protos.google.datastore.admin.v1.Index()), ]; - client.descriptors.page.listIndexes.createStream = stubPageStreamingCall( - expectedResponse - ); + client.descriptors.page.listIndexes.createStream = + stubPageStreamingCall(expectedResponse); const stream = client.listIndexesStream(request); const promise = new Promise((resolve, reject) => { const responses: protos.google.datastore.admin.v1.Index[] = []; @@ -956,9 +948,8 @@ describe('v1.DatastoreAdminClient', () => { generateSampleMessage(new protos.google.datastore.admin.v1.Index()), generateSampleMessage(new protos.google.datastore.admin.v1.Index()), ]; - client.descriptors.page.listIndexes.asyncIterate = stubAsyncIterationCall( - expectedResponse - ); + client.descriptors.page.listIndexes.asyncIterate = + stubAsyncIterationCall(expectedResponse); const responses: protos.google.datastore.admin.v1.IIndex[] = []; const iterable = client.listIndexesAsync(request); for await (const resource of iterable) { diff --git a/test/gapic_datastore_v1.ts b/test/gapic_datastore_v1.ts index 58017d1ac..c78f6f16b 100644 --- a/test/gapic_datastore_v1.ts +++ b/test/gapic_datastore_v1.ts @@ -26,10 +26,9 @@ import * as datastoreModule from '../src'; import {protobuf} from 'google-gax'; function generateSampleMessage(instance: T) { - const filledObject = (instance.constructor as typeof protobuf.Message).toObject( - instance as protobuf.Message, - {defaults: true} - ); + const filledObject = ( + instance.constructor as typeof protobuf.Message + ).toObject(instance as protobuf.Message, {defaults: true}); return (instance.constructor as typeof protobuf.Message).fromObject( filledObject ) as T; @@ -184,9 +183,8 @@ describe('v1.DatastoreClient', () => { const expectedResponse = generateSampleMessage( new protos.google.datastore.v1.LookupResponse() ); - client.innerApiCalls.lookup = stubSimpleCallWithCallback( - expectedResponse - ); + client.innerApiCalls.lookup = + stubSimpleCallWithCallback(expectedResponse); const promise = new Promise((resolve, reject) => { client.lookup( request, @@ -293,9 +291,8 @@ describe('v1.DatastoreClient', () => { const expectedResponse = generateSampleMessage( new protos.google.datastore.v1.RunQueryResponse() ); - client.innerApiCalls.runQuery = stubSimpleCallWithCallback( - expectedResponse - ); + client.innerApiCalls.runQuery = + stubSimpleCallWithCallback(expectedResponse); const promise = new Promise((resolve, reject) => { client.runQuery( request, @@ -402,9 +399,8 @@ describe('v1.DatastoreClient', () => { const expectedResponse = generateSampleMessage( new protos.google.datastore.v1.BeginTransactionResponse() ); - client.innerApiCalls.beginTransaction = stubSimpleCallWithCallback( - expectedResponse - ); + client.innerApiCalls.beginTransaction = + stubSimpleCallWithCallback(expectedResponse); const promise = new Promise((resolve, reject) => { client.beginTransaction( request, @@ -514,9 +510,8 @@ describe('v1.DatastoreClient', () => { const expectedResponse = generateSampleMessage( new protos.google.datastore.v1.CommitResponse() ); - client.innerApiCalls.commit = stubSimpleCallWithCallback( - expectedResponse - ); + client.innerApiCalls.commit = + stubSimpleCallWithCallback(expectedResponse); const promise = new Promise((resolve, reject) => { client.commit( request, @@ -623,9 +618,8 @@ describe('v1.DatastoreClient', () => { const expectedResponse = generateSampleMessage( new protos.google.datastore.v1.RollbackResponse() ); - client.innerApiCalls.rollback = stubSimpleCallWithCallback( - expectedResponse - ); + client.innerApiCalls.rollback = + stubSimpleCallWithCallback(expectedResponse); const promise = new Promise((resolve, reject) => { client.rollback( request, @@ -732,9 +726,8 @@ describe('v1.DatastoreClient', () => { const expectedResponse = generateSampleMessage( new protos.google.datastore.v1.AllocateIdsResponse() ); - client.innerApiCalls.allocateIds = stubSimpleCallWithCallback( - expectedResponse - ); + client.innerApiCalls.allocateIds = + stubSimpleCallWithCallback(expectedResponse); const promise = new Promise((resolve, reject) => { client.allocateIds( request, @@ -844,9 +837,8 @@ describe('v1.DatastoreClient', () => { const expectedResponse = generateSampleMessage( new protos.google.datastore.v1.ReserveIdsResponse() ); - client.innerApiCalls.reserveIds = stubSimpleCallWithCallback( - expectedResponse - ); + client.innerApiCalls.reserveIds = + stubSimpleCallWithCallback(expectedResponse); const promise = new Promise((resolve, reject) => { client.reserveIds( request, diff --git a/test/index.ts b/test/index.ts index c7e885350..91ba9bcdb 100644 --- a/test/index.ts +++ b/test/index.ts @@ -95,14 +95,14 @@ const fakeGoogleGax = { constructor(opts: gax.GrpcClientOptions) { // super constructor must be called first! super(opts); - this.grpc = ({ + this.grpc = { credentials: { // eslint-disable-next-line @typescript-eslint/no-explicit-any createInsecure(...args: any[]) { return (createInsecureOverride || (() => {}))(...args); }, }, - } as {}) as gax.GrpcModule; + } as {} as gax.GrpcModule; } }, }; @@ -1768,7 +1768,7 @@ describe('Datastore', () => { ]; fakeEntity.entityToEntityProto = (entity: EntityObject) => { - return (entity as unknown) as EntityProto; + return entity as unknown as EntityProto; }; datastore.request_ = (config: RequestConfig) => { assert.strictEqual( @@ -1851,9 +1851,9 @@ describe('Datastore', () => { sandbox.stub(fakeEntity, 'keyFromKeyProto').callsFake(keyProto => { keyProtos.push(keyProto); - return ({ + return { id: ids[keyProtos.length - 1], - } as {}) as entity.Key; + } as {} as entity.Key; }); datastore.save( diff --git a/test/query.ts b/test/query.ts index d2546f383..6a99685de 100644 --- a/test/query.ts +++ b/test/query.ts @@ -49,7 +49,7 @@ describe('Query', () => { new Query(SCOPE, '', KINDS), new Query(SCOPE, null, KINDS), new Query(SCOPE, undefined, KINDS), - new Query(SCOPE, (0 as {}) as string, KINDS), + new Query(SCOPE, 0 as {} as string, KINDS), new Query(SCOPE, KINDS), ].forEach(query => { assert.strictEqual(query.namespace, null); diff --git a/test/request.ts b/test/request.ts index 1ed69e2ec..edf3efede 100644 --- a/test/request.ts +++ b/test/request.ts @@ -740,8 +740,8 @@ describe('Request', () => { request.get(keys, (err: Error) => { assert.ifError(err); - const createReadStreamOptions = request.createReadStream.getCall(0) - .args[1]; + const createReadStreamOptions = + request.createReadStream.getCall(0).args[1]; assert.strictEqual(createReadStreamOptions.wrapNumbers, undefined); done(); }); @@ -751,8 +751,8 @@ describe('Request', () => { request.get(keys, {wrapNumbers: true}, (err: Error) => { assert.ifError(err); - const createReadStreamOptions = request.createReadStream.getCall(0) - .args[1]; + const createReadStreamOptions = + request.createReadStream.getCall(0).args[1]; assert.strictEqual( typeof createReadStreamOptions.wrapNumbers, 'boolean' @@ -773,9 +773,8 @@ describe('Request', () => { (err: Error) => { assert.ifError(err); - const createReadStreamOptions = request.createReadStream.getCall( - 0 - ).args[1]; + const createReadStreamOptions = + request.createReadStream.getCall(0).args[1]; assert.strictEqual( createReadStreamOptions.wrapNumbers, integerTypeCastOptions @@ -1089,11 +1088,11 @@ describe('Request', () => { limitVal: 1, offsetVal: 8, }; - const queryProto = ({ + const queryProto = { limit: { value: query.limitVal, }, - } as {}) as QueryProto; + } as {} as QueryProto; let timesRequestCalled = 0; let startCalled = false; @@ -1421,11 +1420,11 @@ describe('Request', () => { const PROJECT_ID = 'project-id'; const NAMESPACE = 'a-namespace'; - const DATASTORE = ({ + const DATASTORE = { request_() {}, projectId: PROJECT_ID, namespace: NAMESPACE, - } as {}) as ds.Datastore; + } as {} as ds.Datastore; const key = { namespace: 'ns', diff --git a/test/transaction.ts b/test/transaction.ts index f97020c56..41e62be1d 100644 --- a/test/transaction.ts +++ b/test/transaction.ts @@ -56,11 +56,11 @@ describe('Transaction', () => { const PROJECT_ID = 'project-id'; const NAMESPACE = 'a-namespace'; - const DATASTORE = ({ + const DATASTORE = { request_() {}, projectId: PROJECT_ID, namespace: NAMESPACE, - } as {}) as Datastore; + } as {} as Datastore; function key(path: Path) { return new entity.Key({path: arrify(path)}); @@ -573,13 +573,13 @@ describe('Transaction', () => { it('should allow full override of transactionOptions', done => { transaction.readOnly = true; - const options = ({ + const options = { transactionOptions: { readWrite: { previousTransaction: 'transaction-id', }, }, - } as {}) as TransactionOptions; + } as {} as TransactionOptions; transaction.request_ = config => { assert.deepStrictEqual(config.reqOpts, options); From 074d218cad62727f646362a6bcebbda55041e710 Mon Sep 17 00:00:00 2001 From: Daniel Bruce Date: Mon, 9 May 2022 15:11:23 -0400 Subject: [PATCH 28/36] Remove file --- test/example.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 test/example.ts diff --git a/test/example.ts b/test/example.ts deleted file mode 100644 index e69de29bb..000000000 From 1bf4254d1276d2cdf291d1d296b086fbef2530fc Mon Sep 17 00:00:00 2001 From: Daniel Bruce Date: Mon, 9 May 2022 15:52:07 -0400 Subject: [PATCH 29/36] Attempts to fix sample test --- samples/concepts.js | 12 ++++++++++-- samples/test/concepts.test.js | 10 ++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/samples/concepts.js b/samples/concepts.js index 9e1eb7419..d6f31c1fc 100644 --- a/samples/concepts.js +++ b/samples/concepts.js @@ -1035,11 +1035,19 @@ class Transaction extends TestHelper { datastore = datastoreMock; assert.strictEqual( accounts[0].balance, - originalBalance - amountToTransfer + { + _entityPropertyName: 'balance', + type: 'DatastoreInt', + value: originalBalance - amountToTransfer + } ); assert.strictEqual( accounts[1].balance, - originalBalance + amountToTransfer + { + _entityPropertyName: 'balance', + type: 'DatastoreInt', + value: originalBalance + amountToTransfer + } ); } catch (err) { datastore = datastoreMock; diff --git a/samples/test/concepts.test.js b/samples/test/concepts.test.js index ef977bd29..5a9c2bb79 100644 --- a/samples/test/concepts.test.js +++ b/samples/test/concepts.test.js @@ -92,6 +92,12 @@ describe('concepts', () => { it('performs a query with multi sort', () => query.testMultiSort()); it('performs a kindless query', () => query.testKindlessQuery()); it('performs a projection query', () => { + const priorities = entity.Int({ + type: 'DatastoreInt', value: 4, propertyName: 'priority' + }); + const percentCompletes = entity.Int({ + type: 'DatastoreInt', value: 10, propertyName: 'percent_complete' + }); return entity .testProperties() .then(() => { @@ -103,8 +109,8 @@ describe('concepts', () => { }) .then(results => { assert.deepStrictEqual(results, { - priorities: [4], - percentCompletes: [10], + priorities, + percentCompletes, }); }); }); From 2cdf4c5ec07a09e19a435964132b8d629aff12b4 Mon Sep 17 00:00:00 2001 From: Daniel Bruce Date: Mon, 9 May 2022 15:56:19 -0400 Subject: [PATCH 30/36] Linting fix --- samples/concepts.js | 26 ++++++++++---------------- samples/test/concepts.test.js | 8 ++++++-- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/samples/concepts.js b/samples/concepts.js index d6f31c1fc..0a2658c45 100644 --- a/samples/concepts.js +++ b/samples/concepts.js @@ -1033,22 +1033,16 @@ class Transaction extends TestHelper { const accounts = results.map(result => result[0]); // Restore `datastore` to the mock API. datastore = datastoreMock; - assert.strictEqual( - accounts[0].balance, - { - _entityPropertyName: 'balance', - type: 'DatastoreInt', - value: originalBalance - amountToTransfer - } - ); - assert.strictEqual( - accounts[1].balance, - { - _entityPropertyName: 'balance', - type: 'DatastoreInt', - value: originalBalance + amountToTransfer - } - ); + assert.strictEqual(accounts[0].balance, { + _entityPropertyName: 'balance', + type: 'DatastoreInt', + value: originalBalance - amountToTransfer, + }); + assert.strictEqual(accounts[1].balance, { + _entityPropertyName: 'balance', + type: 'DatastoreInt', + value: originalBalance + amountToTransfer, + }); } catch (err) { datastore = datastoreMock; throw err; diff --git a/samples/test/concepts.test.js b/samples/test/concepts.test.js index 5a9c2bb79..f1420a01e 100644 --- a/samples/test/concepts.test.js +++ b/samples/test/concepts.test.js @@ -93,10 +93,14 @@ describe('concepts', () => { it('performs a kindless query', () => query.testKindlessQuery()); it('performs a projection query', () => { const priorities = entity.Int({ - type: 'DatastoreInt', value: 4, propertyName: 'priority' + type: 'DatastoreInt', + value: 4, + propertyName: 'priority', }); const percentCompletes = entity.Int({ - type: 'DatastoreInt', value: 10, propertyName: 'percent_complete' + type: 'DatastoreInt', + value: 10, + propertyName: 'percent_complete', }); return entity .testProperties() From 6044901d9d75a473aab5bf0609e9d2b50a7896a1 Mon Sep 17 00:00:00 2001 From: Daniel Bruce Date: Mon, 9 May 2022 16:54:45 -0400 Subject: [PATCH 31/36] Updates for sample test --- samples/concepts.js | 4 ++-- samples/test/concepts.test.js | 4 ++-- src/index.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/samples/concepts.js b/samples/concepts.js index 0a2658c45..1fdeef47a 100644 --- a/samples/concepts.js +++ b/samples/concepts.js @@ -1036,12 +1036,12 @@ class Transaction extends TestHelper { assert.strictEqual(accounts[0].balance, { _entityPropertyName: 'balance', type: 'DatastoreInt', - value: originalBalance - amountToTransfer, + value: Number(originalBalance - amountToTransfer).toString(), }); assert.strictEqual(accounts[1].balance, { _entityPropertyName: 'balance', type: 'DatastoreInt', - value: originalBalance + amountToTransfer, + value: Number(originalBalance + amountToTransfer).toString(), }); } catch (err) { datastore = datastoreMock; diff --git a/samples/test/concepts.test.js b/samples/test/concepts.test.js index f1420a01e..0c248aa22 100644 --- a/samples/test/concepts.test.js +++ b/samples/test/concepts.test.js @@ -92,12 +92,12 @@ describe('concepts', () => { it('performs a query with multi sort', () => query.testMultiSort()); it('performs a kindless query', () => query.testKindlessQuery()); it('performs a projection query', () => { - const priorities = entity.Int({ + const priorities = transaction.datastore.int({ type: 'DatastoreInt', value: 4, propertyName: 'priority', }); - const percentCompletes = entity.Int({ + const percentCompletes = transaction.datastore.int({ type: 'DatastoreInt', value: 10, propertyName: 'percent_complete', diff --git a/src/index.ts b/src/index.ts index 272559872..04d033c0c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1369,11 +1369,11 @@ class Datastore extends DatastoreRequest { * ]); * ``` */ - static int(value: number | string) { + static int(value: number | string | ValueProto) { return new entity.Int(value); } - int(value: number | string) { + int(value: number | string | ValueProto) { return Datastore.int(value); } From 4f0c8b46f4741ac7f95139e0965493f9f78f091a Mon Sep 17 00:00:00 2001 From: Daniel Bruce Date: Mon, 9 May 2022 17:08:01 -0400 Subject: [PATCH 32/36] Fix the datastore int constructions --- samples/concepts.js | 18 ++++++++---------- samples/test/concepts.test.js | 6 ++---- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/samples/concepts.js b/samples/concepts.js index 1fdeef47a..201f7f65e 100644 --- a/samples/concepts.js +++ b/samples/concepts.js @@ -1033,16 +1033,14 @@ class Transaction extends TestHelper { const accounts = results.map(result => result[0]); // Restore `datastore` to the mock API. datastore = datastoreMock; - assert.strictEqual(accounts[0].balance, { - _entityPropertyName: 'balance', - type: 'DatastoreInt', - value: Number(originalBalance - amountToTransfer).toString(), - }); - assert.strictEqual(accounts[1].balance, { - _entityPropertyName: 'balance', - type: 'DatastoreInt', - value: Number(originalBalance + amountToTransfer).toString(), - }); + assert.strictEqual(accounts[0].balance, datastore.int({ + propertyName: 'balance', + integerValue: originalBalance - amountToTransfer, + })); + assert.strictEqual(accounts[1].balance, datastore.int({ + propertyName: 'balance', + integerValue: originalBalance + amountToTransfer, + })); } catch (err) { datastore = datastoreMock; throw err; diff --git a/samples/test/concepts.test.js b/samples/test/concepts.test.js index 0c248aa22..2a37e0c1b 100644 --- a/samples/test/concepts.test.js +++ b/samples/test/concepts.test.js @@ -93,13 +93,11 @@ describe('concepts', () => { it('performs a kindless query', () => query.testKindlessQuery()); it('performs a projection query', () => { const priorities = transaction.datastore.int({ - type: 'DatastoreInt', - value: 4, + integerValue: 4, propertyName: 'priority', }); const percentCompletes = transaction.datastore.int({ - type: 'DatastoreInt', - value: 10, + integerValue: 10, propertyName: 'percent_complete', }); return entity From 85ae2cc573abe204aaaf053274ae08c8f1140a62 Mon Sep 17 00:00:00 2001 From: Daniel Bruce Date: Mon, 9 May 2022 17:11:33 -0400 Subject: [PATCH 33/36] linting fix --- samples/concepts.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/samples/concepts.js b/samples/concepts.js index 201f7f65e..0859e546a 100644 --- a/samples/concepts.js +++ b/samples/concepts.js @@ -1033,14 +1033,20 @@ class Transaction extends TestHelper { const accounts = results.map(result => result[0]); // Restore `datastore` to the mock API. datastore = datastoreMock; - assert.strictEqual(accounts[0].balance, datastore.int({ - propertyName: 'balance', - integerValue: originalBalance - amountToTransfer, - })); - assert.strictEqual(accounts[1].balance, datastore.int({ - propertyName: 'balance', - integerValue: originalBalance + amountToTransfer, - })); + assert.strictEqual( + accounts[0].balance, + datastore.int({ + propertyName: 'balance', + integerValue: originalBalance - amountToTransfer, + }) + ); + assert.strictEqual( + accounts[1].balance, + datastore.int({ + propertyName: 'balance', + integerValue: originalBalance + amountToTransfer, + }) + ); } catch (err) { datastore = datastoreMock; throw err; From 56733e70f8b4f811e59b1ebd8c3531dc310336e8 Mon Sep 17 00:00:00 2001 From: Daniel Bruce Date: Mon, 9 May 2022 17:33:01 -0400 Subject: [PATCH 34/36] before and after balance --- samples/concepts.js | 24 ++++++++++-------------- samples/test/concepts.test.js | 4 ++-- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/samples/concepts.js b/samples/concepts.js index 0859e546a..92ebb1232 100644 --- a/samples/concepts.js +++ b/samples/concepts.js @@ -1020,6 +1020,14 @@ class Transaction extends TestHelper { // Overwrite so the real Datastore instance is used in `transferFunds`. datastore = this.datastore; + const beforeBalance = datastore.int({ + propertyName: 'balance', + integerValue: originalBalance - amountToTransfer, + }); + const afterBalance = datastore.int({ + propertyName: 'balance', + integerValue: originalBalance + amountToTransfer, + }); try { await this.restoreBankAccountBalances({ keys: [fromKey, toKey], @@ -1033,20 +1041,8 @@ class Transaction extends TestHelper { const accounts = results.map(result => result[0]); // Restore `datastore` to the mock API. datastore = datastoreMock; - assert.strictEqual( - accounts[0].balance, - datastore.int({ - propertyName: 'balance', - integerValue: originalBalance - amountToTransfer, - }) - ); - assert.strictEqual( - accounts[1].balance, - datastore.int({ - propertyName: 'balance', - integerValue: originalBalance + amountToTransfer, - }) - ); + assert.strictEqual(accounts[0].balance, beforeBalance); + assert.strictEqual(accounts[1].balance, afterBalance); } catch (err) { datastore = datastoreMock; throw err; diff --git a/samples/test/concepts.test.js b/samples/test/concepts.test.js index 2a37e0c1b..58ad25dce 100644 --- a/samples/test/concepts.test.js +++ b/samples/test/concepts.test.js @@ -111,8 +111,8 @@ describe('concepts', () => { }) .then(results => { assert.deepStrictEqual(results, { - priorities, - percentCompletes, + priorities: priorities, + percentCompletes: percentCompletes, }); }); }); From 80b993b54408009ec8b61c4e9b6f5ed6c64558b7 Mon Sep 17 00:00:00 2001 From: Daniel Bruce Date: Tue, 10 May 2022 09:40:15 -0400 Subject: [PATCH 35/36] use deep strict equal --- samples/concepts.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/concepts.js b/samples/concepts.js index 92ebb1232..bccde8aa8 100644 --- a/samples/concepts.js +++ b/samples/concepts.js @@ -1041,8 +1041,8 @@ class Transaction extends TestHelper { const accounts = results.map(result => result[0]); // Restore `datastore` to the mock API. datastore = datastoreMock; - assert.strictEqual(accounts[0].balance, beforeBalance); - assert.strictEqual(accounts[1].balance, afterBalance); + assert.deepStrictEqual(accounts[0].balance, beforeBalance); + assert.deepStrictEqual(accounts[1].balance, afterBalance); } catch (err) { datastore = datastoreMock; throw err; From 9371dcb6db7eeab3bf4baa932c7548087aa02260 Mon Sep 17 00:00:00 2001 From: Daniel Bruce Date: Tue, 10 May 2022 09:42:43 -0400 Subject: [PATCH 36/36] Fix sample --- samples/test/concepts.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/test/concepts.test.js b/samples/test/concepts.test.js index 58ad25dce..93581b2d1 100644 --- a/samples/test/concepts.test.js +++ b/samples/test/concepts.test.js @@ -111,8 +111,8 @@ describe('concepts', () => { }) .then(results => { assert.deepStrictEqual(results, { - priorities: priorities, - percentCompletes: percentCompletes, + priorities: [priorities], + percentCompletes: [percentCompletes], }); }); });