Skip to content

Commit 8f08639

Browse files
authored
Merge branch 'staging' into solomon/save-submission-status-in-local-storage
2 parents d16fb22 + bd95eb0 commit 8f08639

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+744
-116
lines changed

.github/workflows/test.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ jobs:
117117
EXECUTION_SERVICE_PORT: ${{ vars.EXECUTION_SERVICE_PORT }}
118118
MATCHING_SERVICE_TIMEOUT: ${{ vars.MATCHING_SERVICE_TIMEOUT }}
119119
REDIS_URL: ${{ vars.REDIS_URL }}
120+
RABBITMQ_URL: ${{ vars.RABBITMQ_URL }}
120121
QUESTION_SERVICE_GRPC_URL: ${{ vars.QUESTION_SERVICE_GPRC_URL }}
121122
run: |
122123
cd ./apps/frontend
@@ -147,11 +148,13 @@ jobs:
147148
cd ../history-service
148149
echo "FIREBASE_CREDENTIAL_PATH=$HISTORY_FIREBASE_CREDENTIAL_PATH" >> .env
149150
echo "PORT=$HISTORY_SERVICE_PORT" >> .env
151+
echo "RABBMITMQ_URL=$RABBITMQ_URL" >> .env
150152
151153
cd ../execution-service
152154
echo "FIREBASE_CREDENTIAL_PATH=$EXECUTION_FIREBASE_CREDENTIAL_PATH" >> .env
153155
echo "PORT=$EXECUTION_SERVICE_PORT" >> .env
154156
echo "HISTORY_SERVICE_URL=$HISTORY_SERVICE_URL" >> .env
157+
echo "RABBMITMQ_URL=$RABBITMQ_URL" >> .env
155158
156159
cd ../signalling-service
157160
echo "PORT=$SIGNALLING_SERVICE_PORT" >> .env
@@ -198,6 +201,7 @@ jobs:
198201
SIGNALLING_SERVICE_URL: ${{ vars.SIGNALLING_SERVICE_URL }}
199202
EXECUTION_SERVICE_URL: ${{ vars.EXECUTION_SERVICE_URL }}
200203
run: |
204+
docker ps -a
201205
echo "Testing Question Service..."
202206
curl -sSL -o /dev/null $QUESTION_SERVICE_URL && echo "Question Service is up"
203207
echo "Testing User Service..."

README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,38 @@
99
- You can choose to develop individual microservices within separate folders within this repository **OR** use individual repositories (all public) for each microservice.
1010
- In the latter scenario, you should enable sub-modules on this GitHub classroom repository to manage the development/deployment **AND** add your mentor to the individual repositories as a collaborator.
1111
- The teaching team should be given access to the repositories as we may require viewing the history of the repository in case of any disputes or disagreements.
12+
13+
---
14+
15+
## Architecture Diagram
16+
17+
![Overall Architecture Diagram](./docs/architecture_diagram.png)
18+
19+
The overall architecture of PeerPrep follows a microservices architecture. The client acts as an orchestrator for the interaction between the different services.
20+
21+
## Screenshots
22+
23+
![Home Page](./docs/home_page.png)
24+
25+
![Collaboration Page](./docs/collab_page_1.png)
26+
27+
![Collaboration Page](./docs/collab_page_2.png)
28+
29+
![Question Page](./docs/question_page.png)
30+
31+
![Question Page](./docs/indiv_question_page.png)
32+
33+
![History Page](./docs/submission_history_page.png)
34+
35+
## More details
36+
37+
- [Frontend](./apps/frontend/README.md)
38+
- [User Service](./apps/user-service/README.md)
39+
- [Question Service](./apps/question-service/README.md)
40+
- [Matching Service](./apps/matching-service/README.md)
41+
- [Signalling Service](./apps/signalling-service/README.md)
42+
- [History Service](./apps/history-service/README.md)
43+
- [Execution Service](./apps/execution-service/README.md)
44+
- [CI/CD Guide](./docs/cicid.md)
45+
- [Docker Compose Guide](./apps/README.md)
46+
- [Set Up Guide](./docs/setup.md)

apps/README.md

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
This project uses Docker Compose to manage multiple services such as a frontend, backend, and a database. The configuration is defined in the `docker-compose.yml` file, and environment variables can be stored in environment files for different environments (e.g., development, production).
44

