Skip to content

Commit

Permalink
fix(*): add createChannel() command (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
garrappachc authored May 17, 2022
1 parent 77bfc59 commit df568d0
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 39 deletions.
15 changes: 14 additions & 1 deletion src/channel.spec.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { ChannelState, PermissionQuery } from '@proto/Mumble';
import { Channel } from './channel';
import { ChannelManager } from './channel-manager';
import { Client } from './client';
import { createChannel } from './commands';
import { MumbleSocket } from './mumble-socket';

jest.mock('./client');
jest.mock('./commands', () => ({
fetchChannelPermissions: jest
.fn()
.mockResolvedValue(PermissionQuery.create({ permissions: 0x1 | 0x40 })),
createChannel: jest.fn().mockResolvedValue(1),
}));

describe('Channel', () => {
Expand All @@ -20,6 +23,12 @@ describe('Channel', () => {
username: 'FAKE_USERNAME',
}) as jest.Mocked<Client>;
client.socket = {} as MumbleSocket;
client.channels = {
byId: jest.fn().mockResolvedValue({}),
byName: jest.fn().mockResolvedValue({}),
byPath: jest.fn().mockResolvedValue({}),
findAll: jest.fn().mockReturnValue([]),
} as unknown as ChannelManager;
});

it('should assign properties', () => {
Expand Down Expand Up @@ -72,7 +81,11 @@ describe('Channel', () => {

it('should attempt to create channel', async () => {
await channel.createSubChannel('SUBCHANNEL_NAME');
expect(client.createChannel).toHaveBeenCalledWith(7, 'SUBCHANNEL_NAME');
expect(createChannel).toHaveBeenCalledWith(
client.socket,
7,
'SUBCHANNEL_NAME',
);
});
});

Expand Down
8 changes: 7 additions & 1 deletion src/channel.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ChannelState, PermissionQuery } from '@proto/Mumble';
import { Client } from './client';
import {
createChannel,
fetchChannelPermissions,
linkChannels,
unlinkChannels,
Expand Down Expand Up @@ -64,12 +65,17 @@ export class Channel {
}

async createSubChannel(name: string): Promise<Channel> {
if (!this.client.socket) {
throw new Error('no socket');
}

const permissions = await this.getPermissions();
if (!permissions.canCreateChannel) {
throw new InsufficientPermissionsError();
}

return await this.client.createChannel(this.id, name);
const newChannelId = await createChannel(this.client.socket, this.id, name);
return this.client.channels.byId(newChannelId) as Channel;
}

async remove() {
Expand Down
37 changes: 0 additions & 37 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
import {
Authenticate,
ChannelRemove,
ChannelState,
PermissionDenied,
Ping,
Reject,
Expand All @@ -26,7 +25,6 @@ import {
import { User } from './user';
import { merge } from 'lodash';
import { ChannelManager } from './channel-manager';
import { Channel } from './channel';
import { UserManager } from './user-manager';
import EventEmitter from 'events';
import { encodeMumbleVersion } from './encode-mumble-version';
Expand Down Expand Up @@ -121,41 +119,6 @@ export class Client extends EventEmitter {
return this;
}

async createChannel(parent: number, name: string): Promise<Channel> {
return new Promise((resolve, reject) => {
if (!this.socket) {
reject(new Error('no socket'));
return;
}

race(
this.socket.packet.pipe(
filterPacket(ChannelState),
filter(
channelState =>
channelState.parent === parent && channelState.name === name,
),
take(1),
),
this.socket.packet.pipe(filterPacket(PermissionDenied), take(1)),
).subscribe(packet => {
if (PermissionDenied.is(packet)) {
const reason = packet.reason;
reject(new Error(`failed to create channel (${reason})`));
} else {
if (packet.channelId) {
const channel = this.channels.byId(packet.channelId);
if (channel) {
resolve(channel);
}
}
}
});

this.socket.send(ChannelState, ChannelState.create({ parent, name }));
});
}

async removeChannel(channelId: number): Promise<void> {
return new Promise((resolve, reject) => {
if (!this.socket) {
Expand Down
36 changes: 36 additions & 0 deletions src/commands/create-channel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { PermissionDeniedError } from '@/errors';
import { MumbleSocket } from '@/mumble-socket';
import { filterPacket } from '@/rxjs-operators/filter-packet';
import { ChannelState, PermissionDenied } from '@proto/Mumble';
import { filter, race, take } from 'rxjs';

export const createChannel = async (
socket: MumbleSocket,
parentChannelId: number,
channelName: string,
): Promise<number> =>
new Promise((resolve, reject) => {
race(
socket.packet.pipe(
filterPacket(ChannelState),
filter(
channelState =>
channelState.parent === parentChannelId &&
channelState.name === channelName,
),
take(1),
),
socket.packet.pipe(filterPacket(PermissionDenied), take(1)),
).subscribe(packet => {
if (PermissionDenied.is(packet)) {
reject(new PermissionDeniedError(packet));
} else {
resolve(packet.channelId as number);
}
});

socket.send(
ChannelState,
ChannelState.create({ parent: parentChannelId, name: channelName }),
);
});
1 change: 1 addition & 0 deletions src/commands/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { createChannel } from './create-channel';
export { fetchChannelPermissions } from './fetch-channel-permissions';
export { linkChannels } from './link-channels';
export { unlinkChannels } from './unlink-channels';
1 change: 1 addition & 0 deletions src/errors/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { ConnectionRejectedError } from './connection-rejected.error';
export { InsufficientPermissionsError } from './insufficient-permissions.error';
export { NoSuchChannelError } from './no-such-channel.error';
export { PermissionDeniedError } from './permission-denied.error';
11 changes: 11 additions & 0 deletions src/errors/permission-denied.error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { PermissionDenied } from '@proto/Mumble';

export class PermissionDeniedError extends Error {
constructor(public readonly permissionDenied: PermissionDenied) {
let reason = 'permission denied';
if (permissionDenied.reason) {
reason = permissionDenied.reason;
}
super(reason);
}
}

0 comments on commit df568d0

Please sign in to comment.