Skip to content

Commit bb8cc46

Browse files
authored
Merge pull request #88 from CS3219-AY2425S1/solomon/add-execute-javascript
feat: n4 add javascript execution
2 parents abf4605 + e23725b commit bb8cc46

File tree

7 files changed

+108
-39
lines changed

7 files changed

+108
-39
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

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/utils/executeTest.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package utils
22

33
import (
44
"execution-service/constants"
5+
"execution-service/execution/node"
56
"execution-service/execution/python"
67
"execution-service/models"
78
"fmt"
@@ -15,6 +16,8 @@ func ExecuteVisibleAndCustomTests(code models.Code, test models.Test) (models.Ex
1516
case constants.PYTHON:
1617
testResults, err = getVisibleAndCustomTestResults(code, test, python.RunPythonCode)
1718
break
19+
case constants.JAVASCRIPT:
20+
testResults, err = getVisibleAndCustomTestResults(code, test, node.RunJavaScriptCode)
1821
default:
1922
return models.ExecutionResults{}, fmt.Errorf("unsupported language: %s", code.Language)
2023
}
@@ -33,6 +36,8 @@ func ExecuteVisibleAndHiddenTests(code models.Code, test models.Test) (models.Su
3336
case constants.PYTHON:
3437
testResults, err = getVisibleAndHiddenTestResults(code, test, python.RunPythonCode)
3538
break
39+
case constants.JAVASCRIPT:
40+
testResults, err = getVisibleAndHiddenTestResults(code, test, node.RunJavaScriptCode)
3641
default:
3742
return models.SubmissionResults{}, fmt.Errorf("unsupported language: %s", code.Language)
3843
}

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

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export default function CollaborationPage(props: CollaborationProps) {
7171
const [complexity, setComplexity] = useState<string | undefined>(undefined);
7272
const [categories, setCategories] = useState<string[]>([]); // Store the selected filter categories
7373
const [description, setDescription] = useState<string | undefined>(undefined);
74-
const [selectedLanguage, setSelectedLanguage] = useState("Python"); // State to hold the selected language item
74+
const [selectedLanguage, setSelectedLanguage] = useState("python"); // State to hold the selected language item
7575

7676
// Session states
7777
const [collaborationId, setCollaborationId] = useState<string | undefined>(
@@ -232,22 +232,29 @@ export default function CollaborationPage(props: CollaborationProps) {
232232
localStorage.setItem("visibleTestResults", JSON.stringify(data.visibleTestResults));
233233
};
234234

235+
const updateLangauge = (data: string) => {
236+
setSelectedLanguage(data);
237+
}
238+
235239
const handleRunTestCases = async () => {
236240
if (!questionDocRefId) {
237241
throw new Error("Question ID not found");
238242
}
239243
setIsLoadingTestCase(true);
240244
sendExecutingStateToMatchedUser(true);
241-
const data = await ExecuteVisibleAndCustomTests(questionDocRefId, {
242-
code: code,
243-
language: selectedLanguage,
244-
customTestCases: "",
245-
});
246-
updateExecutionResults(data);
247-
infoMessage("Test cases executed. Review the results below.");
248-
sendExecutionResultsToMatchedUser(data);
249-
setIsLoadingTestCase(false);
250-
sendExecutingStateToMatchedUser(false);
245+
try {
246+
const data = await ExecuteVisibleAndCustomTests(questionDocRefId, {
247+
code: code,
248+
language: selectedLanguage,
249+
customTestCases: "",
250+
});
251+
updateExecutionResults(data);
252+
infoMessage("Test cases executed. Review the results below.");
253+
sendExecutionResultsToMatchedUser(data);
254+
} finally {
255+
setIsLoadingTestCase(false);
256+
sendExecutingStateToMatchedUser(false);
257+
}
251258
};
252259

253260
const handleSubmitCode = async () => {
@@ -256,25 +263,28 @@ export default function CollaborationPage(props: CollaborationProps) {
256263
}
257264
setIsLoadingSubmission(true);
258265
sendSubmittingStateToMatchedUser(true);
259-
const data = await ExecuteVisibleAndHiddenTestsAndSubmit(questionDocRefId, {
260-
code: code,
261-
language: selectedLanguage,
262-
user: currentUser ?? "",
263-
matchedUser: matchedUser ?? "",
264-
matchedTopics: matchedTopics ?? [],
265-
title: questionTitle ?? "",
266-
questionDifficulty: complexity ?? "",
267-
questionTopics: categories,
268-
});
269-
updateExecutionResults({
270-
visibleTestResults: data.visibleTestResults,
271-
customTestResults: [],
272-
});
273-
updateSubmissionResults(data);
274-
sendSubmissionResultsToMatchedUser(data);
275-
successMessage("Code saved successfully!");
276-
setIsLoadingSubmission(false);
277-
sendSubmittingStateToMatchedUser(false);
266+
try {
267+
const data = await ExecuteVisibleAndHiddenTestsAndSubmit(questionDocRefId, {
268+
code: code,
269+
language: selectedLanguage,
270+
user: currentUser ?? "",
271+
matchedUser: matchedUser ?? "",
272+
matchedTopics: matchedTopics ?? [],
273+
title: questionTitle ?? "",
274+
questionDifficulty: complexity ?? "",
275+
questionTopics: categories,
276+
});
277+
updateExecutionResults({
278+
visibleTestResults: data.visibleTestResults,
279+
customTestResults: [],
280+
});
281+
updateSubmissionResults(data);
282+
sendSubmissionResultsToMatchedUser(data);
283+
successMessage("Code saved successfully!");
284+
} finally {
285+
setIsLoadingSubmission(false);
286+
sendSubmittingStateToMatchedUser(false);
287+
}
278288
};
279289

280290
const handleCodeChange = (code: string) => {
@@ -505,7 +515,7 @@ export default function CollaborationPage(props: CollaborationProps) {
505515
ref={editorRef}
506516
user={currentUser}
507517
collaborationId={collaborationId}
508-
language={selectedLanguage}
518+
updateLanguage={updateLangauge}
509519
setMatchedUser={setMatchedUser}
510520
handleCloseCollaboration={handleCloseCollaboration}
511521
providerRef={providerRef}

apps/frontend/src/components/CollaborativeEditor/CollaborativeEditor.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import { ExecutionResults, SubmissionResults } from "@/app/services/execute";
3232
interface CollaborativeEditorProps {
3333
user: string;
3434
collaborationId: string;
35-
language: string;
35+
updateLanguage: (language: string) => void;
3636
setMatchedUser: Dispatch<SetStateAction<string>>;
3737
handleCloseCollaboration: (type: string) => void;
3838
providerRef: MutableRefObject<WebrtcProvider | null>;
@@ -203,6 +203,7 @@ const CollaborativeEditor = forwardRef(
203203
language: selectedLanguage,
204204
id: latestLanguageChangeId,
205205
});
206+
props.updateLanguage(selectedLanguage);
206207
success(`Changed Code Editor's language to ${selectedLanguage}`);
207208
} else {
208209
setMounted(true);
@@ -385,7 +386,7 @@ const CollaborativeEditor = forwardRef(
385386
extensions: [
386387
basicSetup,
387388
languageConf.of(python()),
388-
// languageConf.of(javascript()),
389+
// languageConf.of(node()),
389390
autoLanguage,
390391
yCollab(ytext, provider.awareness, { undoManager }),
391392
keymap.of([indentWithTab]),

0 commit comments

Comments
 (0)