5+
More details on how to set up Docker Compose can be found [here](../docs/setup.md)
6+
57
## Prerequisites
68

79
Before you begin, ensure you have the following installed on your machine:
@@ -30,11 +32,25 @@ In the `./apps` directory:
3032
├── user-service
3133
│ ├── Dockerfile # Dockerfile for user-service
3234
│ └── ... (other user-service files)
33-
35+
├── execution-service
36+
│ ├── Dockerfile # Dockerfile for execution-service
37+
│ └── ... (other execution-service files)
38+
├── signalling-service
39+
│ ├── Dockerfile # Dockerfile for signalling-service
40+
│ └── ... (other signalling-service files)
41+
├── history-service
42+
│ ├── Dockerfile # Dockerfile for history-service
43+
│ └── ... (other history-service files)
3444
```
3545

3646
## Docker Compose Setup
3747

48+
Ensure that you are currently using **Docker Compose v2** in your local Docker Desktop.
49+
- Launch your local Docker Desktop application
50+
- Click on settings button at the top right hand corner (beside the name)
51+
- Under the General tab, scroll down until you see a checkbox that says Use Docker Compose V2, ensure that the box is checked then apply and restart (refer to the image below)
52+
![Docker Compose V2](https://github.com/user-attachments/assets/3b8d47c2-c488-4fc1-804d-418ffebbdd9c)
53+
3854
By using multiple Dockerfiles in Docker Compose, we can manage complex multi-container applications where each service has its own environment and build process.
3955

4056
1. Build and Start the Application
@@ -54,11 +70,15 @@ This will:
5470

5571
Once running, you can access:
5672

57-
- The **frontend** at http://localhost:3000
58-
- The **user service** at http://localhost:3001
59-
- The **question service** at http://localhost:8080 (REST) and http://localhost:50051 (gRPC)
60-
- The **matching service** at http://localhost:8081
61-
- The **redis service** at http://localhost:6379
73+
- The [**frontend**](./frontend/README.md) at http://localhost:3000
74+
- The [**user-service**](./user-service/README.md) at http://localhost:3001
75+
- The [**question-service**](./question-service/README.md) at http://localhost:8080 (REST) and http://localhost:50051 (gRPC)
76+
- The [**matching-service**](./matching-service/README.md) at http://localhost:8081
77+
- The [**history-service**](./history-service/README.md) at http://localhost:8082
78+
- The [**execution-service**](./execution-service/README.md) at http://localhost:8083
79+
- The [**signalling-service**](./signalling-service/README.md) at http://localhost:4444
80+
- The **redis** at http://localhost:6379
81+
- The **rabbitmq** at http://localhost:5672
6282

6383
3. Stopping Services
6484

@@ -76,6 +96,11 @@ This command will stop and remove the containers, networks, and volumes created
7696

7797
- **Port Conflicts**: If you encounter port conflicts, ensure the host ports specified in docker-compose.yml (e.g., 3000:3000) are not in use by other applications.
7898
- **Environment Variables Not Loaded**: Ensure the `.env` files are in the correct directories as found in the `docker-compose.yml` file.
99+
- **Command execution failed**: When you try running test cases or submitting the code in the collaborative environment, if you encounter the following error message:
100+
```bash
101+
Command execution failed: Unable to find image 'apps-python-sandbox:latest' locally docker: Error response from daemon: pull access denied for apps-python-sandbox, repository does not exist or may require 'docker login': denied: requested access to the resource is denied. See 'docker run --help'. : exit status 125
102+
```
103+
Ensure that you have **Docker Compose V2** enabled for your Docker Desktop application. Please refer to the Docker Compose setup guide above to enable it locally.
79104

80105
### Known Issues
81106

apps/execution-service/README.md

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,59 @@
11
# Execution Service
22

3+
The Execution Service provides backend functionality for running and validating code executions or submissions within a coding platform. It enables users to execute code against test cases and receive feedback on the correctness of their solutions.
4+
5+
The Execution Service incorporates a code execution mechanism designed to run user-submitted solutions within an isolated, sandboxed environment. This approach enhances security by preventing arbitrary code from interacting with the host system directly and allows for performance monitoring
6+
7+
### Technology Stack
8+
9+
- Golang (Go): Statically typed, compiled language with low latency. Fast and efficient processing is ideal for high-read, high-write environments like in Execution Service, when many users run tests or submit tests.
10+
- Rest Server: chi router was utilized which supports CORS, logging and timeout via middlewares. It is stateless, which reduces coupling and enhances scalability and reliability, simplicity and flexibility. For example, clients may make requests to different server instances when scaled.
11+
- Firebase Firestore: NoSQL Document database that is designed for automatic horizontal scaling and schema-less design that allows for flexibility as number of tests increases or more users run tests.
12+
- Docker: used to containerize the Execution Service to simplify deployment. Additionally used to provide a sandboxed execution environment for user-submitted code, ensuring security by limiting code access to the host system and managing dependencies independently.
13+
14+
### Execution Process
15+
16+
For execution of user code (running of test cases without submission), only visible (public) and custom test cases are executed.
17+
18+
![Diagram of code execution process](../../docs/exeuction_process.png)
19+
20+
### Submission Process
21+
22+
For submission of user code, both visible (public) and hidden testcases are executed, before calling the history-service API to submit the submission data, code and test results.
23+
24+
![Diagram of code submission process](../../docs/submission_process.png)
25+
26+
### Design Decisions
27+
28+
1. **Docker Containerisation**
29+
a. Upon receiving a code execution request, the service dynamically creates a Docker container with a controlled environment tailored to Python
30+
b. The Docker container is set up with only the minimal permissions and resources needed to execute the code, restricting the execution environment to reduce risk
31+
c. This containerized environment is automatically destroyed after execution, ensuring no residual data or state remains between executions
32+
33+
2. **Security and Isolation**
34+
a. Containers provide isolation from the host system, limiting any interaction between user code and the underlying infrastructure
35+
b. Only essential files and libraries required for code execution are included, reducing potential attack surfaces within each container. The sandboxed, container-based execution system provides a secure and efficient way to run user code submissions.
36+
37+
The sandboxed, container-based execution system provides a secure and efficient way to run user code submissions.
38+
39+
### Communication between Execution and History Service
40+
41+
The communication between the Execution service and the History service is implemented through a RabbitMQ Message Queue. RabbitMQ is ideal for message queues in microservices due to its reliability, flexible routing, and scalability. It ensures messages aren’t lost through durable queues and supports complex routing to handle diverse messaging needs.
42+
43+
Asynchronous communication was chosen as a user’s submission history did not need to be updated immediately. Instead of waiting for a response, the Execution Service can put the message in a queue and continue processing other requests.
44+
45+
![RabbitMQ Message Queue](./../../docs/rabbit_mq_queue.png)
46+
47+
A message queue allows services to communicate without depending on each other's availability. The Execution Service can send a message to the queue, and the History Service can process it when it’s ready. This decoupling promotes loose coupling and reduces dependencies between services, which helps maintain a robust and adaptable system.
48+
49+
---
50+
51+
## Setup
52+
53+
### Prerequisites
54+
55+
Ensure you have Go installed on your machine.
56+
357
### Installation
458

559
1. Install dependencies:
@@ -61,10 +115,10 @@ The server will be available at http://localhost:8083.
61115

62116
## API Endpoints
63117

64-
- `POST /tests/populate`
65-
- `GET /tests/{questionDocRefId}/`
66-
- `POST /tests/{questionDocRefId}/execute`
67-
- `POST /tests/{questionDocRefId}/submit`
118+
- `POST: /tests/populate`: Deletes and repopulates all tests in Firebase
119+
- `GET: /{questionDocRefId}`: Reads the public testcases for the question, identified by the question reference ID
120+
- `POST: /{questionDocRefId}/execute`: Executes the public testcases for the question, identified by the question reference ID
121+
- `POST: /{questionDocRefId}/submit`: Executes the public and hidden testcases for the question, identified by the question reference ID, and submits the code submission to History Service
68122

69123
## Managing Firebase
70124

apps/frontend/README.md

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,27 @@
1-
This is the frontend for the question service.
1+
# Frontend
22

3-
## Tech Stack
3+
![Home page](../../docs/home_page.png)
44

5-
- Next.js
6-
- TypeScript
7-
- Ant Design
8-
- SCSS
5+
### Tech Stack
6+
7+
- React: React is one of the most popular UI libraries that allows the creation of reusable UI functional components. Its community ecosystem also offers React hooks that simplify the implementation of some of our frontend components, such as websockets.
8+
- Next.js: A React framework for building single-page applications. It comes with several useful features such as automatic page routing based on filesystem.
9+
- Ant Design: An enterprise-level design system that comes with several extensible UI components and solutions out-of-the-box, which allows us to quickly create nice-looking components that can be adjusted according to our requirements.
10+
- Typescript: A language extension of Javascript that allows us to perform static type-checking, to ensure that issues with incorrectly used types are caught and resolved as early as possible, improving code maintainability.
11+
12+
### Authorization-based Route Protection with Next.js Middleware
13+
14+
Middleware is a Next.js feature that allows the webpage server to intercept page requests and perform checks before serving the webpage. We used this feature to protect page access from unauthenticated users. This was done by checking the request’s JWT token (passed as a cookie) against the user service and redirecting users without authorized access to a public route (namely, the login page).
15+
16+
### User Flow and Communication between Microservices
17+
18+
Clients interact with the microservices through dedicated endpoints, with each microservice managing its own database for independent reading and writing.
19+
20+
Having individual databases per microservice improves data security, scalability, fault isolation, flexibility in database choice, and development efficiency. This approach allows each microservice to operate independently, optimizing stability, performance, and adaptability in the system.
21+
22+
![Diagram for user flow and communication between microservices](../../docs/userflow.png)
23+
24+
---
925

1026
## Getting Started
1127

apps/frontend/__tests__/unit-tests/question.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,9 +210,11 @@ describe("GetSingleQuestion", () => {
210210
const DOCREF = "mockdocref";
211211
beforeEach(() => {
212212
global.fetch = jest.fn().mockResolvedValue({
213+
ok: true, // Ensure `ok` is true to hit the success branch
213214
async json() {
214215
return QUESTIONS[0]
215-
}
216+
},
217+
text: () => Promise.resolve('mocked response'),
216218
});
217219
});
218220

@@ -238,6 +240,7 @@ describe("CreateQuestion", () => {
238240
global.fetch = jest.fn().mockResolvedValue({
239241
status: 200,
240242
statusText: "OK",
243+
ok: true, // Ensure `ok` is true to hit the success branch
241244
async json() {
242245
return createdQuestion
243246
}

apps/frontend/pnpm-lock.yaml

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/frontend/src/app/collaboration/[id]/page.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
Tag,
1313
Typography,
1414
Spin,
15+
Tooltip,
1516
} from "antd";
1617
import { Content } from "antd/es/layout/layout";
1718
import "./styles.scss";
@@ -168,6 +169,13 @@ export default function CollaborationPage(props: CollaborationProps) {
168169
});
169170
};
170171

172+
const errorMessage = (message: string) => {
173+
messageApi.open({
174+
type: "error",
175+
content: message,
176+
});
177+
};
178+
171179
const sendSubmissionResultsToMatchedUser = (data: SubmissionResults) => {
172180
if (!providerRef.current) {
173181
throw new Error("Provider not initialized");
@@ -312,6 +320,8 @@ export default function CollaborationPage(props: CollaborationProps) {
312320
if (visibleTestCases.length == 0) {
313321
GetVisibleTests(questionDocRefId).then((data: Test[]) => {
314322
setVisibleTestCases(data);
323+
}).catch((e) => {
324+
errorMessage(e.message);
315325
});
316326
}
317327

@@ -399,6 +409,7 @@ export default function CollaborationPage(props: CollaborationProps) {
399409
localStorage.removeItem("matchedTopics");
400410
localStorage.removeItem("submissionHiddenTestResultsAndStatus");
401411
localStorage.removeItem("visibleTestResults");
412+
localStorage.removeItem("editor-language"); // Remove editor language type when session closed
402413
};
403414

404415
return (
@@ -507,7 +518,9 @@ export default function CollaborationPage(props: CollaborationProps) {
507518
/>
508519
)}
509520
<div className="hidden-test-results">
510-
<InfoCircleFilled className="hidden-test-icon" />
521+
<Tooltip title="Status applies only to this session">
522+
<InfoCircleFilled className="hidden-test-icon" />
523+
</Tooltip>
511524
<Typography.Text
512525
strong
513526
style={{

0 commit comments

Comments
 (0)