This section guides you through the steps required to set up your computer for developing CATcher.
Prerequisites:
- Node.js 14 -- (run
node -v
in your OS terminal to check the version of Node on your computer)
Recommended:
- IDE: Visual Studio Code
Steps:
- Fork this repository into your Github account.
- Clone the forked repository into your computer.
- Install dependencies with npm: Run
npm install
- a list of warnings on outdated dependencies is to be expected and no action needs to be taken before proceeding.
- Compile and start the application in browser: Run
npm run ng:serve:web
.
Given below are different commands you can use to run the app locally.
Command | Description |
---|---|
npm run ng:serve:web |
Start the app from the browser in development mode. |
If you are using Visual Studio Code to debug CATcher with Chrome, Firefox or Edge.
Refer to the Debugging Angular
section of this guide for a step-by-step walkthrough of the debugger setup.
In summary, the following steps are needed:
- Install the
Debugger for Chrome
extension for VS Code. You can also install debugger extensions for Firefox and Edge. - Create VS Code's debugger config file (
launch.json
) as shown in the guide. Particularly, set theurl
attribute tohttp://localhost:4200
(reason: CATcher app is served locally on port 4200, by default.) - In the root project folder, run
npm run ng:serve:web
. - In VS Code's Debug View, launch the debugger by clicking the green arrow (or F5). You should see the CATcher app loading within a new browser window.
CATcher documentation is hosted in a separate repo CATcher-org/catcher-org.github.io. When you need to update documentation, you'll need to fork and clone that repo to your computer as well.
We use MarkBind for documentation. Follow this tutorial to learn how to use MarkBind for updating project documentation.
These tasks assume a basic understanding of Angular
and TypeScript
.
If you wish to know more about them, you can visit our tools page.
Task 1: Add new label type.UiFlaw
Currently, there are only 3 types of bugs in CATcher: type.DocumentationBug
, type.FeatureFlaw
and type.FunctionalityBug
. Let's add a fourth one, type.UiFlaw
to understand the backend.
Your Task
Add a new label type.UiFlaw
to the existing list of labels. You may choose a color and a definition to your liking.
First, you need to locate the files responsible for labels. Note that this is a backend task, so it is unlikely to be an Angular component.
You may encounter 2 files that handle labels: label.service.ts
and label.model.ts
. label.model.ts
contains the class responsible for Labels. It does not contain the list of labels used in CATcher. Therefore, look into label.service.ts
.
In label.service.ts
, you will see how the various labels are defined. Can you emulate the other labels and add the new label?
In label.service.ts
, you should declare a new definition of type.UiFlaw
, then add it to the LABEL_DEFINITIONS
and REQUIRED_LABELS
under REQUIRED_LABELS['type']['UiFlaw']
.
See the changes here.
Task 2: Delete labels that are not in use
Upon creation of a new repository, Github will automatically create default labels, which we may not use in CATcher. The user might also have added other labels on their own.
Your task
Add a feature to delete all labels that are not required by CATcher upon login.
Similar to the first task, one of the files responsible for this feature is label.service.ts
. However, we need to interact with the Github API to delete labels. Can you find which service is responsible for handling Github API calls?
Refer to the octokit documentation for details on which Github API call to use.
-
Create a new method
deleteLabel(labelName)
ingithub.service.ts
. Call the octokit function to delete label. -
In
label.service.ts
, edit the privateLabelService.ensureRepoHasRequiredLabels(actualLabels, requiredLabels)
method, by iterating throughactualLabels
and deleting every label inactualLabels
that are not present inrequiredLabels
.
Refer here for the full changes.
Task 1: Make error snack bars automatically close
- To help you see the error snack bar, first launch
CATcher
locally. - Enter
null
inside the Settings Location input and click submit. - On the bottom of your browser, a pop-up "Failed to fetch settings file." should appear.
- You will see that it doesn't close by itself; you have to manually click the close button.
Your Task
Make the error snack bar disappear automatically, after being displayed for 3 seconds.
First, you need to locate the files responsible for error handling.
When searching for the relevant files, it is useful to know the name of the frontend component involved.
In this task, the name of the frontend component is MatSnackBar
. Try to find out where it's used.
For more information on MatSnackBar
, you can read the documentation here.
You should find that our related file is error-handling.service.ts
.
All that's left is to figure out how to tell snackBar
to close after a certain amount of time.
There are multiple ways to do this. One way is to use setTimeout
function on the snackBarRef.dismiss()
like this:
const snackBarRef = this.snackBar.openFromComponent(GeneralMessageErrorComponent, {data: error});
setTimeout(() => {
snackBarRef.dismiss();
}, this.snackBarAutoCloseTime);
There is more than 1 way to achieve this. By combining the changes in hint 1 and hint 2, you should be able to reach a solution.
import { ErrorHandler, Injectable } from '@angular/core';
import { MatSnackBar, MatSnackBarRef } from '@angular/material';
import { GeneralMessageErrorComponent } from '../../shared/error-toasters/general-message-error/general-message-error.component';
import { FormErrorComponent } from '../../shared/error-toasters/form-error/form-error.component';
import { HttpErrorResponse } from '@angular/common/http';
import { RequestError } from '@octokit/request-error';
import { LoggingService } from './logging.service';
export const ERRORCODE_NOT_FOUND = 404;
const FILTERABLE = ['node_modules'];
@Injectable({
providedIn: 'root',
})
export class ErrorHandlingService implements ErrorHandler {
snackBarAutoCloseTime = 3000;
constructor(private snackBar: MatSnackBar, private logger: LoggingService) {}
handleError(error: HttpErrorResponse | Error | RequestError, actionCallback?: () => void) {
this.logger.error(error);
if (error instanceof Error) {
this.logger.error(this.cleanStack(error.stack));
}
if (error instanceof HttpErrorResponse || error instanceof RequestError) {
this.handleHttpError(error, actionCallback);
} else {
this.handleGeneralError(error.message || JSON.stringify(error));
}
}
private addAutoClose<T>(snackBarRef: MatSnackBarRef<T>) {
setTimeout(() => {
snackBarRef.dismiss();
}, this.snackBarAutoCloseTime);
}
private cleanStack(stacktrace: string): string {
return stacktrace
.split('\n')
.filter(line => !FILTERABLE.some(word => line.includes(word))) // exclude lines that contain words in FILTERABLE
.join('\n');
}
// Ref: https://developer.github.com/v3/#client-errors
private handleHttpError(error: HttpErrorResponse | RequestError, actionCallback?: () => void): void {
let snackBarRef = null;
// Angular treats 304 Not Modified as an error, we will ignore it.
if (error.status === 304) {
return;
}
if (!navigator.onLine) {
snackBarRef = this.handleGeneralError('No Internet Connection');
this.addAutoClose(snackBarRef);
return;
}
switch (error.status) {
case 500: // Internal Server Error.
snackBarRef = this.snackBar.openFromComponent(GeneralMessageErrorComponent, {data: error});
break;
case 422: // Form errors
snackBarRef = this.snackBar.openFromComponent(FormErrorComponent, {data: error});
break;
case 400: // Bad request
case 401: // Unauthorized
case 404: // Not found
snackBarRef = this.snackBar.openFromComponent(GeneralMessageErrorComponent, {data: error});
break;
default:
snackBarRef = this.snackBar.openFromComponent(GeneralMessageErrorComponent, {data: error});
}
if (snackBarRef) {
this.addAutoClose(snackBarRef);
}
}
private handleGeneralError(error: string): void {
const snackBarRef = this.snackBar.openFromComponent(GeneralMessageErrorComponent, {data: {message: error}});
this.addAutoClose(snackBarRef);
}
}