Skip to content

Commit

Permalink
Merge pull request #52 from nikele2001/Branch-Input-Validation
Browse files Browse the repository at this point in the history
Add input validation
  • Loading branch information
sopa301 authored Dec 17, 2023
2 parents 66ea124 + 330d4da commit 88b7d56
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 127 deletions.
90 changes: 19 additions & 71 deletions src/commands/addProjectScene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,17 @@ import { Scenes } from 'telegraf';
import { createProject } from '../db/functions';

import {
UnknownError,
InvalidTextError,
InvalidInputTypeError,
} from '../exceptions';
validateProjectDescription,
validateProjectName,
getProjectMembersFromString,
} from '../util/userInput';

import { getTextFromTextMessages } from '../util/userInput';

import { BotContext } from '../BotContext';

const debug = createDebug('bot:add_project_command');

/**
* Creates a new project in the database.
* @returns A scene with middleware functions that handles the creation of a project.
*/
const invalidTextName = (text: string) =>
text.length < 3 || text.startsWith('/');

const askForProjectName = async (ctx: BotContext) => {
debug(`Entering addProject scene.`);
await ctx.reply(
Expand All @@ -32,30 +27,14 @@ const askForProjectName = async (ctx: BotContext) => {
};

const handleProjectName = async (ctx: BotContext) => {
debug(`Asking for project name.`);
try {
if (!ctx.message || !ctx.from) {
throw new UnknownError(
'An unknown error occurred. Please try again later.',
);
}

if (!('text' in ctx.message)) {
throw new InvalidInputTypeError(
'Invalid input type. Please enter a text message.',
);
}

const text = ctx.message.text;
if (!text || invalidTextName(text)) {
throw new InvalidTextError(
'Please enter a valid project name. A project name needs to be at least 3 characters long and cannot start with /.',
);
}
const text = getTextFromTextMessages(ctx);
validateProjectName(text);

// Save the project name and ask for the next piece of information
ctx.scene.session.project = Project.createBlankProject(
text,
ctx.from.id,
ctx.from?.id as number,
);
await ctx.reply(
`Project name saved. Please enter a short description for your project.`,
Expand All @@ -65,30 +44,15 @@ const handleProjectName = async (ctx: BotContext) => {
const errorMessage = (error as Error).message;
debug(errorMessage);
await ctx.reply(errorMessage);
return ctx.scene.reenter();
return ctx.wizard.selectStep(1);
}
};

const askForProjectDescription = async (ctx: BotContext) => {
debug(`Asking for project description.`);
try {
if (!ctx.message) {
throw new UnknownError(
'An unknown error occurred. Please try again later.',
);
}
if (!('text' in ctx.message)) {
throw new InvalidInputTypeError(
'Invalid input type. Please enter a text message.',
);
}
const text = ctx.message.text;
if (!text) {
throw new InvalidTextError(
'Please enter a valid project description.',
);
}
// Save the project description and ask for the next piece of information
debug(`Valid project description: ${text}`);
const text = getTextFromTextMessages(ctx);
validateProjectDescription(text);
ctx.scene.session.project.setDescription(text);
await ctx.reply(
`Project description saved. Please enter the project members' names, delimited by commas and no spaces.`,
Expand All @@ -98,32 +62,16 @@ const askForProjectDescription = async (ctx: BotContext) => {
const errorMessage = (error as Error).message;
debug(errorMessage);
await ctx.reply(errorMessage);
return ctx.scene.reenter();
return ctx.wizard.selectStep(2);
}
};

const askForProjectMembers = async (ctx: BotContext) => {
debug("Asking for project members' names.");
try {
if (!ctx.message) {
throw new UnknownError(
'An unknown error occurred. Please try again later.',
);
}
if (!('text' in ctx.message)) {
throw new InvalidInputTypeError(
'Invalid input type. Please enter a text message.',
);
}
const text = ctx.message.text;
if (!text) {
throw new InvalidTextError(
'Please enter a valid string representing group members, delimited by commas and no spaces.',
);
}
// Save the project description and ask for the next piece of information
debug(`Valid project members' inputs: ${text}`);
const text = getTextFromTextMessages(ctx);
const project = ctx.scene.session.project;
const personArr = text.split(',');
const personArr = getProjectMembersFromString(text);
project.setPersons(personArr);
createProject(project);
await ctx.reply(`Project members saved. Exiting scene now.`);
Expand All @@ -132,7 +80,7 @@ const askForProjectMembers = async (ctx: BotContext) => {
const errorMessage = (error as Error).message;
debug(errorMessage);
await ctx.reply(errorMessage);
return ctx.scene.reenter();
return ctx.wizard.selectStep(3);
}
};

Expand Down
99 changes: 67 additions & 32 deletions src/commands/viewMainMenuScene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,85 @@ import createDebug from 'debug';

import { Scenes, Markup } from 'telegraf';

import { UnknownError, InvalidInputTypeError } from '../exceptions';
import {
InvalidInputTypeError,
InvalidTextError,
UnknownError,
} from '../exceptions';

import { BotContext } from '../BotContext';

/**
* Debug module for 'bot:generate_existing_projects_command'.
*/
const debug = createDebug('bot:generate_existing_projects_command');

/**
* Map of menu options to scene names.
*/
const menuOptionsToScenesMap = new Map<string, string>([
['Create New Project', 'addProject'],
['View Existing Project(s)', 'existingProjects'],
]);

/**
* Array of menu options.
*/
const menuOptions = Array.from(menuOptionsToScenesMap.keys());

/**
* Handles the selection of a main menu option.
* @param ctx - The bot context.
* @throws If ctx.message is undefined, it throws an unknownError.
* @throws If ctx.message does not contain a 'text' property, it throws an invalidInputTypeError.
*/
const handleMainMenuSelection = async (ctx: BotContext) => {
if (!ctx.message) {
throw new UnknownError(
'An unknown error occurred. Please try again later.',
);
}

if (!('text' in ctx.message)) {
throw new InvalidInputTypeError(
'Invalid input type. Please enter a text message.',
);
}

const sceneName = menuOptionsToScenesMap.get(ctx.message?.text);
if (sceneName) {
debug(`User selected "${ctx.message?.text}"`);
return ctx.scene.enter(sceneName, Markup.removeKeyboard());
}

throw new InvalidTextError(
'Invalid option. Please select a valid option from the keyboard.',
);
};

/**
* Asks the user to select a main menu option.
* @param ctx - The bot context.
*/
const askForMainMenuOption = async (ctx: BotContext) => {
debug(`Entering Main Menu scene.`);
await ctx.reply(
`Please select what you want to do.`,
Markup.keyboard([
['Create New Project', 'View Existing Project(s)'], // Each array represents a row of buttons
]).resize(),
Markup.keyboard([menuOptions]).resize(),
);
return ctx.wizard.next();
};

/**
* Handles the selection of a main menu option.
* @param ctx - The bot context.
* @throws If ctx.message is undefined, it throws an UnknownError.
* @throws If ctx.message does not contain a 'text' property, it throws an InvalidInputTypeError.
* @throws If ctx.message.text is not a valid option, it throws an InvalidTextError.
*/
const handleMainMenuOption = async (ctx: BotContext) => {
try {
if (!ctx.message) {
throw new UnknownError(
'An unknown error occurred. Please try again later.',
);
}

if (!('text' in ctx.message)) {
throw new InvalidInputTypeError(
'Invalid input type. Please enter a text message.',
);
}
// const text = ctx.message.text;
if (ctx.message?.text === 'Create New Project') {
debug('User selected "Create New Project"');
// Handle 'Create New Project' option
// ...
return ctx.scene.enter('addProject', Markup.removeKeyboard());
} else if (ctx.message?.text === 'View Existing Project(s)') {
debug('User selected "View Existing Project(s)"');
// Handle 'View Existing Project(s)' option
// ...
return ctx.scene.enter('existingProjects', Markup.removeKeyboard());
} else {
await ctx.reply(
'Invalid option. Please select a valid option from the keyboard.',
);
return ctx.wizard.back();
}
await handleMainMenuSelection(ctx);
} catch (error) {
const errorMessage = (error as Error).message;
debug(errorMessage);
Expand All @@ -57,6 +89,9 @@ const handleMainMenuOption = async (ctx: BotContext) => {
}
};

/**
* The main menu scene.
*/
const viewMainMenuScene = new Scenes.WizardScene<BotContext>(
'mainMenu',
askForMainMenuOption,
Expand Down
22 changes: 1 addition & 21 deletions src/models/TestAlgorithmUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class AlgorithmTester {
* @returns a project with random weights
*/
public static generateProjectWithRandomWeights(n: number): Project {
return new Project(
return Project.createProject(
'projid1',
1,
'projname',
Expand All @@ -42,26 +42,6 @@ class AlgorithmTester {
.map((_, i) => 'name ' + i.toString()),
);
}

/**
* Generate a project with no weights
* @param n - Size of the project
* @returns a project with no weights
*/
public static generateProjectWithNoWeights(n: number): Project {
return new Project(
'projid1',
1,
'projname',
'projdesc',
Array(n)
.fill(null)
.map(() => Array(n).fill(0)),
Array(n)
.fill(null)
.map((_, i) => 'name ' + i.toString()),
);
}
}

export { AlgorithmTester };
3 changes: 2 additions & 1 deletion src/models/sampleTest.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { AlgorithmRunner } from './AlgorithmRunner';
import { AlgorithmTester } from './TestAlgorithmUtil';
import { Project } from './Project';

// Random project with 27 nodes and 10 groups and random weights
const project = AlgorithmTester.generateProjectWithRandomWeights(27);
const logic = new AlgorithmRunner(project, 10); // 3 is the number of groups desired
console.log(logic.prettyPrintGroupings());

// Project with 27 nodes and 10 groups and 0 weights
const project2 = AlgorithmTester.generateProjectWithNoWeights(27);
const project2 = Project.createBlankProject('proj name', 27);
const logic2 = new AlgorithmRunner(project2, 10); // 3 is the number of groups desired
console.log(logic2.prettyPrintGroupings());
Loading

0 comments on commit 88b7d56

Please sign in to comment.