Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/improve error management in core module #8933

Merged
merged 3 commits into from
Dec 7, 2024

Conversation

AMoreaux
Copy link
Contributor

@AMoreaux AMoreaux commented Dec 6, 2024

Summary

This Pull Request introduces a custom validator for checking forbidden words in workspaces and refines how exceptions are handled within the workspace module.

  • Introduced ForbiddenWords custom class validator for validating forbidden words against specific fields in UpdateWorkspaceInput.
  • Added EnvironmentService usage in WorkspaceService to check default subdomains.
  • New file workspaceGraphqlApiExceptionHandler to handle GraphQL API exceptions with specific error mappings.
  • Expanded WorkspaceExceptionCode with SUBDOMAIN_ALREADY_TAKEN.
  • Added new unit tests for validating forbidden words and exception handler behavior.

@AMoreaux AMoreaux requested a review from Weiko December 6, 2024 15:43
@AMoreaux AMoreaux self-assigned this Dec 6, 2024
Add custom class validator to restrict certain subdomains. Integrate the "ForbiddenWords" constraint to prevent illegal subdomain choices like 'demo'. Update workspace service to check default subdomains, enhancing domain management and validation in the application.
@AMoreaux AMoreaux force-pushed the feat/improve-error-management-in-core-module branch from 2ec5542 to 2821bef Compare December 6, 2024 15:44
@AMoreaux AMoreaux marked this pull request as ready for review December 6, 2024 16:21
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

PR Summary

This PR introduces comprehensive error handling improvements in the workspace core module, focusing on consistent exception handling and subdomain validation.

  • Added workspaceGraphqlApiExceptionHandler.ts to map workspace exceptions to appropriate GraphQL errors (NotFound, Conflict, Internal)
  • Implemented new ForbiddenWords validator to prevent reserved words (e.g. 'demo') in workspace subdomains
  • Enhanced UpdateWorkspaceInput with stricter subdomain validation using regex pattern and forbidden words check
  • Added environment-aware validation in WorkspaceService to prevent using default subdomains
  • Standardized error handling in WorkspaceResolver by wrapping mutations with the new exception handler

8 file(s) reviewed, 8 comment(s)
Edit PR Review Bot Settings | Greptile

Comment on lines 17 to +18
@Matches(/^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$/)
@ForbiddenWords(['demo'])
Copy link
Contributor

Choose a reason for hiding this comment

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

style: regex allows 3-30 char subdomains, but error message from ForbiddenWords doesn't mention length restrictions

Comment on lines +20 to +22
expect(() => workspaceGraphqlApiExceptionHandler(error)).toThrow(
NotFoundError,
);
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Consider testing the error message is preserved by checking error.message matches 'Subdomain not found'

Comment on lines +95 to +102
try {
return this.workspaceService.updateWorkspaceById({
...data,
id: workspace.id,
});
} catch (error) {
workspaceGraphqlApiExceptionHandler(error);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: missing return statement after error handler - could cause undefined to be returned instead of throwing

import { ForbiddenWords } from 'src/engine/utils/custom-class-validator/ForbiddenWords';

describe('ForbiddenWordsConstraint', () => {
test('should throw error when word is forbidden', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: test case missing validation for case sensitivity - 'FORBIDDEN' or 'Forbidden' could potentially pass validation

Comment on lines +24 to +37
test('should pass validation word is not in the list', async () => {
class Test {
@ForbiddenWords(['forbidden', 'restricted'])
subdomain: string;
}

const instance = new Test();

instance.subdomain = 'valid';

const errors = await validate(instance);

expect(errors.length).toEqual(0);
});
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: test case missing validation for partial matches - 'forbidden-word' or 'restricted_name' could contain forbidden words


@ValidatorConstraint({ async: false })
export class ForbiddenWordsConstraint implements ValidatorConstraintInterface {
private forbiddenWords: Set<string>;
Copy link
Contributor

Choose a reason for hiding this comment

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

style: forbiddenWords Set is recreated on every validation call instead of being initialized once in constructor


constructor() {}

validate(value: string, validationArguments: ValidationArguments) {
Copy link
Contributor

Choose a reason for hiding this comment

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

style: value parameter should be typed as unknown since input type cannot be guaranteed

Comment on lines +21 to +23
defaultMessage() {
return `${Array.from(this.forbiddenWords).join(', ')} are not allowed`;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: error message includes all forbidden words rather than just the specific word that failed validation - could expose sensitive information

@@ -13,6 +15,7 @@ export class UpdateWorkspaceInput {
@IsString()
@IsOptional()
@Matches(/^[a-z0-9][a-z0-9-]{1,28}[a-z0-9]$/)
@ForbiddenWords(['demo'])
Copy link
Member

Choose a reason for hiding this comment

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

LGTM but I feel this validation should happen at the model level not in the dto which is the graphql input (we could actually justify that they happen in both points)

if (!subdomainAvailable) {
throw new ConflictError('Subdomain already taken');
if (
!subdomainAvailable ||
Copy link
Member

Choose a reason for hiding this comment

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

here we are adding the check updateWorkspaceById method which is closer to the model layer. This is fine as long as we say that we will always use this method to update a workspace and not use the repository directly.
I feel our model layer is typeorm and an approach like this https://orkhan.gitbook.io/typeorm/docs/validation would be more robust

Copy link
Member

@charlesBochet charlesBochet left a comment

Choose a reason for hiding this comment

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

LGTM, thank you!

@charlesBochet charlesBochet merged commit 2524d64 into main Dec 7, 2024
19 checks passed
@charlesBochet charlesBochet deleted the feat/improve-error-management-in-core-module branch December 7, 2024 15:48
Copy link

github-actions bot commented Dec 7, 2024

Thanks @AMoreaux for your contribution!
This marks your 25th PR on the repo. You're top 2% of all our contributors 🎉
See contributor page - Share on LinkedIn - Share on Twitter

Contributions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants