Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 3 additions & 8 deletions backend/eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@ import globals from 'globals';
import baseConfig from '../eslint.config.mjs';

export default [
{
...baseConfig,

files: ['**/*.{ts,tsx,js,jsx}'],
languageOptions: {
globals: globals.node,
},
},
...baseConfig,
{ files: ['**/*.{ts,tsx,js,jsx}'], languageOptions: { globals: globals.node } },
{ files: ['**/*.test.{ts,tsx,js,jsx}'], rules: { '@typescript-eslint/await-thenable': 'off' } },
];
11 changes: 6 additions & 5 deletions backend/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@
},
"start": {
"executor": "nx:run-commands",
"options": {
"cwd": "backend",
"command": "node --import tsx index.ts"
}
"options": { "cwd": "backend", "command": "node --import tsx index.ts" }
},
"debug": {
"executor": "nx:run-commands",
Expand Down Expand Up @@ -48,10 +45,14 @@
"inputs": ["default", "^default", { "externalDependencies": ["jest", "ts-jest"] }]
},
"ts-check": {
"executor": "nx:run-commands",
"options": { "cwd": "backend", "command": "tsc -p tsconfig.json --pretty true --noEmit" }
},
"lint": {
"executor": "nx:run-commands",
"options": {
"cwd": "backend",
"command": "tsc --noEmit -p tsconfig.json --pretty true"
"command": "eslint \"**/*.{ts,tsx,js,jsx}\" --report-unused-disable-directives --max-warnings 0"
}
}
},
Expand Down
44 changes: 9 additions & 35 deletions backend/routes/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,15 @@ import createGetOrCreateSession from './getOrCreateSession';
const sessionRouter = Router();
const videoService = createVideoService();
const sessionService = getSessionStorageService();
const getOrCreateSession = createGetOrCreateSession({
videoService,
sessionService,
});
const getOrCreateSession = createGetOrCreateSession({ videoService, sessionService });

