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
4 changes: 2 additions & 2 deletions server/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import tseslint from 'typescript-eslint';

export default tseslint.config(
{
ignores: ['dist', 'node_modules'],
ignores: ['dist', 'node_modules', 'scripts', 'eslint.config.js'],
},
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the scripts need to be linted too, ts config will change to include them in the project but exclude them from the build

eslint.configs.recommended,
tseslint.configs.strictTypeChecked,
Expand All @@ -22,7 +22,7 @@ export default tseslint.config(
rules: {
'@typescript-eslint/restrict-template-expressions': ['error', { allowNumber: true }],
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': 'error',
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
},
}
);
6 changes: 3 additions & 3 deletions server/src/api-docs/swagger.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import swaggerUi from 'swagger-ui-express';
import swaggerUi, { JsonObject } from 'swagger-ui-express';
import fs from 'fs';
import YAML from 'yaml';
import { RequestHandler } from 'express';
Expand All @@ -9,8 +9,8 @@ import { RequestHandler } from 'express';
* @param {string} documentPath The file path to the Swagger YAML document.
* @returns {Array<RequestHandler>} An array for serving and setting up Swagger UI.
* */
export default (documentPath: string): Array<RequestHandler> => {
export default (documentPath: string): RequestHandler[] => {
const file = fs.readFileSync(documentPath, 'utf8');
const swaggerDocument = YAML.parse(file);
const swaggerDocument = YAML.parse(file) as JsonObject;
return swaggerUi.serve.concat(swaggerUi.setup(swaggerDocument));
};
10 changes: 6 additions & 4 deletions server/src/controllers/AuthenticationController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ export class AuthenticationController implements AuthenticationControllerType {

// 3. Revoke Google OAuth token - we don't use it anymore.
// Do not wait for the response. Continue with login process.
this.googleOAuthService.revokeToken(googleAccessToken);
await this.googleOAuthService.revokeToken(googleAccessToken);
googleAccessToken = '';

if (!oauthUser || !oauthUser.emailVerified) {
if (!oauthUser.emailVerified) {
throw new Error('Could not verify google auth code');
}
} catch (error) {
Expand All @@ -74,7 +74,7 @@ export class AuthenticationController implements AuthenticationControllerType {

// Check if the user is allowed to access to the system
const user = await this.userRepository.findUserByEmail(oauthUser.email);
if (!user || !user.isActive) {
if (!user?.isActive) {
res.status(403).json(new ResponseError('Not allowed to login'));
Sentry.setUser({ email: oauthUser.email });
Sentry.captureMessage(`Attempt to login with unauthorized user`, 'warning');
Expand All @@ -96,11 +96,13 @@ export class AuthenticationController implements AuthenticationControllerType {
res.status(200).json(user);
}

// eslint-disable-next-line @typescript-eslint/require-await
async getSession(req: Request, res: Response): Promise<void> {
const user = res.locals.user as AuthenticatedUser;
res.status(200).json(user);
}

// eslint-disable-next-line @typescript-eslint/require-await
async logout(req: Request, res: Response): Promise<void> {
res.clearCookie(TOKEN_COOKIE_NAME);
res.status(204).end();
Expand All @@ -112,7 +114,7 @@ export class AuthenticationController implements AuthenticationControllerType {
try {
const url = new URL(redirectURI);
return allowedHosts.includes(url.hostname);
} catch (error) {
} catch {
return false;
}
}
Expand Down
1 change: 1 addition & 0 deletions server/src/controllers/DashboardController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export class DashboardController implements DashboardControllerType {
/**
* Retrieve the dashboard data.
*/
// eslint-disable-next-line @typescript-eslint/require-await
async getDashboard(req: Request, res: Response) {
const response = {
demographics: {
Expand Down
30 changes: 12 additions & 18 deletions server/src/controllers/GeographyController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,23 @@ export class GeographyController implements GeographyControllerType {
this.geographyRepository = geographyRepository;
}

async getCountries(req: Request, res: Response, next: NextFunction): Promise<void> {
async getCountries(req: Request, res: Response): Promise<void> {
const query = (req.query.q as string) ?? '';
const limit = Number.parseInt(req.query.limit as string) || null;
try {
const countries = await this.geographyRepository.searchCountry(query, limit);
const jsonResponse = countries.map(({ name, flag }) => {
return { name, flag };
});
res.json(jsonResponse);
} catch (error) {
next(error);
}

const countries = await this.geographyRepository.searchCountry(query, limit);
const jsonResponse = countries.map(({ name, flag }) => {
return { name, flag };
});
res.json(jsonResponse);
}

async getCities(req: Request, res: Response, next: NextFunction): Promise<void> {
async getCities(req: Request, res: Response): Promise<void> {
const query = (req.query.q as string) ?? '';
const limit = Number.parseInt(req.query.limit as string) || null;
try {
const cities = await this.geographyRepository.searchCity(query, limit);
const jsonResponse = cities.map((city) => city.name);
res.json(jsonResponse);
} catch (error) {
next(error);
}

const cities = await this.geographyRepository.searchCity(query, limit);
const jsonResponse = cities.map((city) => city.name);
res.json(jsonResponse);
}
}
12 changes: 4 additions & 8 deletions server/src/controllers/SearchController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,15 @@ export class SearchController implements SearchControllerType {
this.traineesRepository = traineesRepository;
}

async search(req: Request, res: Response, next: NextFunction) {
async search(req: Request, res: Response) {
const maxAllowedLimit = 50;
const inputLimit = Number(req.query.limit) || 20;
const limit = Math.min(inputLimit, maxAllowedLimit);
const searchQuery: string = (req.query.q as string) ?? '';

let results: SearchResult[] = [];
try {
results = await this.getSearchResults(searchQuery, limit);
} catch (error) {
next(error);
return;
}

results = await this.getSearchResults(searchQuery, limit);

const response: SearchResponse = {
hits: {
Expand Down Expand Up @@ -75,7 +71,7 @@ export class SearchController implements SearchControllerType {
.map((trainee) => {
return {
id: trainee.id,
name: `${trainee.displayName}`,
name: trainee.displayName,
thumbnail: trainee.thumbnailURL ?? null,
cohort: trainee.educationInfo.currentCohort ?? null,
profilePath: trainee.profilePath,
Expand Down
73 changes: 27 additions & 46 deletions server/src/controllers/Trainee/EmploymentHistoryController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,27 @@ export interface EmploymentHistoryControllerType {
export class EmploymentHistoryController implements EmploymentHistoryControllerType {
constructor(private readonly traineesRepository: TraineesRepository) {}

async getEmploymentHistory(req: Request, res: Response, next: NextFunction): Promise<void> {
try {
const traineeID = req.params.id;
const employmentHistory = await this.traineesRepository.getEmploymentHistory(traineeID);
res.json(employmentHistory);
} catch (error: any) {
next(error);
}
async getEmploymentHistory(req: Request, res: Response): Promise<void> {
const traineeID = req.params.id;
const employmentHistory = await this.traineesRepository.getEmploymentHistory(traineeID);
res.json(employmentHistory);
}

async addEmploymentHistory(req: Request, res: Response, next: NextFunction): Promise<void> {
async addEmploymentHistory(req: Request, res: Response): Promise<void> {
const traineeID = req.params.id;
const employmentHistoryData: EmploymentHistory = req.body;
try {
validateEmploymentHistory(employmentHistoryData);
} catch (error: any) {
res.status(400).send(new ResponseError(error.message));
} catch (error) {
if (error instanceof Error) res.status(400).send(new ResponseError(error.message));
return;
}

try {
const newEmploymentHistory = await this.traineesRepository.addEmploymentHistory(traineeID, employmentHistoryData);
res.status(201).json(newEmploymentHistory);
} catch (error: any) {
next(error);
}
const newEmploymentHistory = await this.traineesRepository.addEmploymentHistory(traineeID, employmentHistoryData);
res.status(201).json(newEmploymentHistory);
}

async updateEmploymentHistory(req: Request, res: Response, next: NextFunction): Promise<void> {
async updateEmploymentHistory(req: Request, res: Response): Promise<void> {
const trainee = await this.traineesRepository.getTrainee(req.params.id);
if (!trainee) {
res.status(404).send(new ResponseError('Trainee not found'));
Expand Down Expand Up @@ -69,41 +61,30 @@ export class EmploymentHistoryController implements EmploymentHistoryControllerT

try {
validateEmploymentHistory(historyToUpdate);
} catch (error: any) {
res.status(400).send(new ResponseError(error.message));
} catch (error) {
if (error instanceof Error) res.status(400).send(new ResponseError(error.message));
return;
}

try {
const updatedEmploymentHistory = await this.traineesRepository.updateEmploymentHistory(
trainee.id,
historyToUpdate
);
const updatedEmploymentHistory = await this.traineesRepository.updateEmploymentHistory(trainee.id, historyToUpdate);

res.json(updatedEmploymentHistory);
} catch (error: any) {
next(error);
}
res.json(updatedEmploymentHistory);
}

async deleteEmploymentHistory(req: Request, res: Response, next: NextFunction): Promise<void> {
try {
const trainee = await this.traineesRepository.getTrainee(req.params.id);
if (!trainee) {
res.status(404).send(new ResponseError('Trainee not found'));
return;
}

const employmentHistoryID = req.params.employmentHistoryID;
if (!trainee.employmentInfo.employmentHistory.find((history) => history.id === employmentHistoryID)) {
res.status(404).send(new ResponseError('Employment history not found'));
return;
}
async deleteEmploymentHistory(req: Request, res: Response): Promise<void> {
const trainee = await this.traineesRepository.getTrainee(req.params.id);
if (!trainee) {
res.status(404).send(new ResponseError('Trainee not found'));
return;
}

await this.traineesRepository.deleteEmploymentHistory(trainee.id, employmentHistoryID);
res.status(204).send();
} catch (error: any) {
next(error);
const employmentHistoryID = req.params.employmentHistoryID;
if (!trainee.employmentInfo.employmentHistory.find((history) => history.id === employmentHistoryID)) {
res.status(404).send(new ResponseError('Employment history not found'));
return;
}

await this.traineesRepository.deleteEmploymentHistory(trainee.id, employmentHistoryID);
res.status(204).send();
}
}
53 changes: 17 additions & 36 deletions server/src/controllers/Trainee/InteractionController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,18 @@ export class InteractionController implements InteractionControllerType {
private notificationService: NotificationService
) {}

async getInteractions(req: Request, res: Response, next: NextFunction): Promise<void> {
async getInteractions(req: Request, res: Response): Promise<void> {
const trainee = await this.traineesRepository.getTrainee(req.params.id);
if (!trainee) {
res.status(404).send(new ResponseError('Trainee not found'));
return;
}

try {
const interactions = await this.traineesRepository.getInteractions(trainee.id);
res.status(200).json(interactions);
} catch (error: any) {
next(error);
}
const interactions = await this.traineesRepository.getInteractions(trainee.id);
res.status(200).json(interactions);
}

async addInteraction(req: Request, res: Response, next: NextFunction): Promise<void> {
async addInteraction(req: Request, res: Response): Promise<void> {
const trainee = await this.traineesRepository.getTrainee(req.params.id);
if (!trainee) {
res.status(404).send(new ResponseError('Trainee not found'));
Expand All @@ -51,21 +47,17 @@ export class InteractionController implements InteractionControllerType {
if (!reporter) {
throw new Error(`Invalid reporter ID ${reporterID}. User not found.`);
}
} catch (error: any) {
res.status(400).send(new ResponseError(error.message));
} catch (error) {
if (error instanceof Error) res.status(400).send(new ResponseError(error.message));
return;
}

try {
const interaction = await this.traineesRepository.addInteraction(req.params.id, newInteraction);
res.status(201).json(interaction);
this.notificationService.interactionCreated(trainee, interaction);
} catch (error: any) {
next(error);
}
const interaction = await this.traineesRepository.addInteraction(req.params.id, newInteraction);
res.status(201).json(interaction);
await this.notificationService.interactionCreated(trainee, interaction);
}

async updateInteraction(req: Request, res: Response, next: NextFunction): Promise<void> {
async updateInteraction(req: Request, res: Response): Promise<void> {
const trainee = await this.traineesRepository.getTrainee(req.params.id);
if (!trainee) {
res.status(404).send(new ResponseError('Trainee not found'));
Expand All @@ -91,22 +83,16 @@ export class InteractionController implements InteractionControllerType {
// Validate new interaction model after applying the changes
try {
validateInteraction(interactionToUpdate);
} catch (error: any) {
res.status(400).send(new ResponseError(error.message));
} catch (error) {
if (error instanceof Error) res.status(400).send(new ResponseError(error.message));
return;
}

try {
const updatedInteraction = await this.traineesRepository.updateInteraction(req.params.id, interactionToUpdate);
res.status(200).json(updatedInteraction);
} catch (error: any) {
console.error(error);
next(error);
return;
}
const updatedInteraction = await this.traineesRepository.updateInteraction(req.params.id, interactionToUpdate);
res.status(200).json(updatedInteraction);
}

async deleteInteraction(req: Request, res: Response, next: NextFunction): Promise<void> {
async deleteInteraction(req: Request, res: Response): Promise<void> {
const trainee = await this.traineesRepository.getTrainee(req.params.id);
if (!trainee) {
res.status(404).send(new ResponseError('Trainee not found'));
Expand All @@ -118,12 +104,7 @@ export class InteractionController implements InteractionControllerType {
return;
}

try {
await this.traineesRepository.deleteInteraction(req.params.id, req.params.interactionID);
res.status(204).end();
} catch (error: any) {
next(error);
return;
}
await this.traineesRepository.deleteInteraction(req.params.id, req.params.interactionID);
res.status(204).end();
}
}
Loading
Loading