Skip to content

Commit 6b55beb

Browse files
committed
Resolve merge conflicts
2 parents 559af68 + 60f044f commit 6b55beb

File tree

31 files changed

+1495
-122
lines changed

31 files changed

+1495
-122
lines changed

apps/docker-compose.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,15 @@ services:
129129
- apps_network
130130
container_name: python-sandbox
131131

132+
node-sandbox:
133+
build:
134+
context: ./execution-service/execution/node
135+
dockerfile: Dockerfile
136+
networks:
137+
- apps_network
138+
container_name: node-sandbox
139+
stdin_open: true # Enables interactive mode for passing standard input
140+
132141
networks:
133142
apps_network:
134143

apps/execution-service/README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,43 @@ The following json format will be returned:
160160
]
161161
```
162162

163+
`POST /tests`
164+
165+
To create a new test case, run the following command:
166+
167+
```bash
168+
curl -X POST http://localhost:8083/tests \
169+
-H "Content-Type: application/json" \
170+
-d '{
171+
"questionDocRefId": "sampleDocRefId123",
172+
"questionTitle": "Sample Question Title",
173+
"visibleTestCases": "2\nhello\nolleh\nHannah\nhannaH",
174+
"hiddenTestCases": "2\nHannah\nhannaH\nabcdefg\ngfedcba"
175+
}'
176+
```
177+
178+
`PUT /tests/{questionDocRefId}`
179+
180+
To update an existing test case from an existing question, run the following command:
181+
182+
```bash
183+
curl -X PUT http://localhost:8083/tests/{questionDocRefId} \
184+
-H "Content-Type: application/json" \
185+
-d '{
186+
"visibleTestCases": "2\nhello\nolleh\nHannah\nhannaH",
187+
"hiddenTestCases": "2\nHannah\nhannaH\nabcdefg\ngfedcba"
188+
}'
189+
```
190+
191+
`DELETE /tests/{questionDocRefId}`
192+
193+
To delete an existing test case from an existing question, run the following command:
194+
195+
```bash
196+
curl -X DELETE http://localhost:8083/tests/{questionDocRefId} \
197+
-H "Content-Type: application/json"
198+
```
199+
163200
`POST /tests/{questionDocRefId}/execute`
164201

165202
To execute test cases via a question ID without custom test cases, run the following command, with custom code and language:
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package constants
22

33
const (
4-
JAVA = "Java"
5-
PYTHON = "Python"
6-
GOLANG = "Golang"
7-
JAVASCRIPT = "Javascript"
8-
CPP = "C++"
4+
JAVA = "java"
5+
PYTHON = "python"
6+
GOLANG = "golang"
7+
JAVASCRIPT = "javascript"
8+
CPP = "c++"
99
)
1010

1111
const (
@@ -17,6 +17,6 @@ var IS_VALID_LANGUAGE = map[string]bool{
1717
PYTHON: true,
1818
//JAVA: true,
1919
//GOLANG: true,
20-
//JAVASCRIPT: true,
20+
JAVASCRIPT: true,
2121
//CPP: true,
2222
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Use a slim Node.js image
2+
FROM node:18-slim
3+
4+
# Set the working directory
5+
WORKDIR /app
6+
7+
# Install any dependencies if necessary (you can skip if no dependencies)
8+
# COPY package*.json ./
9+
# RUN npm install
10+
11+
# No entry point or CMD needed as you'll provide the command at runtime
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package node
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"os/exec"
7+
"strings"
8+
)
9+
10+
func RunJavaScriptCode(code string, input string) (string, string, error) {
11+
cmd := exec.Command(
12+
"docker", "run", "--rm",
13+
"-i", // allows standard input to be passed in
14+
"apps-node-sandbox", // Docker image with Node.js environment
15+
"node", "-e", code, // Runs JavaScript code with Node.js
16+
)
17+
18+
// Pass input to the JavaScript script
19+
cmd.Stdin = bytes.NewBufferString(input)
20+
21+
// Capture standard output and error output
22+
var output bytes.Buffer
23+
var errorOutput bytes.Buffer
24+
cmd.Stdout = &output
25+
cmd.Stderr = &errorOutput
26+
27+
// Run the command
28+
if err := cmd.Run(); err != nil {
29+
return "", fmt.Sprintf("Command execution failed: %s: %v", errorOutput.String(), err), nil
30+
}
31+
32+
return strings.TrimSuffix(output.String(), "\n"), strings.TrimSuffix(errorOutput.String(), "\n"), nil
33+
}

apps/execution-service/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ require (
88
github.com/go-chi/chi/v5 v5.1.0
99
github.com/go-chi/cors v1.2.1
1010
github.com/joho/godotenv v1.5.1
11+
github.com/rabbitmq/amqp091-go v1.10.0
1112
github.com/traefik/yaegi v0.16.1
1213
google.golang.org/api v0.203.0
1314
)
@@ -31,7 +32,6 @@ require (
3132
github.com/google/uuid v1.6.0 // indirect
3233
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
3334
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
34-
github.com/rabbitmq/amqp091-go v1.10.0 // indirect
3535
go.opencensus.io v0.24.0 // indirect
3636
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect
3737
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect

apps/execution-service/go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHy
111111
go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok=
112112
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
113113
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
114+
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
115+
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
114116
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
115117
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
116118
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package handlers
2+
3+
import (
4+
"cloud.google.com/go/firestore"
5+
"encoding/json"
6+
"execution-service/models"
7+
"execution-service/utils"
8+
"google.golang.org/api/iterator"
9+
"net/http"
10+
)
11+
12+
func (s *Service) CreateTest(w http.ResponseWriter, r *http.Request) {
13+
ctx := r.Context()
14+
15+
var test models.Test
16+
if err := utils.DecodeJSONBody(w, r, &test); err != nil {
17+
http.Error(w, err.Error(), http.StatusBadRequest)
18+
return
19+
}
20+
21+
// Basic validation for question title and question ID
22+
if test.QuestionDocRefId == "" || test.QuestionTitle == "" {
23+
http.Error(w, "QuestionDocRefId and QuestionTitle are required", http.StatusBadRequest)
24+
return
25+
}
26+
27+
// Normalise test cases
28+
test.VisibleTestCases = utils.NormaliseTestCaseFormat(test.VisibleTestCases)
29+
test.HiddenTestCases = utils.NormaliseTestCaseFormat(test.HiddenTestCases)
30+
31+
// Automatically populate validation for input and output in test case
32+
test.InputValidation = utils.GetDefaultValidation()
33+
test.OutputValidation = utils.GetDefaultValidation()
34+
35+
// Validate test case format
36+
if _, err := utils.ValidateTestCaseFormat(test.VisibleTestCases, test.InputValidation,
37+
test.OutputValidation); err != nil {
38+
http.Error(w, err.Error(), http.StatusBadRequest)
39+
return
40+
}
41+
if _, err := utils.ValidateTestCaseFormat(test.HiddenTestCases, test.InputValidation,
42+
test.OutputValidation); err != nil {
43+
http.Error(w, err.Error(), http.StatusBadRequest)
44+
return
45+
}
46+
47+
// Check if a test already exists for the question
48+
iter := s.Client.Collection("tests").Where("questionDocRefId", "==", test.QuestionDocRefId).Documents(ctx)
49+
for {
50+
_, err := iter.Next()
51+
if err == iterator.Done {
52+
break
53+
}
54+
if err != nil {
55+
http.Error(w, "Error fetching test", http.StatusInternalServerError)
56+
return
57+
}
58+
http.Error(w, "Test already exists for the question", http.StatusConflict)
59+
return
60+
}
61+
defer iter.Stop()
62+
63+
// Save test to Firestore
64+
docRef, _, err := s.Client.Collection("tests").Add(ctx, map[string]interface{}{
65+
"questionDocRefId": test.QuestionDocRefId,
66+
"questionTitle": test.QuestionTitle,
67+
"visibleTestCases": test.VisibleTestCases,
68+
"hiddenTestCases": test.HiddenTestCases,
69+
"inputValidation": test.InputValidation,
70+
"outputValidation": test.OutputValidation,
71+
"createdAt": firestore.ServerTimestamp,
72+
"updatedAt": firestore.ServerTimestamp,
73+
})
74+
if err != nil {
75+
http.Error(w, err.Error(), http.StatusInternalServerError)
76+
return
77+
}
78+
79+
// Get data
80+
doc, err := docRef.Get(ctx)
81+
if err != nil {
82+
if err != iterator.Done {
83+
http.Error(w, "Test not found", http.StatusInternalServerError)
84+
return
85+
}
86+
http.Error(w, "Failed to get test", http.StatusInternalServerError)
87+
return
88+
}
89+
90+
// Map data
91+
if err := doc.DataTo(&test); err != nil {
92+
http.Error(w, err.Error(), http.StatusInternalServerError)
93+
return
94+
}
95+
96+
w.Header().Set("Content-Type", "application/json")
97+
w.WriteHeader(http.StatusCreated)
98+
json.NewEncoder(w).Encode(test)
99+
}
100+
101+
// Manual test cases
102+
103+
//curl -X POST http://localhost:8083/tests \
104+
//-H "Content-Type: application/json" \
105+
//-d '{
106+
//"questionDocRefId": "sampleDocRefId123",
107+
//"questionTitle": "Sample Question Title",
108+
//"visibleTestCases": "2\nhello\nolleh\nHannah\nhannaH",
109+
//"hiddenTestCases": "2\nHannah\nhannaH\nabcdefg\ngfedcba"
110+
//}'
111+
112+
//curl -X POST http://localhost:8083/tests \
113+
//-H "Content-Type: application/json" \
114+
//-d "{
115+
//\"questionDocRefId\": \"sampleDocRefId12345\",
116+
//\"questionTitle\": \"Sample Question Title\",
117+
//\"visibleTestCases\": \"2\\nhello\\nolleh\\nHannah\\nhannaH\",
118+
//\"hiddenTestCases\": \"2\\nHannah\\nhannaH\\nabcdefg\\ngfedcba\"
119+
//}"
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package handlers
2+
3+
import (
4+
"github.com/go-chi/chi/v5"
5+
"google.golang.org/api/iterator"
6+
"net/http"
7+
)
8+
9+
func (s *Service) DeleteTest(w http.ResponseWriter, r *http.Request) {
10+
ctx := r.Context()
11+
12+
// Parse request
13+
docRefID := chi.URLParam(r, "questionDocRefId")
14+
15+
docRef := s.Client.Collection("tests").Where("questionDocRefId", "==", docRefID).Limit(1).Documents(ctx)
16+
doc, err := docRef.Next()
17+
if err != nil {
18+
if err == iterator.Done {
19+
http.Error(w, "Test not found", http.StatusNotFound)
20+
return
21+
}
22+
http.Error(w, err.Error(), http.StatusInternalServerError)
23+
return
24+
}
25+
defer docRef.Stop()
26+
27+
_, err = doc.Ref.Delete(ctx)
28+
if err != nil {
29+
http.Error(w, "Error deleting test", http.StatusInternalServerError)
30+
return
31+
}
32+
33+
w.Header().Set("Content-Type", "application/json")
34+
w.WriteHeader(http.StatusOK)
35+
}
36+
37+
// Manual test cases
38+
39+
//curl -X DELETE http://localhost:8083/tests/sampleDocRefId123 \
40+
//-H "Content-Type: application/json"
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package handlers
2+
3+
import (
4+
"encoding/json"
5+
"execution-service/models"
6+
"execution-service/utils"
7+
"net/http"
8+
9+
"github.com/go-chi/chi/v5"
10+
"google.golang.org/api/iterator"
11+
)
12+
13+
func (s *Service) ReadAllTests(w http.ResponseWriter, r *http.Request) {
14+
ctx := r.Context()
15+
16+
questionDocRefId := chi.URLParam(r, "questionDocRefId")
17+
if questionDocRefId == "" {
18+
http.Error(w, "questionDocRefId is required", http.StatusBadRequest)
19+
return
20+
}
21+
22+
iter := s.Client.Collection("tests").Where("questionDocRefId", "==", questionDocRefId).Limit(1).Documents(ctx)
23+
doc, err := iter.Next()
24+
if err != nil {
25+
if err == iterator.Done {
26+
http.Error(w, "Test not found", http.StatusNotFound)
27+
return
28+
}
29+
http.Error(w, err.Error(), http.StatusInternalServerError)
30+
return
31+
}
32+
defer iter.Stop()
33+
34+
var test models.Test
35+
if err := doc.DataTo(&test); err != nil {
36+
http.Error(w, err.Error(), http.StatusInternalServerError)
37+
return
38+
}
39+
40+
_, hiddenTestCases, err := utils.GetTestLengthAndUnexecutedCases(test.HiddenTestCases)
41+
42+
var hiddenTests []models.HiddenTest
43+
for _, hiddenTestCase := range hiddenTestCases {
44+
hiddenTests = append(hiddenTests, models.HiddenTest{
45+
Input: hiddenTestCase.Input,
46+
Expected: hiddenTestCase.Expected,
47+
})
48+
}
49+
50+
_, visibleTestCases, err := utils.GetTestLengthAndUnexecutedCases(test.VisibleTestCases)
51+
52+
var visibleTests []models.VisibleTest
53+
for _, visibleTestCase := range visibleTestCases {
54+
visibleTests = append(visibleTests, models.VisibleTest{
55+
Input: visibleTestCase.Input,
56+
Expected: visibleTestCase.Expected,
57+
})
58+
}
59+
60+
allTests := models.AllTests{
61+
VisibleTests: visibleTests,
62+
HiddenTests: hiddenTests,
63+
}
64+
65+
w.Header().Set("Content-Type", "application/json")
66+
w.WriteHeader(http.StatusOK)
67+
json.NewEncoder(w).Encode(allTests)
68+
}
69+
70+
//curl -X GET http://localhost:8083/tests/bmzFyLMeSOoYU99pi4yZ/ \
71+
//-H "Content-Type: application/json"

0 commit comments

Comments
 (0)