@stream-io/video-client 0.1.5 → 0.1.6

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.
@@ -406,7 +406,7 @@ export declare class Call {
406
406
  * @param request
407
407
  * @returns
408
408
  */
409
- queryMembers: (request: Omit<QueryMembersRequest, 'type' | 'id'>) => Promise<QueryMembersResponse>;
409
+ queryMembers: (request?: Omit<QueryMembersRequest, 'type' | 'id'>) => Promise<QueryMembersResponse>;
410
410
  /**
411
411
  * Will update the call members.
412
412
  *
@@ -82,7 +82,7 @@ export declare class StreamVideoClient {
82
82
  *
83
83
  * @param data the query data.
84
84
  */
85
- queryCalls: (data: QueryCallsRequest) => Promise<{
85
+ queryCalls: (data?: QueryCallsRequest) => Promise<{
86
86
  calls: Call[];
87
87
  duration: string;
88
88
  next?: string | undefined;
@@ -0,0 +1 @@
1
+ import 'dotenv/config';
@@ -0,0 +1 @@
1
+ import 'dotenv/config';
@@ -0,0 +1 @@
1
+ import 'dotenv/config';
@@ -0,0 +1 @@
1
+ import 'dotenv/config';
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "0.1.5";
1
+ export declare const version = "0.1.6";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream-io/video-client",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "packageManager": "yarn@3.2.4",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.es.js",
package/src/Call.ts CHANGED
@@ -1517,11 +1517,11 @@ export class Call {
1517
1517
  * @param request
1518
1518
  * @returns
1519
1519
  */
1520
- queryMembers = (request: Omit<QueryMembersRequest, 'type' | 'id'>) => {
1520
+ queryMembers = (request?: Omit<QueryMembersRequest, 'type' | 'id'>) => {
1521
1521
  return this.streamClient.post<QueryMembersResponse, QueryMembersRequest>(
1522
1522
  '/call/members',
1523
1523
  {
1524
- ...request,
1524
+ ...(request || {}),
1525
1525
  id: this.id,
1526
1526
  type: this.type,
1527
1527
  },
@@ -340,7 +340,7 @@ export class StreamVideoClient {
340
340
  *
341
341
  * @param data the query data.
342
342
  */
343
- queryCalls = async (data: QueryCallsRequest) => {
343
+ queryCalls = async (data: QueryCallsRequest = {}) => {
344
344
  const response = await this.streamClient.post<
345
345
  QueryCallsResponse,
346
346
  QueryCallsRequest
@@ -2,20 +2,20 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
2
  import { StreamVideoClient } from '../StreamVideoClient';
3
3
  import 'dotenv/config';
4
4
  import { generateUUIDv4 } from '../coordinator/connection/utils';
5
- import { WSConnectionFallback } from '../coordinator/connection/connection_fallback';
5
+ import { StreamVideoServerClient } from '../StreamVideoServerClient';
6
6
 
7
7
  const apiKey = process.env.STREAM_API_KEY!;
8
- const tokenUrl = process.env.TOKEN_PROVIDER_URL!;
8
+ const secret = process.env.STREAM_SECRET!;
9
9
 
10
10
  const tokenProvider = (userId: string) => {
11
+ const serverClient = new StreamVideoServerClient(apiKey, { secret });
11
12
  return async () => {
12
- const url = new URL(tokenUrl);
13
- url.searchParams.set('api_key', apiKey);
14
- url.searchParams.set('user_id', userId);
15
-
16
- const response = await fetch(url.toString());
17
- const data = await response.json();
18
- return data.token;
13
+ return new Promise<string>((resolve) => {
14
+ setTimeout(() => {
15
+ const token = serverClient.createToken(userId);
16
+ resolve(token);
17
+ }, 100);
18
+ });
19
19
  };
20
20
  };
21
21
 
@@ -0,0 +1,93 @@
1
+ import 'dotenv/config';
2
+ import { beforeAll, describe, expect, it, vi } from 'vitest';
3
+ import { StreamVideoServerClient } from '../../StreamVideoServerClient';
4
+ import { generateUUIDv4 } from '../../coordinator/connection/utils';
5
+ import { Call } from '../../Call';
6
+
7
+ const apiKey = process.env.STREAM_API_KEY!;
8
+ const secret = process.env.STREAM_SECRET!;
9
+
10
+ describe('call members API', () => {
11
+ let client: StreamVideoServerClient;
12
+ const callId = `call${generateUUIDv4()}`;
13
+ let call: Call;
14
+
15
+ beforeAll(() => {
16
+ client = new StreamVideoServerClient(apiKey, {
17
+ secret,
18
+ logLevel: 'error',
19
+ });
20
+
21
+ call = client.call('default', callId);
22
+ });
23
+
24
+ it('create with members', async () => {
25
+ const response = await call.getOrCreate({
26
+ data: {
27
+ created_by_id: 'john',
28
+ members: [{ user_id: 'john', role: 'admin' }, { user_id: 'jack' }],
29
+ },
30
+ });
31
+
32
+ expect(response.members[0].user_id).toBe('jack');
33
+ expect(response.members[1].user_id).toBe('john');
34
+ expect(response.members[1].role).toBe('admin');
35
+ });
36
+
37
+ it('add or update members', async () => {
38
+ const response = await call.updateCallMembers({
39
+ update_members: [
40
+ { user_id: 'sara' },
41
+ { user_id: 'jane', role: 'admin' },
42
+ { user_id: 'john', role: 'user' },
43
+ ],
44
+ });
45
+
46
+ expect(response.members[0].user_id).toBe('sara');
47
+ expect(response.members[1].user_id).toBe('jane');
48
+ expect(response.members[1].role).toBe('admin');
49
+ expect(response.members[2].user_id).toBe('john');
50
+ expect(response.members[2].role).toBe('user');
51
+ });
52
+
53
+ it('remove members', async () => {
54
+ const response = await call.updateCallMembers({
55
+ remove_members: ['sara'],
56
+ });
57
+
58
+ expect(response.duration).toBeDefined();
59
+ });
60
+
61
+ it('query members', async () => {
62
+ let response = await call.queryMembers();
63
+
64
+ let members = response.members;
65
+ expect(members.length).toBe(3);
66
+
67
+ const queryMembersReq = {
68
+ sort: [{ field: 'user_id', direction: 1 }],
69
+ limit: 2,
70
+ };
71
+ response = await call.queryMembers(queryMembersReq);
72
+
73
+ members = response.members;
74
+ expect(members.length).toBe(2);
75
+ expect(members[0].user_id).toBe('jack');
76
+ expect(members[1].user_id).toBe('jane');
77
+
78
+ response = await call.queryMembers({
79
+ ...queryMembersReq,
80
+ next: response.next,
81
+ });
82
+
83
+ expect(response.members.length).toBe(1);
84
+
85
+ response = await call.queryMembers({
86
+ filter_conditions: { role: { $eq: 'admin' } },
87
+ });
88
+ members = response.members;
89
+
90
+ expect(members.length).toBe(1);
91
+ members.forEach((m) => expect(m.role).toBe('admin'));
92
+ });
93
+ });
@@ -0,0 +1,78 @@
1
+ import 'dotenv/config';
2
+ import { beforeAll, describe, expect, it } from 'vitest';
3
+ import { StreamVideoServerClient } from '../../StreamVideoServerClient';
4
+ import { generateUUIDv4 } from '../../coordinator/connection/utils';
5
+ import { LogLevel } from '../../coordinator/connection/types';
6
+ import { OwnCapability, RecordSettingsModeEnum } from '../../gen/coordinator';
7
+
8
+ const apiKey = process.env.STREAM_API_KEY!;
9
+ const secret = process.env.STREAM_SECRET!;
10
+
11
+ describe('call types CRUD API', () => {
12
+ let client: StreamVideoServerClient;
13
+ const callTypeName = `calltype${generateUUIDv4()}`;
14
+
15
+ beforeAll(() => {
16
+ client = new StreamVideoServerClient(apiKey, {
17
+ secret,
18
+ logLevel: 'trace',
19
+ logger: (logLevel: LogLevel, message: string, ...args: unknown[]) => {
20
+ console.log(new Date().toISOString(), message, ...args);
21
+ },
22
+ });
23
+ });
24
+
25
+ it('create', async () => {
26
+ const createResponse = await client.createCallType({
27
+ name: callTypeName,
28
+ settings: {
29
+ audio: { mic_default_on: true, default_device: 'speaker' },
30
+ },
31
+ grants: {
32
+ admin: [
33
+ OwnCapability.SEND_AUDIO,
34
+ OwnCapability.SEND_VIDEO,
35
+ OwnCapability.MUTE_USERS,
36
+ ],
37
+ user: [OwnCapability.SEND_AUDIO, OwnCapability.SEND_VIDEO],
38
+ },
39
+ });
40
+
41
+ expect(createResponse.name).toBe(callTypeName);
42
+ expect(createResponse.settings.audio.mic_default_on).toBe(true);
43
+ expect(createResponse.settings.audio.default_device).toBe('speaker');
44
+ expect(createResponse.grants.admin).toBeDefined();
45
+ expect(createResponse.grants.user).toBeDefined();
46
+ });
47
+
48
+ it('read', async () => {
49
+ const readResponse = await client.getCallTypes();
50
+
51
+ expect(readResponse.call_types[callTypeName]).toContain({
52
+ name: callTypeName,
53
+ });
54
+ });
55
+
56
+ it('update', async () => {
57
+ const updateResponse = await client.updateCallType(callTypeName, {
58
+ settings: {
59
+ audio: { mic_default_on: false, default_device: 'earpiece' },
60
+ recording: {
61
+ mode: RecordSettingsModeEnum.DISABLED,
62
+ },
63
+ },
64
+ });
65
+
66
+ expect(updateResponse.settings.audio.mic_default_on).toBeFalsy();
67
+ expect(updateResponse.settings.audio.default_device).toBe('earpiece');
68
+ expect(updateResponse.settings.recording.mode).toBe(
69
+ RecordSettingsModeEnum.DISABLED,
70
+ );
71
+ });
72
+
73
+ it('delete', async () => {
74
+ await client.deleteCallType(callTypeName);
75
+
76
+ await expect(() => client.getCallType(callTypeName)).rejects.toThrowError();
77
+ });
78
+ });
@@ -0,0 +1,147 @@
1
+ import 'dotenv/config';
2
+ import { beforeAll, describe, expect, it, vi } from 'vitest';
3
+ import { StreamVideoServerClient } from '../../StreamVideoServerClient';
4
+ import { generateUUIDv4 } from '../../coordinator/connection/utils';
5
+ import { Call } from '../../Call';
6
+ import {
7
+ RecordSettingsRequestModeEnum,
8
+ RecordSettingsRequestQualityEnum,
9
+ } from '../../gen/coordinator';
10
+
11
+ const apiKey = process.env.STREAM_API_KEY!;
12
+ const secret = process.env.STREAM_SECRET!;
13
+
14
+ describe('call API', () => {
15
+ let client: StreamVideoServerClient;
16
+ const callId = `call${generateUUIDv4()}`;
17
+ let call: Call;
18
+
19
+ beforeAll(() => {
20
+ client = new StreamVideoServerClient(apiKey, {
21
+ secret,
22
+ logLevel: 'error',
23
+ });
24
+
25
+ call = client.call('default', callId);
26
+ });
27
+
28
+ it('create', async () => {
29
+ const response = await call.getOrCreate({
30
+ data: { created_by_id: 'john' },
31
+ });
32
+
33
+ expect(response.call.created_by.id).toBe('john');
34
+ });
35
+
36
+ it('update', async () => {
37
+ const response = await call.update({
38
+ settings_override: {
39
+ audio: { mic_default_on: true, default_device: 'speaker' },
40
+ },
41
+ custom: { color: 'red' },
42
+ });
43
+
44
+ expect(response.call.settings.audio.mic_default_on).toBe(true);
45
+ expect(response.call.custom.color).toBe('red');
46
+ });
47
+
48
+ it('RTMP address', async () => {
49
+ const resp = await call.getOrCreate();
50
+ const address = resp.call.ingress.rtmp.address;
51
+
52
+ expect(address).toBeDefined();
53
+ });
54
+
55
+ it('query calls', async () => {
56
+ let response = await client.queryCalls();
57
+
58
+ let calls = response.calls;
59
+ expect(calls.length).toBeGreaterThanOrEqual(1);
60
+
61
+ const queryCallsReq = {
62
+ sort: [{ field: 'starts_at', direction: -1 }],
63
+ limit: 2,
64
+ };
65
+ response = await client.queryCalls(queryCallsReq);
66
+
67
+ calls = response.calls;
68
+ expect(calls.length).toBe(2);
69
+
70
+ response = await client.queryCalls({
71
+ ...queryCallsReq,
72
+ next: response.next,
73
+ });
74
+
75
+ expect(response.calls.length).toBeLessThanOrEqual(2);
76
+
77
+ response = await client.queryCalls({
78
+ filter_conditions: { backstage: { $eq: false } },
79
+ });
80
+
81
+ expect(response.calls.length).toBeGreaterThanOrEqual(1);
82
+ });
83
+
84
+ describe('recording', () => {
85
+ it('enable call recording', async () => {
86
+ let response = await call.update({
87
+ settings_override: {
88
+ recording: {
89
+ mode: RecordSettingsRequestModeEnum.DISABLED,
90
+ },
91
+ },
92
+ });
93
+ let settings = response.call.settings.recording;
94
+
95
+ expect(settings.mode).toBe(RecordSettingsRequestModeEnum.DISABLED);
96
+
97
+ response = await call.update({
98
+ settings_override: {
99
+ recording: {
100
+ mode: RecordSettingsRequestModeEnum.AVAILABLE,
101
+ },
102
+ },
103
+ });
104
+
105
+ settings = response.call.settings.recording;
106
+ expect(settings.mode).toBe(RecordSettingsRequestModeEnum.AVAILABLE);
107
+
108
+ response = await call.update({
109
+ settings_override: {
110
+ recording: {
111
+ audio_only: false,
112
+ quality: RecordSettingsRequestQualityEnum._1080P,
113
+ },
114
+ },
115
+ });
116
+
117
+ settings = response.call.settings.recording;
118
+ expect(settings.audio_only).toBe(false);
119
+ expect(settings.quality).toBe(RecordSettingsRequestQualityEnum._1080P);
120
+ });
121
+
122
+ it('start recording', async () => {
123
+ // somewhat dummy test, we should do a proper test in the future where we join a call and start recording
124
+ await expect(() => call.startRecording()).rejects.toThrowError(
125
+ 'Stream error code 4: StartRecording failed with error: "cannot record inactive call"',
126
+ );
127
+ });
128
+
129
+ it('stop recording', async () => {
130
+ // somewhat dummy test, we should do a proper test in the future
131
+ await expect(() => call.stopRecording()).rejects.toThrowError(
132
+ 'Stream error code 4: StopRecording failed with error: "call is not being recorded"',
133
+ );
134
+ });
135
+
136
+ it('query recordings', async () => {
137
+ // somewhat dummy test, we should do a proper test in the future
138
+ let response = await call.queryRecordings();
139
+
140
+ expect(response.recordings).toBeDefined();
141
+
142
+ response = await call.queryRecordings('session123');
143
+
144
+ expect(response.recordings).toBeDefined();
145
+ });
146
+ });
147
+ });
@@ -0,0 +1,44 @@
1
+ import 'dotenv/config';
2
+ import jwt from 'jsonwebtoken';
3
+ import { beforeAll, describe, expect, it } from 'vitest';
4
+ import { StreamVideoServerClient } from '../../StreamVideoServerClient';
5
+
6
+ const apiKey = process.env.STREAM_API_KEY!;
7
+ const secret = process.env.STREAM_SECRET!;
8
+
9
+ describe('creating tokens', () => {
10
+ let client: StreamVideoServerClient;
11
+ const userId = 'john';
12
+
13
+ beforeAll(() => {
14
+ client = new StreamVideoServerClient(apiKey, {
15
+ secret,
16
+ logLevel: 'error',
17
+ });
18
+ });
19
+
20
+ it('with no expiration', () => {
21
+ const token = client.createToken(userId);
22
+ const decodedToken = jwt.verify(token, secret) as any;
23
+
24
+ expect(decodedToken.user_id).toBe(userId);
25
+ });
26
+
27
+ it('with expiration and issued at provided', () => {
28
+ const exp = Math.round(new Date().getTime() / 1000) + 60 * 60;
29
+ const iat = Math.round(new Date().getTime() / 1000);
30
+ const token = client.createToken(userId, exp, iat);
31
+ const decodedToken = jwt.verify(token, secret) as any;
32
+
33
+ expect(decodedToken.exp).toBe(exp);
34
+ expect(decodedToken.iat).toBe(iat);
35
+ });
36
+
37
+ it('with call IDs provided', () => {
38
+ const call_cids = ['default:call1', 'livestream:call2'];
39
+ const token = client.createToken(userId, undefined, undefined, call_cids);
40
+ const decodedToken = jwt.verify(token, secret) as any;
41
+
42
+ expect(decodedToken.call_cids).toEqual(call_cids);
43
+ });
44
+ });
@@ -0,0 +1,28 @@
1
+ import 'dotenv/config';
2
+ import { beforeEach, describe, expect, it } from 'vitest';
3
+ import { StreamVideoServerClient } from '../../StreamVideoServerClient';
4
+
5
+ const apiKey = process.env.STREAM_API_KEY!;
6
+ const secret = process.env.STREAM_SECRET!;
7
+
8
+ describe('server side user connect', () => {
9
+ let client: StreamVideoServerClient;
10
+
11
+ beforeEach(() => {
12
+ client = new StreamVideoServerClient(apiKey, {
13
+ browser: false,
14
+ secret,
15
+ allowServerSideConnect: true,
16
+ });
17
+ });
18
+
19
+ it('hold up API requests until connect is ready', async () => {
20
+ const userId = 'server-side-test';
21
+ const token = client.createToken(userId);
22
+ client.connectUser({ id: userId }, token);
23
+
24
+ const response = await client.queryCalls({});
25
+
26
+ expect(response).toBeDefined();
27
+ });
28
+ });
@@ -504,9 +504,6 @@ export class StreamClient {
504
504
  response,
505
505
  },
506
506
  );
507
- this.logger('trace', `client:${type} - Response payload`, {
508
- response,
509
- });
510
507
  };
511
508
 
512
509
  _logApiError = (type: string, url: string, error: unknown) => {
@@ -562,9 +559,9 @@ export class StreamClient {
562
559
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
563
560
  } catch (e: any /**TODO: generalize error types */) {
564
561
  e.client_request_id = requestConfig.headers?.['x-client-request-id'];
565
- this._logApiError(type, url, e);
566
562
  this.consecutiveFailures += 1;
567
563
  if (e.response) {
564
+ this._logApiError(type, url, e.response);
568
565
  /** connection_fallback depends on this token expiration logic */
569
566
  if (
570
567
  e.response.data.code === KnownCodes.TOKEN_EXPIRED &&
@@ -578,6 +575,7 @@ export class StreamClient {
578
575
  }
579
576
  return this.handleResponse(e.response);
580
577
  } else {
578
+ this._logApiError(type, url, e);
581
579
  // eslint-disable-next-line no-throw-literal
582
580
  throw e as AxiosError<APIErrorResponse>;
583
581
  }
@@ -1,110 +0,0 @@
1
- import { StreamVideoServerClient } from '../StreamVideoServerClient';
2
- import 'dotenv/config';
3
- import jwt from 'jsonwebtoken';
4
- import { beforeEach, describe, expect, it } from 'vitest';
5
- import { generateUUIDv4 } from '../coordinator/connection/utils';
6
-
7
- const apiKey = process.env.STREAM_API_KEY!;
8
- const secret = process.env.STREAM_SECRET!;
9
-
10
- describe('StreamVideoServerClient - docs snippets', () => {
11
- let client: StreamVideoServerClient;
12
-
13
- beforeEach(() => {
14
- client = new StreamVideoServerClient(apiKey, {
15
- secret,
16
- // turn off
17
- logger: () => {},
18
- });
19
- });
20
-
21
- describe('creating tokens', () => {
22
- const userId = 'john';
23
-
24
- it('with no expiration', () => {
25
- const token = client.createToken(userId);
26
- const decodedToken = jwt.verify(token, secret) as any;
27
-
28
- expect(decodedToken.user_id).toBe(userId);
29
- });
30
-
31
- it('with expiration and issued at provided', () => {
32
- const exp = Math.round(new Date().getTime() / 1000) + 60 * 60;
33
- const iat = Math.round(new Date().getTime() / 1000);
34
- const token = client.createToken(userId, exp, iat);
35
- const decodedToken = jwt.verify(token, secret) as any;
36
-
37
- expect(decodedToken.exp).toBe(exp);
38
- expect(decodedToken.iat).toBe(iat);
39
- });
40
-
41
- it('with call IDs provided', () => {
42
- const call_cids = ['default:call1', 'livestream:call2'];
43
- const token = client.createToken(userId, undefined, undefined, call_cids);
44
- const decodedToken = jwt.verify(token, secret) as any;
45
-
46
- expect(decodedToken.call_cids).toEqual(call_cids);
47
- });
48
- });
49
-
50
- describe('call type CRUD API', () => {
51
- const callTypeName = `calltype${generateUUIDv4()}`;
52
-
53
- it('create', async () => {
54
- const createResponse = await client.createCallType({
55
- name: callTypeName,
56
- });
57
-
58
- expect(createResponse.name).toBe(callTypeName);
59
- });
60
-
61
- it('read', async () => {
62
- const readResponse = await client.getCallTypes();
63
-
64
- expect(readResponse.call_types[callTypeName]).toContain({
65
- name: callTypeName,
66
- });
67
- });
68
-
69
- it('update', async () => {
70
- const updateResponse = await client.updateCallType(callTypeName, {
71
- settings: {
72
- audio: { mic_default_on: false, default_device: 'earpiece' },
73
- },
74
- });
75
-
76
- expect(updateResponse.settings.audio.mic_default_on).toBeFalsy();
77
- expect(updateResponse.settings.audio.default_device).toBe('earpiece');
78
- });
79
-
80
- it('delete', async () => {
81
- await client.deleteCallType(callTypeName);
82
-
83
- await expect(() =>
84
- client.getCallType(callTypeName),
85
- ).rejects.toThrowError();
86
- });
87
- });
88
- });
89
-
90
- describe('StreamVideoServerClient - server side user connect', () => {
91
- let client: StreamVideoServerClient;
92
-
93
- beforeEach(() => {
94
- client = new StreamVideoServerClient(apiKey, {
95
- browser: false,
96
- secret,
97
- allowServerSideConnect: true,
98
- });
99
- });
100
-
101
- it('hold up API requests until connect is ready', async () => {
102
- const userId = 'server-side-test';
103
- const token = client.createToken(userId);
104
- client.connectUser({ id: userId }, token);
105
-
106
- const response = await client.queryCalls({});
107
-
108
- expect(response).toBeDefined();
109
- });
110
- });