diff --git a/packages/client/src/InfluxDBClient.ts b/packages/client/src/InfluxDBClient.ts index 29e314b0..3b0153aa 100644 --- a/packages/client/src/InfluxDBClient.ts +++ b/packages/client/src/InfluxDBClient.ts @@ -6,7 +6,7 @@ import {ClientOptions, QueryType, WriteOptions} from './options' import {IllegalArgumentError} from './errors' import {WritableData, writableDataToLineProtocol} from './util/generics' import {throwReturn} from './util/common' -import {Point} from './Point' +import {PointValues} from './PointValues' const argumentErrorMessage = `\ Please specify the 'database' as a method parameter or use default configuration \ @@ -80,7 +80,7 @@ export default class InfluxDBClient { database: string, queryType: QueryType, measurement: string - ): AsyncGenerator { + ): AsyncGenerator { const points = this._queryApi.queryPoints( query, database ?? diff --git a/packages/client/src/Point.ts b/packages/client/src/Point.ts index 8a703ed6..d87c4265 100644 --- a/packages/client/src/Point.ts +++ b/packages/client/src/Point.ts @@ -2,16 +2,16 @@ import {TimeConverter} from './WriteApi' import {convertTimeToNanos, convertTime} from './util/time' import {escape} from './util/escape' import {WritePrecision} from './options' -import { PointFieldType, PointValues } from "./PointValues" +import {PointFieldType, PointValues} from './PointValues' const fieldToLPString: { -(type: 'float', value: number): string, -(type: 'integer', value: number): string, -(type: 'uinteger', value: number): string, -(type: 'string', value: string): string, -(type: 'boolean', value: boolean): string, -(type: PointFieldType, value: number|string|boolean): string, -} = (type: PointFieldType, value: number|string|boolean): string => { + (type: 'float', value: number): string + (type: 'integer', value: number): string + (type: 'uinteger', value: number): string + (type: 'string', value: string): string + (type: 'boolean', value: boolean): string + (type: PointFieldType, value: number | string | boolean): string +} = (type: PointFieldType, value: number | string | boolean): string => { switch (type) { case 'string': return escape.quoted(value as string) @@ -46,15 +46,15 @@ export class Point { * * @param measurementName - the measurement name */ - constructor(measurementName: string) + private constructor(measurementName: string) /** * Create a new Point with given values. * After creating Point, it's values shouldn't be modified directly by PointValues object. * * @param measurementName - the measurement name */ - constructor(values: PointValues) - constructor(arg0?: PointValues | string) { + private constructor(values: PointValues) + private constructor(arg0?: PointValues | string) { if (arg0 instanceof PointValues) { this._values = arg0 } else { @@ -64,6 +64,14 @@ export class Point { if (typeof arg0 === 'string') this._values.measurement(arg0) } + public static measurement(name: string): Point { + return new Point(name) + } + + public static fromValues(values: PointValues): Point { + return new Point(values) + } + /** * Sets point's measurement. * @@ -76,7 +84,9 @@ export class Point { } public getMeasurement(): string { - return this._values.getMeasurement() as NonNullable> + return this._values.getMeasurement() as NonNullable< + ReturnType + > } /** @@ -140,7 +150,7 @@ export class Point { throw new Error(`uint value for field '${name}' out of range: ${value}`) } this._values.uintField(name, Math.floor(value as number)) - } else { + } else { const strVal = String(value) for (let i = 0; i < strVal.length; i++) { const code = strVal.charCodeAt(i) @@ -197,8 +207,8 @@ export class Point { public stringField(name: string, value: string | any): Point { if (value !== null && value !== undefined) { if (typeof value !== 'string') value = String(value) - this._values.stringField(name, value) - } + this._values.stringField(name, value) + } return this } @@ -328,13 +338,14 @@ export class Point { ): string | undefined { if (!this._values.getMeasurement()) return undefined let fieldsLine = '' - this._values.getFieldNames() + this._values + .getFieldNames() .sort() .forEach((name) => { if (name) { const type = this._values.getFieldType(name) const value = this._values.getField(name) - if (!type || !value) return; + if (type === undefined || value === undefined) return const lpStringValue = fieldToLPString(type, value) if (fieldsLine.length > 0) fieldsLine += ',' fieldsLine += `${escape.tag(name)}=${lpStringValue}` @@ -343,18 +354,16 @@ export class Point { if (fieldsLine.length === 0) return undefined // no fields present let tagsLine = '' const tagNames = this._values.getTagNames() - tagNames - .sort() - .forEach((x) => { - if (x) { - const val = this._values.getTag(x) - if (val) { - tagsLine += ',' - tagsLine += `${escape.tag(x)}=${escape.tag(val)}` - } + tagNames.sort().forEach((x) => { + if (x) { + const val = this._values.getTag(x) + if (val) { + tagsLine += ',' + tagsLine += `${escape.tag(x)}=${escape.tag(val)}` } - }) - let time = this._values.getTimestamp(); + } + }) + let time = this._values.getTimestamp() if (!convertTimePrecision) { time = convertTimeToNanos(time) @@ -364,9 +373,9 @@ export class Point { time = convertTimePrecision(time) } - return `${escape.measurement(this.getMeasurement())}${tagsLine} ${fieldsLine}${ - time !== undefined ? ` ${time}` : '' - }` + return `${escape.measurement( + this.getMeasurement() + )}${tagsLine} ${fieldsLine}${time !== undefined ? ` ${time}` : ''}` } toString(): string { diff --git a/packages/client/src/PointValues.ts b/packages/client/src/PointValues.ts index 15e70bf1..8ef8be20 100644 --- a/packages/client/src/PointValues.ts +++ b/packages/client/src/PointValues.ts @@ -1,4 +1,4 @@ -import { Point } from "./Point" +import {Point} from './Point' export type PointFieldType = | 'float' @@ -72,7 +72,7 @@ export class PointValues { } public getTag(name: string): string | undefined { - return this._tags[name]; + return this._tags[name] } public getTagNames(): string[] { @@ -226,7 +226,9 @@ export class PointValues { * @param value - field value * @returns this */ - public fields(fields: {[key: string]: number | boolean | string}): PointValues { + public fields(fields: { + [key: string]: number | boolean | string + }): PointValues { for (const [name, value] of Object.entries(fields)) { this.field(name, value) } @@ -318,11 +320,11 @@ export class PointValues { } public getTimestamp(): Date | number | string | undefined { - return this._time; + return this._time } public asPoint(): Point { - return new Point(this); + return Point.fromValues(this) } copy(): PointValues { diff --git a/packages/client/src/QueryApi.ts b/packages/client/src/QueryApi.ts index 15f302ce..394937e9 100644 --- a/packages/client/src/QueryApi.ts +++ b/packages/client/src/QueryApi.ts @@ -1,4 +1,4 @@ -import {Point} from './Point' +import {PointValues} from './PointValues' import {QueryType} from './options' /** @@ -25,13 +25,13 @@ export default interface QueryApi { * @param query - The query string. * @param database - The name of the database to query. * @param queryType - The type of query (default: 'sql'). - * @returns An async generator that yields Point object. + * @returns An async generator that yields PointValues object. */ queryPoints( query: string, database: string, queryType: QueryType - ): AsyncGenerator + ): AsyncGenerator close(): Promise } diff --git a/packages/client/src/impl/QueryApiImpl.ts b/packages/client/src/impl/QueryApiImpl.ts index 9b283030..75372221 100644 --- a/packages/client/src/impl/QueryApiImpl.ts +++ b/packages/client/src/impl/QueryApiImpl.ts @@ -6,7 +6,7 @@ import {ConnectionOptions, QueryType} from '../options' import {createInt32Uint8Array} from '../util/common' import {RpcMetadata, RpcOptions} from '@protobuf-ts/runtime-rpc' import {impl} from './implSelector' -import {Point, PointFieldType} from '../Point' +import {PointFieldType, PointValues} from '../PointValues' export default class QueryApiImpl implements QueryApi { private _closed = false @@ -86,12 +86,12 @@ export default class QueryApiImpl implements QueryApi { query: string, database: string, queryType: QueryType - ): AsyncGenerator { + ): AsyncGenerator { const batches = this._queryRawBatches(query, database, queryType) for await (const batch of batches) { for (let rowIndex = 0; rowIndex < batch.numRows; rowIndex++) { - const point = new Point() + const point = new PointValues() for (let columnIndex = 0; columnIndex < batch.numCols; columnIndex++) { const columnSchema = batch.schema.fields[columnIndex] const name = columnSchema.name diff --git a/packages/client/test/integration/e2e.test.ts b/packages/client/test/integration/e2e.test.ts index 5d5e4f7e..9918db96 100644 --- a/packages/client/test/integration/e2e.test.ts +++ b/packages/client/test/integration/e2e.test.ts @@ -1,6 +1,7 @@ import {expect} from 'chai' import {InfluxDBClient, Point} from '../../src' import {rejects} from 'assert' +import {PointValues} from '../../src/PointValues' const getEnvVariables = () => { const { @@ -42,7 +43,7 @@ describe('e2e test', () => { const avg1 = getRandomInt(110, 500) const max1 = getRandomInt(900, 1000) - const point = new Point('stat') + const point = Point.measurement('stat') .tag('unit', 'temperature') .floatField('avg', avg1) .floatField('max', max1) @@ -76,7 +77,7 @@ describe('e2e test', () => { const dataPoints = client.queryPoints(query, database, queryType, 'stat') - let pointRow: IteratorResult + let pointRow: IteratorResult pointRow = await dataPoints.next() expect(pointRow.done).to.equal(false) diff --git a/packages/client/test/unit/Write.test.ts b/packages/client/test/unit/Write.test.ts index 91978728..461b4538 100644 --- a/packages/client/test/unit/Write.test.ts +++ b/packages/client/test/unit/Write.test.ts @@ -71,10 +71,16 @@ describe('Write', () => { await rejects(subject.write('text value=1', DATABASE)) await rejects(subject.write(['text value=1', 'text value=2'], DATABASE)) await rejects( - subject.write(new Point('test').floatField('value', 1), DATABASE) + subject.write( + Point.measurement('test').floatField('value', 1), + DATABASE + ) ) await rejects( - subject.write([new Point('test').floatField('value', 1)], DATABASE) + subject.write( + [Point.measurement('test').floatField('value', 1)], + DATABASE + ) ) }) }) @@ -129,7 +135,7 @@ describe('Write', () => { } }) .persist() - const point = new Point('test') + const point = Point.measurement('test') .tag('t', ' ') .floatField('value', 1) .timestamp('') @@ -155,7 +161,7 @@ describe('Write', () => { requests = 0 // generates no lines, no requests done - await subject.write(new Point(), DATABASE) + await subject.write(Point.measurement('m'), DATABASE) await subject.write([], DATABASE) await subject.write('', DATABASE) expect(requests).to.equal(0) @@ -163,10 +169,12 @@ describe('Write', () => { expect(logs.warn).has.length(0) const points = [ - new Point('test').floatField('value', 1).timestamp('1'), - new Point('test').floatField('value', 2).timestamp(2.1), - new Point('test').floatField('value', 3).timestamp(new Date(3)), - new Point('test') + Point.measurement('test').floatField('value', 1).timestamp('1'), + Point.measurement('test').floatField('value', 2).timestamp(2.1), + Point.measurement('test') + .floatField('value', 3) + .timestamp(new Date(3)), + Point.measurement('test') .floatField('value', 4) .timestamp(false as any as string), // server decides what to do with such values ] @@ -224,7 +232,10 @@ describe('Write', () => { return [204, '', {}] }) .persist() - await subject.write(new Point('test').floatField('value', 1), DATABASE) + await subject.write( + Point.measurement('test').floatField('value', 1), + DATABASE + ) expect(logs.error).has.length(0) expect(logs.warn).has.length(0) expect(authorization).equals(`Token customToken`) @@ -241,7 +252,10 @@ describe('Write', () => { return [204, '', {}] }) .persist() - await subject.write(new Point('test').floatField('value', 1), DATABASE) + await subject.write( + Point.measurement('test').floatField('value', 1), + DATABASE + ) await subject.close() expect(logs.error).has.length(0) expect(logs.warn).deep.equals([]) diff --git a/packages/client/test/unit/util/generics.test.ts b/packages/client/test/unit/util/generics.test.ts index 43ab0ce1..a5207de2 100644 --- a/packages/client/test/unit/util/generics.test.ts +++ b/packages/client/test/unit/util/generics.test.ts @@ -22,7 +22,7 @@ describe('writableDataToLineProtocol', () => { }) it('should convert single Point to line protocol', () => { - const point = new Point('test').floatField('blah', 123.6) + const point = Point.measurement('test').floatField('blah', 123.6) const output = writableDataToLineProtocol(point) expect(output.length).to.equal(1) expect(output[0]).satisfies((x: string) => { @@ -31,10 +31,14 @@ describe('writableDataToLineProtocol', () => { }) it('should convert array-like Point to line protocol', () => { - const point1 = new Point('test').floatField('blah', 123.6) + const point1 = Point.measurement('test').floatField('blah', 123.6) const date = Date.now() - const point2 = new Point('test').floatField('blah', 456.7).timestamp(date) - const point3 = new Point('test').floatField('blah', 789.8).timestamp('') + const point2 = Point.measurement('test') + .floatField('blah', 456.7) + .timestamp(date) + const point3 = Point.measurement('test') + .floatField('blah', 789.8) + .timestamp('') const input: WritableData = [point1, point2, point3] const output = writableDataToLineProtocol(input) expect(output.length).to.equal(3) diff --git a/packages/client/test/unit/util/point.test.ts b/packages/client/test/unit/util/point.test.ts index 54ed4647..db6d3484 100644 --- a/packages/client/test/unit/util/point.test.ts +++ b/packages/client/test/unit/util/point.test.ts @@ -3,8 +3,7 @@ import {Point, convertTime} from '../../../src' describe('point', () => { it('creates point with various fields', () => { - const point = new Point() - .measurement('blah') + const point = Point.measurement('blah') .booleanField('truthy', true) .booleanField('falsy', false) .intField('intFromString', '20') @@ -18,32 +17,32 @@ describe('point', () => { it('fails on invalid fields', () => { expect(() => { - new Point().intField('fails', NaN) + Point.measurement('a').intField('fails', NaN) }).to.throw(`invalid integer value for field 'fails': 'NaN'`) expect(() => { - new Point().intField('fails', Infinity) + Point.measurement('a').intField('fails', Infinity) }).to.throw(`invalid integer value for field 'fails': 'Infinity'!`) expect(() => { - new Point().intField('fails', 9223372036854776e3) + Point.measurement('a').intField('fails', 9223372036854776e3) }).to.throw( `invalid integer value for field 'fails': '9223372036854776000'!` ) expect(() => { - new Point().floatField('fails', Infinity) + Point.measurement('a').floatField('fails', Infinity) }).to.throw(`invalid float value for field 'fails': 'Infinity'!`) expect(() => { - new Point().uintField('fails', NaN) + Point.measurement('a').uintField('fails', NaN) }).to.throw(`uint value for field 'fails' out of range: NaN`) expect(() => { - new Point().uintField('fails', -1) + Point.measurement('a').uintField('fails', -1) }).to.throw(`uint value for field 'fails' out of range: -1`) expect(() => { - new Point().uintField('fails', Number.MAX_SAFE_INTEGER + 10) + Point.measurement('a').uintField('fails', Number.MAX_SAFE_INTEGER + 10) }).to.throw( `uint value for field 'fails' out of range: ${ Number.MAX_SAFE_INTEGER + 10 @@ -51,18 +50,18 @@ describe('point', () => { ) expect(() => { - new Point().uintField('fails', '10a8') + Point.measurement('a').uintField('fails', '10a8') }).to.throw(`uint value has an unsupported character at pos 2: 10a8`) expect(() => { - new Point().uintField('fails', '18446744073709551616') + Point.measurement('a').uintField('fails', '18446744073709551616') }).to.throw( `uint value for field 'fails' out of range: 18446744073709551616` ) }) it('infers type when no type supported', () => { - const point = new Point('a') + const point = Point.measurement('a') .fields({ float: 20.3, float2: 20, @@ -78,15 +77,14 @@ describe('point', () => { it('throws when invalid type for method field is provided', () => { expect(() => { - new Point().field('errorlike', undefined, 'bad-type' as any) + Point.measurement('a').field('errorlike', undefined, 'bad-type' as any) }).to.throw( `invalid field type for field 'errorlike': type -> bad-type, value -> undefined!` ) }) it('adds field using field method', () => { - const point = new Point() - .measurement('blah') + const point = Point.measurement('blah') .field('truthy', true, 'boolean') .field('falsy', false, 'boolean') .field('intFromString', '20', 'integer') @@ -99,7 +97,7 @@ describe('point', () => { }) it('creates point with uint fields', () => { - const point = new Point('a') + const point = Point.measurement('a') .uintField('floored', 10.88) .uintField('fromString', '789654123') .timestamp('') @@ -109,7 +107,7 @@ describe('point', () => { }) it('returns field of with getField and throws if type not match', () => { - const point = new Point('a').fields({ + const point = Point.measurement('a').fields({ float: 20.3, float2: 20, string: 'text', @@ -131,8 +129,7 @@ describe('point', () => { }) it('creates deep copy of point', () => { - const point = new Point() - .measurement('measure1') + const point = Point.measurement('measure1') .booleanField('truthy', true) .booleanField('falsy', false) .intField('intFromString', '20') @@ -156,20 +153,20 @@ describe('point', () => { convertTime(value, precision) it('converts empty string to no timestamp', () => { - const p = new Point('a').floatField('b', 1).timestamp('') + const p = Point.measurement('a').floatField('b', 1).timestamp('') expect(p.toLineProtocol(clinetConvertTime)).equals('a b=1') }) it('converts number to timestamp', () => { - const p = new Point('a').floatField('b', 1).timestamp(1.2) + const p = Point.measurement('a').floatField('b', 1).timestamp(1.2) expect(p.toLineProtocol(clinetConvertTime)).equals('a b=1 1') }) it('converts Date to timestamp', () => { const d = new Date() - const p = new Point('a').floatField('b', 1).timestamp(d) + const p = Point.measurement('a').floatField('b', 1).timestamp(d) expect(p.toLineProtocol(precision)).equals(`a b=1 ${d.getTime()}`) }) it('converts undefined to local timestamp', () => { - const p = new Point('a').floatField('b', 1) + const p = Point.measurement('a').floatField('b', 1) expect(p.toLineProtocol(precision)).satisfies((x: string) => { return x.startsWith('a b=1') }, `does not start with 'a b=1'`) @@ -178,7 +175,10 @@ describe('point', () => { }) }) it('toString() works same as toLineProtocol()', () => { - const p = new Point('a').floatField('b', 1).tag('c', 'd').timestamp('') + const p = Point.measurement('a') + .floatField('b', 1) + .tag('c', 'd') + .timestamp('') expect(p.toLineProtocol()).equals(p.toString()) }) })