sessionRouter.get('/:room', async (req: Request<{ room: string }>, res: Response) => {
try {
const { room: roomName } = req.params;
const sessionId = await getOrCreateSession(roomName);
const data = videoService.generateToken(sessionId);
const captionsId = await sessionService.getCaptionsId(roomName);
res.json({
sessionId,
token: data.token,
apiKey: data.apiKey,
captionsId,
});
res.json({ sessionId, token: data.token, apiKey: data.apiKey, captionsId });
} catch (error: unknown) {
const message = error instanceof Error ? error.message : error;
res.status(500).send({ message });
Expand All @@ -36,15 +28,12 @@ sessionRouter.post('/:room/startArchive', async (req: Request<{ room: string }>,
const sessionId = await sessionService.getSession(roomName);
if (sessionId) {
const archive = await videoService.startArchive(roomName, sessionId);
res.json({
archiveId: archive.id,
status: 200,
});
res.json({ archiveId: archive.id, status: 200 });
} else {
res.status(404).json({ message: 'Room not found' });
}
} catch (error: unknown) {
console.log(error);
console.error(error);
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
res.status(500).json({ message: errorMessage });
}
Expand All @@ -57,10 +46,7 @@ sessionRouter.post(
const { archiveId } = req.params;
if (archiveId) {
const responseArchiveId = await videoService.stopArchive(archiveId);
res.json({
archiveId: responseArchiveId,
status: 200,
});
res.json({ archiveId: responseArchiveId, status: 200 });
}
} catch (error: unknown) {
res.status(500).send({ message: (error as Error).message ?? error });
Expand All @@ -74,10 +60,7 @@ sessionRouter.get('/:room/archives', async (req: Request<{ room: string }>, res:
const sessionId = await sessionService.getSession(roomName);
if (sessionId) {
const archives = await videoService.listArchives(sessionId);
res.json({
archives,
status: 200,
});
res.json({ archives, status: 200 });
} else {
res.status(404).json({ message: 'Room not found' });
}
Expand Down Expand Up @@ -109,10 +92,7 @@ sessionRouter.post(
} else {
// the captions were already enabled for this room
const captionsId = await sessionService.getCaptionsId(roomName);
res.json({
captionsId,
status: 200,
});
res.json({ captionsId, status: 200 });
}
} catch (error: unknown) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
Expand Down Expand Up @@ -148,15 +128,9 @@ sessionRouter.post(
if (captionsUserCount === 0) {
const disableResponse = await videoService.disableCaptions(captionsId);
await sessionService.setCaptionsId(roomName, '');
res.json({
disableResponse,
status: 200,
});
res.json({ disableResponse, status: 200 });
} else {
res.json({
disableResponse: 'Captions are still active for other users',
status: 200,
});
res.json({ disableResponse: 'Captions are still active for other users', status: 200 });
}
} catch (error: unknown) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
Expand Down
2 changes: 1 addition & 1 deletion backend/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ app.get('/*', (_req: Request, res: Response) => {
const startServer: () => Promise<Server> = () => {
return new Promise((res) => {
const server: Server = app.listen(port, () => {
console.log('Server listening on port', port);
console.info('Server listening on port', port);
res(server);
});
});
Expand Down
33 changes: 8 additions & 25 deletions backend/services/jiraFeedbackService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,29 +33,18 @@ class JiraFeedbackService implements FeedbackService {
async reportIssue(data: FeedbackData): Promise<ReportIssueReturn | null> {
const feedbackIssueData = {
fields: {
project: {
key: this.jiraKey,
},
project: { key: this.jiraKey },
summary: data.title,
description: `Reported by: ${data.name}\n\n Issue description:\n${data.issue}`,
issuetype: {
name: 'Bug',
},
components: [
{
id: this.getComponentIdByOrigin(data.origin),
},
],
issuetype: { name: 'Bug' },
components: [{ id: this.getComponentIdByOrigin(data.origin) }],
[this.jiraEpicLink]: this.jiraEpicUrl,
},
};

try {
const response = await axios.post(this.jiraApiUrl, feedbackIssueData, {
headers: {
'Content-Type': 'application/json',
Authorization: `Basic ${this.jiraToken}`,
},
const response = await axios.post<{ key: string }>(this.jiraApiUrl, feedbackIssueData, {
headers: { 'Content-Type': 'application/json', Authorization: `Basic ${this.jiraToken}` },
});

const ticketData: ReportIssueReturn = {
Expand All @@ -68,7 +57,7 @@ class JiraFeedbackService implements FeedbackService {
}
return await this.addAttachment(data.attachment, ticketData, response.data.key);
} catch (error) {
console.log('Response Error:', error);
console.error('Response Error:', error);
return null;
}
}
Expand All @@ -80,10 +69,7 @@ class JiraFeedbackService implements FeedbackService {
): Promise<ReportIssueReturn> {
const fileBuffer = Buffer.from(attachment, 'base64');
const formData = new FormData();
formData.append('file', fileBuffer, {
filename: 'screenshot.png',
contentType: 'image/png',
});
formData.append('file', fileBuffer, { filename: 'screenshot.png', contentType: 'image/png' });

await axios.post(`${this.jiraApiUrl}${key}/attachments`, formData, {
headers: {
Expand All @@ -94,10 +80,7 @@ class JiraFeedbackService implements FeedbackService {
},
});

return {
...ticketData,
screenshotIncluded: true,
};
return { ...ticketData, screenshotIncluded: true };
}

private getComponentIdByOrigin(origin: FeedbackOrigin): string {
Expand Down
8 changes: 3 additions & 5 deletions backend/storage/inMemorySessionStorage.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// [TODO]: Fix require-await linting issue

Check warning on line 1 in backend/storage/inMemorySessionStorage.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Complete the task associated to this "TODO" comment.

See more on https://sonarcloud.io/project/issues?id=Vonage_vonage-video-react-app&issues=AZrFa4S-2v82-KtypAFJ&open=AZrFa4S-2v82-KtypAFJ&pullRequest=283
/* eslint-disable @typescript-eslint/require-await */
import { SessionStorage } from './sessionStorage';

interface SessionData {
Expand All @@ -14,11 +16,7 @@
}

async setSession(roomName: string, sessionId: string): Promise<void> {
this.sessions[roomName] = {
sessionId,
captionsId: null,
captionsUserCount: 0,
};
this.sessions[roomName] = { sessionId, captionsId: null, captionsUserCount: 0 };
}

async setCaptionsId(roomName: string, captionsId: string): Promise<void> {
Expand Down
1 change: 0 additions & 1 deletion backend/storage/tests/inMemorySessionStorage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ describe('InMemorySessionStorage', () => {
await storage.setSession(room, 'session123');
await storage.incrementCaptionsUserCount(room);
for (let i = 0; i < 5; i++) {
// eslint-disable-next-line no-await-in-loop
const count = await storage.decrementCaptionsUserCount(room);
expect(count).toBe(0);
}
Expand Down
4 changes: 2 additions & 2 deletions backend/storage/vcrSessionStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class VcrSessionStorage implements SessionStorage {

async incrementCaptionsUserCount(roomName: string): Promise<number> {
const key = `captionsUserCount:${roomName}`;
const currentCaptionsUsersCount = (await this.dbState.get(key)) as number;
const currentCaptionsUsersCount = await this.dbState.get<number>(key);
const newCaptionsUsersCount = currentCaptionsUsersCount ? currentCaptionsUsersCount + 1 : 1;
await this.dbState.set(key, newCaptionsUsersCount);
await this.setKeyExpiry(key);
Expand All @@ -56,7 +56,7 @@ class VcrSessionStorage implements SessionStorage {

async decrementCaptionsUserCount(roomName: string): Promise<number> {
const key = `captionsUserCount:${roomName}`;
const currentCaptionsUsersCount = (await this.dbState.get(key)) as number;
const currentCaptionsUsersCount = await this.dbState.get<number>(key);
const newCaptionsUsersCount = currentCaptionsUsersCount ? currentCaptionsUsersCount - 1 : 0;
if (newCaptionsUsersCount < 0) {
await this.dbState.delete(key);
Expand Down
13 changes: 5 additions & 8 deletions backend/tests/apple-app-site-association.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ describe('GET /.well-known/apple-app-site-association', () => {

it('returns valid JSON content', async () => {
const res = await request(server).get('/.well-known/apple-app-site-association');
expect(() => JSON.parse(res.text)).not.toThrow();
expect(() => {
JSON.parse(res.text);
}).not.toThrow();
});

it('returns the correct structure for asset links', async () => {
Expand All @@ -42,13 +44,8 @@ describe('GET /.well-known/apple-app-site-association', () => {
expect.objectContaining({
appIDs: expect.arrayContaining(['PR6C39UQ38.com.vonage.VERA']),
components: expect.arrayContaining([
expect.objectContaining({
'/': '/waiting-room/*',
}),
expect.objectContaining({
'/': '/room/*',
comment: 'Matches any room URL',
}),
expect.objectContaining({ '/': '/waiting-room/*' }),
expect.objectContaining({ '/': '/room/*', comment: 'Matches any room URL' }),
]),
}),
]),
Expand Down
4 changes: 3 additions & 1 deletion backend/tests/assetlinks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ describe('GET /.well-known/assetlinks.json', () => {

it('returns valid JSON content', async () => {
const res = await request(server).get('/.well-known/assetlinks.json');
expect(() => JSON.parse(res.text)).not.toThrow();
expect(() => {
JSON.parse(res.text);
}).not.toThrow();
});

it('returns the correct structure for asset links', async () => {
Expand Down
1 change: 0 additions & 1 deletion backend/types/opentok-jwt-d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
declare module 'opentok-jwt' {
// eslint-disable-next-line import/prefer-default-export
export function projectToken(apiKey: string, apiSecret: string, expire?: number): string;
}
39 changes: 10 additions & 29 deletions backend/videoService/tests/opentokVideoService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,16 @@ await jest.unstable_mockModule('opentok', () => ({
callback(null, { sessionId: mockSessionId });
}
),
generateToken: jest.fn<() => { token: string; apiKey: string }>().mockReturnValue({
token: mockToken,
apiKey: mockApiKey,
}),
generateToken: jest
.fn<() => { token: string; apiKey: string }>()
.mockReturnValue({ token: mockToken, apiKey: mockApiKey }),
startArchive: jest.fn(
(
_sessionId: string,
_options: unknown,
callback: (
err: unknown,
session: {
archive: {
id: string;
};
}
) => void
callback: (err: unknown, session: { archive: { id: string } }) => void
) => {
callback(null, {
archive: {
id: mockArchiveId,
},
});
callback(null, { archive: { id: mockArchiveId } });
}
),
stopArchive: jest.fn(
Expand All @@ -55,9 +43,9 @@ await jest.unstable_mockModule('opentok', () => ({

await jest.unstable_mockModule('axios', () => ({
default: {
post: jest.fn<() => Promise<{ data: { captionsId: string } }>>().mockResolvedValue({
data: { captionsId: mockCaptionId },
}),
post: jest
.fn<() => Promise<{ data: { captionsId: string } }>>()
.mockResolvedValue({ data: { captionsId: mockCaptionId } }),
},
}));

Expand All @@ -81,19 +69,12 @@ describe('OpentokVideoService', () => {

it('generates a token', () => {
const result = opentokVideoService.generateToken(mockSessionId);
expect(result.token).toEqual({
apiKey: mockApiKey,
token: mockToken,
});
expect(result.token).toEqual({ apiKey: mockApiKey, token: mockToken });
});

it('starts an archive', async () => {
const response = await opentokVideoService.startArchive(mockRoomName, mockSessionId);
expect(response).toMatchObject({
archive: {
id: mockArchiveId,
},
});
expect(response).toMatchObject({ archive: { id: mockArchiveId } });
});

it('stops an archive', async () => {
Expand Down
Loading
Loading