@synnaxlabs/client 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.DS_Store +0 -0
- package/.editorconfig +15 -0
- package/.eslintrc.json +33 -0
- package/.gitignore +9 -0
- package/.nyc_output/20720f2d-6abe-420f-a3c5-304d52d60827.json +1 -0
- package/.nyc_output/4725921c-6f1b-4ae9-9819-e455f702d31c.json +1 -0
- package/.nyc_output/47478588-5ffd-4332-873c-facaa4a2fc38.json +1 -0
- package/.nyc_output/48180641-e0b2-49ab-a6eb-e7910e9eac2f.json +1 -0
- package/.nyc_output/cb0abf31-740f-47db-b94a-8e3f8f117cb8.json +1 -0
- package/.nyc_output/fc77fce2-dad0-49a8-8d4b-0a9014ecf8c5.json +1 -0
- package/.nyc_output/processinfo/20720f2d-6abe-420f-a3c5-304d52d60827.json +1 -0
- package/.nyc_output/processinfo/4725921c-6f1b-4ae9-9819-e455f702d31c.json +1 -0
- package/.nyc_output/processinfo/47478588-5ffd-4332-873c-facaa4a2fc38.json +1 -0
- package/.nyc_output/processinfo/48180641-e0b2-49ab-a6eb-e7910e9eac2f.json +1 -0
- package/.nyc_output/processinfo/cb0abf31-740f-47db-b94a-8e3f8f117cb8.json +1 -0
- package/.nyc_output/processinfo/fc77fce2-dad0-49a8-8d4b-0a9014ecf8c5.json +1 -0
- package/.nyc_output/processinfo/index.json +1 -0
- package/.prettierignore +2 -0
- package/CHANGELOG.md +5 -0
- package/build/main/index.d.ts +1 -1
- package/build/main/index.js +2 -3
- package/build/main/lib/auth.d.ts +54 -0
- package/build/main/lib/auth.js +62 -0
- package/build/main/lib/auth.spec.d.ts +1 -0
- package/build/main/lib/auth.spec.js +39 -0
- package/build/main/lib/channel/channel.spec.js +17 -3
- package/build/main/lib/channel/client.d.ts +2 -2
- package/build/main/lib/channel/client.js +6 -3
- package/build/main/lib/channel/payload.d.ts +2 -2
- package/build/main/lib/client.d.ts +13 -6
- package/build/main/lib/client.js +16 -4
- package/build/main/lib/segment/iterator.spec.js +14 -3
- package/build/main/lib/segment/typed.js +4 -4
- package/build/main/lib/segment/writer.spec.js +17 -3
- package/build/main/lib/telem.d.ts +2 -2
- package/build/main/lib/telem.js +4 -4
- package/build/main/lib/telem.spec.js +4 -2
- package/build/main/lib/transport.d.ts +2 -1
- package/build/main/lib/transport.js +5 -1
- package/build/main/lib/user/payload.d.ts +12 -0
- package/build/main/lib/user/payload.js +9 -0
- package/build/main/setupspecs.d.ts +4 -0
- package/build/main/setupspecs.js +17 -0
- package/build/module/index.d.ts +1 -1
- package/build/module/index.js +2 -2
- package/build/module/lib/auth.d.ts +54 -0
- package/build/module/lib/auth.js +63 -0
- package/build/module/lib/auth.spec.d.ts +1 -0
- package/build/module/lib/auth.spec.js +34 -0
- package/build/module/lib/channel/channel.spec.js +17 -3
- package/build/module/lib/channel/client.d.ts +2 -2
- package/build/module/lib/channel/client.js +6 -3
- package/build/module/lib/channel/payload.d.ts +2 -2
- package/build/module/lib/client.d.ts +13 -6
- package/build/module/lib/client.js +17 -4
- package/build/module/lib/segment/iterator.spec.js +14 -3
- package/build/module/lib/segment/typed.js +5 -5
- package/build/module/lib/segment/writer.spec.js +18 -4
- package/build/module/lib/telem.d.ts +2 -2
- package/build/module/lib/telem.js +4 -4
- package/build/module/lib/telem.spec.js +5 -3
- package/build/module/lib/transport.d.ts +2 -1
- package/build/module/lib/transport.js +5 -1
- package/build/module/lib/user/payload.d.ts +12 -0
- package/build/module/lib/user/payload.js +6 -0
- package/build/module/setupspecs.d.ts +4 -0
- package/build/module/setupspecs.js +16 -0
- package/build/tsconfig.module.tsbuildinfo +1 -0
- package/build/tsconfig.tsbuildinfo +1 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +191 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +191 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +196 -0
- package/coverage/lcov-report/src/index.html +116 -0
- package/coverage/lcov-report/src/lib/auth.ts.html +340 -0
- package/coverage/lcov-report/src/lib/channel/client.ts.html +604 -0
- package/coverage/lcov-report/src/lib/channel/creator.ts.html +304 -0
- package/coverage/lcov-report/src/lib/channel/index.html +176 -0
- package/coverage/lcov-report/src/lib/channel/payload.ts.html +139 -0
- package/coverage/lcov-report/src/lib/channel/registry.ts.html +202 -0
- package/coverage/lcov-report/src/lib/channel/retriever.ts.html +244 -0
- package/coverage/lcov-report/src/lib/client.ts.html +244 -0
- package/coverage/lcov-report/src/lib/errors.ts.html +484 -0
- package/coverage/lcov-report/src/lib/index.html +176 -0
- package/coverage/lcov-report/src/lib/segment/client.ts.html +463 -0
- package/coverage/lcov-report/src/lib/segment/index.html +206 -0
- package/coverage/lcov-report/src/lib/segment/iterator.ts.html +928 -0
- package/coverage/lcov-report/src/lib/segment/payload.ts.html +139 -0
- package/coverage/lcov-report/src/lib/segment/splitter.ts.html +181 -0
- package/coverage/lcov-report/src/lib/segment/typed.ts.html +307 -0
- package/coverage/lcov-report/src/lib/segment/validator.ts.html +331 -0
- package/coverage/lcov-report/src/lib/segment/writer.ts.html +727 -0
- package/coverage/lcov-report/src/lib/telem.ts.html +2056 -0
- package/coverage/lcov-report/src/lib/transport.ts.html +196 -0
- package/coverage/lcov-report/src/lib/user/index.html +116 -0
- package/coverage/lcov-report/src/lib/user/payload.ts.html +109 -0
- package/coverage/lcov-report/src/lib/util/index.html +116 -0
- package/coverage/lcov-report/src/lib/util/telem.ts.html +124 -0
- package/coverage/lcov-report/src/setupspecs.ts.html +133 -0
- package/coverage/lcov.info +1230 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +196 -0
- package/coverage/src/index.html +116 -0
- package/coverage/src/lib/auth.ts.html +340 -0
- package/coverage/src/lib/channel/client.ts.html +604 -0
- package/coverage/src/lib/channel/creator.ts.html +304 -0
- package/coverage/src/lib/channel/index.html +176 -0
- package/coverage/src/lib/channel/payload.ts.html +139 -0
- package/coverage/src/lib/channel/registry.ts.html +202 -0
- package/coverage/src/lib/channel/retriever.ts.html +244 -0
- package/coverage/src/lib/client.ts.html +244 -0
- package/coverage/src/lib/errors.ts.html +484 -0
- package/coverage/src/lib/index.html +176 -0
- package/coverage/src/lib/segment/client.ts.html +463 -0
- package/coverage/src/lib/segment/index.html +206 -0
- package/coverage/src/lib/segment/iterator.ts.html +928 -0
- package/coverage/src/lib/segment/payload.ts.html +139 -0
- package/coverage/src/lib/segment/splitter.ts.html +181 -0
- package/coverage/src/lib/segment/typed.ts.html +307 -0
- package/coverage/src/lib/segment/validator.ts.html +331 -0
- package/coverage/src/lib/segment/writer.ts.html +727 -0
- package/coverage/src/lib/telem.ts.html +2056 -0
- package/coverage/src/lib/transport.ts.html +196 -0
- package/coverage/src/lib/user/index.html +116 -0
- package/coverage/src/lib/user/payload.ts.html +109 -0
- package/coverage/src/lib/util/index.html +116 -0
- package/coverage/src/lib/util/telem.ts.html +124 -0
- package/coverage/src/setupspecs.ts.html +133 -0
- package/package.json +2 -2
- package/src/index.ts +13 -0
- package/src/lib/.DS_Store +0 -0
- package/src/lib/auth.spec.ts +36 -0
- package/src/lib/auth.ts +85 -0
- package/src/lib/channel/channel.spec.ts +49 -0
- package/src/lib/channel/client.ts +173 -0
- package/src/lib/channel/creator.ts +73 -0
- package/src/lib/channel/payload.ts +18 -0
- package/src/lib/channel/registry.ts +39 -0
- package/src/lib/channel/retriever.ts +53 -0
- package/src/lib/client.ts +53 -0
- package/src/lib/errors.ts +133 -0
- package/src/lib/segment/client.ts +126 -0
- package/src/lib/segment/iterator.spec.ts +78 -0
- package/src/lib/segment/iterator.ts +281 -0
- package/src/lib/segment/payload.ts +18 -0
- package/src/lib/segment/splitter.ts +32 -0
- package/src/lib/segment/typed.ts +74 -0
- package/src/lib/segment/validator.ts +82 -0
- package/src/lib/segment/writer.spec.ts +85 -0
- package/src/lib/segment/writer.ts +214 -0
- package/src/lib/telem.spec.ts +200 -0
- package/src/lib/telem.ts +657 -0
- package/src/lib/transport.ts +37 -0
- package/src/lib/user/payload.ts +8 -0
- package/src/lib/util/telem.ts +13 -0
- package/src/setupspecs.ts +16 -0
- package/tsconfig.json +47 -0
- package/tsconfig.module.json +9 -0
- package/yarn-error.log +5756 -0
- package/yarn.lock +5936 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { UnaryClient } from '@synnaxlabs/freighter';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
import Transport from '../transport';
|
|
5
|
+
|
|
6
|
+
import { ChannelPayload, ChannelPayloadSchema } from './payload';
|
|
7
|
+
|
|
8
|
+
const RequestSchema = z.object({
|
|
9
|
+
keys: z.string().array().optional(),
|
|
10
|
+
nodeId: z.number().optional(),
|
|
11
|
+
names: z.string().array().optional(),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
type Request = z.infer<typeof RequestSchema>;
|
|
15
|
+
|
|
16
|
+
const ResponseSchema = z.object({
|
|
17
|
+
channels: ChannelPayloadSchema.array(),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export default class Retriever {
|
|
21
|
+
private static ENDPOINT = '/channel/retrieve';
|
|
22
|
+
private client: UnaryClient;
|
|
23
|
+
|
|
24
|
+
constructor(transport: Transport) {
|
|
25
|
+
this.client = transport.getClient();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private async execute(request: Request): Promise<ChannelPayload[]> {
|
|
29
|
+
const [res, err] = await this.client.send(
|
|
30
|
+
Retriever.ENDPOINT,
|
|
31
|
+
request,
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
33
|
+
// @ts-ignore
|
|
34
|
+
ResponseSchema
|
|
35
|
+
);
|
|
36
|
+
if (err) {
|
|
37
|
+
throw err;
|
|
38
|
+
}
|
|
39
|
+
return res?.channels as ChannelPayload[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async retrieveByKeys(...keys: string[]): Promise<ChannelPayload[]> {
|
|
43
|
+
return await this.execute({ keys });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async retrieveByNames(...names: string[]): Promise<ChannelPayload[]> {
|
|
47
|
+
return await this.execute({ names });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async retrieveByNodeID(nodeId: number): Promise<ChannelPayload[]> {
|
|
51
|
+
return await this.execute({ nodeId: nodeId });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { URL } from '@synnaxlabs/freighter';
|
|
2
|
+
|
|
3
|
+
import AuthenticationClient from './auth';
|
|
4
|
+
import ChannelClient from './channel/client';
|
|
5
|
+
import ChannelCreator from './channel/creator';
|
|
6
|
+
import Registry from './channel/registry';
|
|
7
|
+
import ChannelRetriever from './channel/retriever';
|
|
8
|
+
import SegmentClient from './segment/client';
|
|
9
|
+
import Transport from './transport';
|
|
10
|
+
|
|
11
|
+
export type SynnaxProps = {
|
|
12
|
+
host: string;
|
|
13
|
+
port: number;
|
|
14
|
+
username?: string;
|
|
15
|
+
password?: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Client to perform operations against a Synnax cluster.
|
|
20
|
+
*
|
|
21
|
+
* @property channel - Channel client for creating and retrieving channels.
|
|
22
|
+
* @property data - Data client for reading and writing telemetry.
|
|
23
|
+
*/
|
|
24
|
+
export default class Synnax {
|
|
25
|
+
private transport: Transport;
|
|
26
|
+
data: SegmentClient;
|
|
27
|
+
channel: ChannelClient;
|
|
28
|
+
auth: AuthenticationClient | undefined;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param props.host - Hostname of a node in the cluster.
|
|
32
|
+
* @param props.port - Port of the node in the cluster.
|
|
33
|
+
* @param props.username - Username for authentication. Not required if the
|
|
34
|
+
* cluster is insecure.
|
|
35
|
+
* @param props.password - Password for authentication. Not required if the
|
|
36
|
+
* cluster is insecure.
|
|
37
|
+
*/
|
|
38
|
+
constructor({ host, port, username, password }: SynnaxProps) {
|
|
39
|
+
this.transport = new Transport(new URL({ host, port }));
|
|
40
|
+
if (username && password) {
|
|
41
|
+
this.auth = new AuthenticationClient(this.transport.httpFactory, {
|
|
42
|
+
username,
|
|
43
|
+
password,
|
|
44
|
+
});
|
|
45
|
+
this.transport.use(this.auth.middleware());
|
|
46
|
+
}
|
|
47
|
+
const chRetriever = new ChannelRetriever(this.transport);
|
|
48
|
+
const chCreator = new ChannelCreator(this.transport);
|
|
49
|
+
const chRegistry = new Registry(chRetriever);
|
|
50
|
+
this.data = new SegmentClient(this.transport, chRegistry);
|
|
51
|
+
this.channel = new ChannelClient(this.data, chRetriever, chCreator);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { BaseTypedError, registerError } from '@synnaxlabs/freighter';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
const _FREIGHTER_EXCEPTION_TYPE = 'synnax.api.errors';
|
|
5
|
+
|
|
6
|
+
const APIErrorPayloadSchema = z.object({
|
|
7
|
+
type: z.string(),
|
|
8
|
+
error: z.record(z.unknown()),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
type APIErrorPayload = z.infer<typeof APIErrorPayloadSchema>;
|
|
12
|
+
|
|
13
|
+
enum APIErrorType {
|
|
14
|
+
General = 'general',
|
|
15
|
+
Nil = 'nil',
|
|
16
|
+
Parse = 'parse',
|
|
17
|
+
Auth = 'auth',
|
|
18
|
+
Unexpected = 'unexpected',
|
|
19
|
+
Validation = 'validation',
|
|
20
|
+
Query = 'query',
|
|
21
|
+
Route = 'route',
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type Field = {
|
|
25
|
+
field: string;
|
|
26
|
+
message: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
class BaseError extends BaseTypedError {
|
|
30
|
+
constructor(message: string) {
|
|
31
|
+
super(message, _FREIGHTER_EXCEPTION_TYPE);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Raised when a validation error occurs.
|
|
37
|
+
*/
|
|
38
|
+
export class ValidationError extends BaseError {
|
|
39
|
+
fields: Field[];
|
|
40
|
+
|
|
41
|
+
constructor(fieldsOrMessage: string | Field[] | Field) {
|
|
42
|
+
if (typeof fieldsOrMessage === 'string') {
|
|
43
|
+
super(fieldsOrMessage);
|
|
44
|
+
this.fields = [];
|
|
45
|
+
} else if (Array.isArray(fieldsOrMessage)) {
|
|
46
|
+
super(
|
|
47
|
+
fieldsOrMessage
|
|
48
|
+
.map((field) => `${field.field}: ${field.message}`)
|
|
49
|
+
.join('\n')
|
|
50
|
+
);
|
|
51
|
+
this.fields = fieldsOrMessage;
|
|
52
|
+
} else {
|
|
53
|
+
super(`${fieldsOrMessage.field}: ${fieldsOrMessage.message}`);
|
|
54
|
+
this.fields = [fieldsOrMessage];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* GeneralError is raised when a general error occurs.
|
|
61
|
+
*/
|
|
62
|
+
export class GeneralError extends BaseError {}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* ParseError is raised when a parse error occurs.
|
|
66
|
+
*/
|
|
67
|
+
export class ParseError extends BaseError {}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* AuthError is raised when an authentication error occurs.
|
|
71
|
+
*/
|
|
72
|
+
export class AuthError extends BaseError {}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* UnexpectedError is raised when an unexpected error occurs.
|
|
76
|
+
*/
|
|
77
|
+
export class UnexpectedError extends BaseError {}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* QueryError is raised when a query error occurs.
|
|
81
|
+
*/
|
|
82
|
+
export class QueryError extends BaseError {}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* RouteError is raised when a routing error occurs.
|
|
86
|
+
*/
|
|
87
|
+
export class RouteError extends BaseError {
|
|
88
|
+
path: string;
|
|
89
|
+
|
|
90
|
+
constructor(message: string, path: string) {
|
|
91
|
+
super(message);
|
|
92
|
+
this.path = path;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Raised when time-series data is not contiguous.
|
|
98
|
+
*/
|
|
99
|
+
export class ContiguityError extends BaseError {}
|
|
100
|
+
|
|
101
|
+
const parsePayload = (payload: APIErrorPayload): Error | undefined => {
|
|
102
|
+
switch (payload.type) {
|
|
103
|
+
case APIErrorType.General:
|
|
104
|
+
return new GeneralError(payload.error.message as string);
|
|
105
|
+
case APIErrorType.Parse:
|
|
106
|
+
return new ParseError(payload.error.message as string);
|
|
107
|
+
case APIErrorType.Auth:
|
|
108
|
+
return new AuthError(payload.error.message as string);
|
|
109
|
+
case APIErrorType.Unexpected:
|
|
110
|
+
return new UnexpectedError(JSON.stringify(payload.error));
|
|
111
|
+
case APIErrorType.Validation:
|
|
112
|
+
return new ValidationError(payload.error.fields as string | Field[]);
|
|
113
|
+
case APIErrorType.Query:
|
|
114
|
+
return new QueryError(payload.error.message as string);
|
|
115
|
+
case APIErrorType.Route:
|
|
116
|
+
return new RouteError(
|
|
117
|
+
payload.error.path as string,
|
|
118
|
+
payload.error.message as string
|
|
119
|
+
);
|
|
120
|
+
default:
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const decode = (encoded: string): Error | undefined => {
|
|
126
|
+
return parsePayload(APIErrorPayloadSchema.parse(JSON.parse(encoded)));
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const encode = (): string => {
|
|
130
|
+
throw new Error('Not implemented');
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
registerError({ type: _FREIGHTER_EXCEPTION_TYPE, encode, decode });
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import Registry from '../channel/registry';
|
|
2
|
+
import { TimeRange, TypedArray, UnparsedTimeStamp } from '../telem';
|
|
3
|
+
import Transport from '../transport';
|
|
4
|
+
|
|
5
|
+
import { TypedIterator } from './iterator';
|
|
6
|
+
import TypedSegment from './typed';
|
|
7
|
+
import { TypedWriter } from './writer';
|
|
8
|
+
|
|
9
|
+
export default class SegmentClient {
|
|
10
|
+
private transport: Transport;
|
|
11
|
+
private channels: Registry;
|
|
12
|
+
|
|
13
|
+
constructor(transport: Transport, channels: Registry) {
|
|
14
|
+
this.transport = transport;
|
|
15
|
+
this.channels = channels;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Opens a new iterator over the given channels within the provided time range.
|
|
20
|
+
*
|
|
21
|
+
* @param tr - A time range to iterate over.
|
|
22
|
+
* @param keys - A list of channel keys to iterate over.
|
|
23
|
+
* @param aggregate - Whether to accumulate iteration results or reset them
|
|
24
|
+
* on every iterator method call.
|
|
25
|
+
* @returns a new {@link TypedIterator}.
|
|
26
|
+
*/
|
|
27
|
+
async newIterator(
|
|
28
|
+
tr: TimeRange,
|
|
29
|
+
keys: string[],
|
|
30
|
+
aggregate: boolean
|
|
31
|
+
): Promise<TypedIterator> {
|
|
32
|
+
const iter = new TypedIterator(
|
|
33
|
+
this.transport.streamClient,
|
|
34
|
+
this.channels,
|
|
35
|
+
aggregate
|
|
36
|
+
);
|
|
37
|
+
await iter.open(tr, keys);
|
|
38
|
+
return iter;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Opens a new writer on the given channels.
|
|
43
|
+
*
|
|
44
|
+
* @param keys - The keys of the channels to write to. A writer cannot write to
|
|
45
|
+
* a channel that is not in this list. See the {@link TypedWriter} documentation
|
|
46
|
+
* for more information.
|
|
47
|
+
* @returns a new {@link TypedWriter}.
|
|
48
|
+
*/
|
|
49
|
+
async newWriter(keys: string[]): Promise<TypedWriter> {
|
|
50
|
+
const writer = new TypedWriter(this.transport.streamClient, this.channels);
|
|
51
|
+
await writer.open(keys);
|
|
52
|
+
return writer;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Writes telemetry to the given channel starting at the given timestamp.
|
|
57
|
+
*
|
|
58
|
+
* @param to - The key of the channel to write to.
|
|
59
|
+
* @param start - The starting timestamp of the first sample in data.
|
|
60
|
+
* @param data - The telemetry to write. This telemetry must have the same
|
|
61
|
+
* data type as the channel.
|
|
62
|
+
* @throws if the channel does not exist.
|
|
63
|
+
*/
|
|
64
|
+
async write(
|
|
65
|
+
to: string,
|
|
66
|
+
start: UnparsedTimeStamp,
|
|
67
|
+
data: TypedArray
|
|
68
|
+
): Promise<boolean> {
|
|
69
|
+
const writer = await this.newWriter([to]);
|
|
70
|
+
try {
|
|
71
|
+
return await writer.write(to, start, data);
|
|
72
|
+
} finally {
|
|
73
|
+
await writer.close();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Reads telemetry from the channel between the two timestamps.
|
|
79
|
+
*
|
|
80
|
+
* @param from - The key of the channel to read from.
|
|
81
|
+
* @param start - The starting timestamp of the range to read from.
|
|
82
|
+
* @param end - The ending timestamp of the range to read from.
|
|
83
|
+
* @returns a typed array containing the retrieved telemetry.
|
|
84
|
+
* @throws if the channel does not exist.
|
|
85
|
+
* @throws if the telemetry between start and end is not contiguous.
|
|
86
|
+
*/
|
|
87
|
+
async read(
|
|
88
|
+
from: string,
|
|
89
|
+
start: UnparsedTimeStamp,
|
|
90
|
+
end: UnparsedTimeStamp
|
|
91
|
+
): Promise<TypedArray> {
|
|
92
|
+
return (await this.readSegment(from, start, end)).view;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Reads a segment from the channel between the two timestamps.
|
|
97
|
+
*
|
|
98
|
+
* @param from - The key of the channel to read from.
|
|
99
|
+
* @param start - The starting timestamp of the range to read from.
|
|
100
|
+
* @param end - The ending timestamp of the range to read from.
|
|
101
|
+
* @returns a segment containing the retrieved telemetry.
|
|
102
|
+
* @throws if the channel does not exist.
|
|
103
|
+
* @throws if the telemetry between start and end is not contiguous.
|
|
104
|
+
*/
|
|
105
|
+
async readSegment(
|
|
106
|
+
from: string,
|
|
107
|
+
start: UnparsedTimeStamp,
|
|
108
|
+
end: UnparsedTimeStamp
|
|
109
|
+
): Promise<TypedSegment> {
|
|
110
|
+
const iter = await this.newIterator(
|
|
111
|
+
new TimeRange(start, end),
|
|
112
|
+
[from],
|
|
113
|
+
true
|
|
114
|
+
);
|
|
115
|
+
let seg: TypedSegment;
|
|
116
|
+
try {
|
|
117
|
+
await iter.first();
|
|
118
|
+
// eslint-disable-next-line no-empty
|
|
119
|
+
while (await iter.next()) {}
|
|
120
|
+
seg = (await iter.value())[from];
|
|
121
|
+
} finally {
|
|
122
|
+
await iter.close();
|
|
123
|
+
}
|
|
124
|
+
return seg as TypedSegment;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
|
|
3
|
+
import { newClient } from '../../setupspecs';
|
|
4
|
+
import { ContiguityError } from '../errors';
|
|
5
|
+
import { DataType, Rate, TimeRange, TimeSpan } from '../telem';
|
|
6
|
+
import { randomTypedArray } from '../util/telem';
|
|
7
|
+
|
|
8
|
+
const client = newClient();
|
|
9
|
+
|
|
10
|
+
const newChannel = async () => {
|
|
11
|
+
return await client.channel.create({
|
|
12
|
+
name: 'test',
|
|
13
|
+
nodeId: 1,
|
|
14
|
+
rate: Rate.Hz(25),
|
|
15
|
+
dataType: DataType.Float64,
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
test('TypedIterator - basic iteration', async (t) => {
|
|
20
|
+
const ch = await newChannel();
|
|
21
|
+
const writer = await client.data.newWriter([ch.key]);
|
|
22
|
+
const data = randomTypedArray(25, ch.dataType);
|
|
23
|
+
try {
|
|
24
|
+
await writer.write(ch.key, TimeSpan.Second, data);
|
|
25
|
+
await writer.write(ch.key, TimeSpan.Seconds(2), data);
|
|
26
|
+
await writer.write(ch.key, TimeSpan.Seconds(3), data);
|
|
27
|
+
} finally {
|
|
28
|
+
await writer.close();
|
|
29
|
+
}
|
|
30
|
+
const iterator = await client.data.newIterator(
|
|
31
|
+
new TimeRange(TimeSpan.Zero, TimeSpan.Seconds(4)),
|
|
32
|
+
[ch.key],
|
|
33
|
+
false
|
|
34
|
+
);
|
|
35
|
+
try {
|
|
36
|
+
t.true(await iterator.first());
|
|
37
|
+
t.true((await iterator.value())[ch.key].view.length === 25);
|
|
38
|
+
let c = 1;
|
|
39
|
+
while (await iterator.next()) {
|
|
40
|
+
c++;
|
|
41
|
+
t.true((await iterator.value())[ch.key].view.length === 25);
|
|
42
|
+
}
|
|
43
|
+
t.true(c === 3);
|
|
44
|
+
} finally {
|
|
45
|
+
await iterator.close();
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('Client - basic read', async (t) => {
|
|
50
|
+
const ch = await newChannel();
|
|
51
|
+
const writer = await client.data.newWriter([ch.key]);
|
|
52
|
+
const data = randomTypedArray(25, ch.dataType);
|
|
53
|
+
try {
|
|
54
|
+
await writer.write(ch.key, TimeSpan.Second, data);
|
|
55
|
+
await writer.write(ch.key, TimeSpan.Seconds(2), data);
|
|
56
|
+
await writer.write(ch.key, TimeSpan.Seconds(3), data);
|
|
57
|
+
} finally {
|
|
58
|
+
await writer.close();
|
|
59
|
+
}
|
|
60
|
+
const resData = await client.data.read(
|
|
61
|
+
ch.key,
|
|
62
|
+
TimeSpan.Zero,
|
|
63
|
+
TimeSpan.Seconds(4)
|
|
64
|
+
);
|
|
65
|
+
resData.slice(0, 25).forEach((v, i) => t.true(v === data[i]));
|
|
66
|
+
t.true(resData.length === 75);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('Client - incontiguous read', async (t) => {
|
|
70
|
+
const ch = await newChannel();
|
|
71
|
+
const data = randomTypedArray(25, ch.dataType);
|
|
72
|
+
await ch.write(TimeSpan.Zero, data);
|
|
73
|
+
await ch.write(TimeSpan.Seconds(2), data);
|
|
74
|
+
const err = await t.throwsAsync(async () => {
|
|
75
|
+
await client.data.read(ch.key, TimeSpan.Zero, TimeSpan.Seconds(4));
|
|
76
|
+
});
|
|
77
|
+
t.true(err instanceof ContiguityError);
|
|
78
|
+
});
|