Skip to content

Commit

Permalink
Resolve merge conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
solomonng2001 committed Nov 14, 2024
2 parents 559af68 + 60f044f commit 6b55beb
Show file tree
Hide file tree
Showing 31 changed files with 1,495 additions and 122 deletions.
9 changes: 9 additions & 0 deletions apps/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,15 @@ services:
- apps_network
container_name: python-sandbox

node-sandbox:
build:
context: ./execution-service/execution/node
dockerfile: Dockerfile
networks:
- apps_network
container_name: node-sandbox
stdin_open: true # Enables interactive mode for passing standard input

networks:
apps_network:

Expand Down
37 changes: 37 additions & 0 deletions apps/execution-service/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,43 @@ The following json format will be returned:
]
```

`POST /tests`

To create a new test case, run the following command:

```bash
curl -X POST http://localhost:8083/tests \
-H "Content-Type: application/json" \
-d '{
"questionDocRefId": "sampleDocRefId123",
"questionTitle": "Sample Question Title",
"visibleTestCases": "2\nhello\nolleh\nHannah\nhannaH",
"hiddenTestCases": "2\nHannah\nhannaH\nabcdefg\ngfedcba"
}'
```

`PUT /tests/{questionDocRefId}`

To update an existing test case from an existing question, run the following command:

```bash
curl -X PUT http://localhost:8083/tests/{questionDocRefId} \
-H "Content-Type: application/json" \
-d '{
"visibleTestCases": "2\nhello\nolleh\nHannah\nhannaH",
"hiddenTestCases": "2\nHannah\nhannaH\nabcdefg\ngfedcba"
}'
```

`DELETE /tests/{questionDocRefId}`

To delete an existing test case from an existing question, run the following command:

```bash
curl -X DELETE http://localhost:8083/tests/{questionDocRefId} \
-H "Content-Type: application/json"
```

`POST /tests/{questionDocRefId}/execute`

To execute test cases via a question ID without custom test cases, run the following command, with custom code and language:
Expand Down
12 changes: 6 additions & 6 deletions apps/execution-service/constants/constant.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package constants

const (
JAVA = "Java"
PYTHON = "Python"
GOLANG = "Golang"
JAVASCRIPT = "Javascript"
CPP = "C++"
JAVA = "java"
PYTHON = "python"
GOLANG = "golang"
JAVASCRIPT = "javascript"
CPP = "c++"
)

const (
Expand All @@ -17,6 +17,6 @@ var IS_VALID_LANGUAGE = map[string]bool{
PYTHON: true,
//JAVA: true,
//GOLANG: true,
//JAVASCRIPT: true,
JAVASCRIPT: true,
//CPP: true,
}
11 changes: 11 additions & 0 deletions apps/execution-service/execution/node/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Use a slim Node.js image
FROM node:18-slim

# Set the working directory
WORKDIR /app

# Install any dependencies if necessary (you can skip if no dependencies)
# COPY package*.json ./
# RUN npm install

# No entry point or CMD needed as you'll provide the command at runtime
33 changes: 33 additions & 0 deletions apps/execution-service/execution/node/javascript.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package node

import (
"bytes"
"fmt"
"os/exec"
"strings"
)

func RunJavaScriptCode(code string, input string) (string, string, error) {
cmd := exec.Command(
"docker", "run", "--rm",
"-i", // allows standard input to be passed in
"apps-node-sandbox", // Docker image with Node.js environment
"node", "-e", code, // Runs JavaScript code with Node.js
)

// Pass input to the JavaScript script
cmd.Stdin = bytes.NewBufferString(input)

// Capture standard output and error output
var output bytes.Buffer
var errorOutput bytes.Buffer
cmd.Stdout = &output
cmd.Stderr = &errorOutput

// Run the command
if err := cmd.Run(); err != nil {
return "", fmt.Sprintf("Command execution failed: %s: %v", errorOutput.String(), err), nil
}

return strings.TrimSuffix(output.String(), "\n"), strings.TrimSuffix(errorOutput.String(), "\n"), nil
}
2 changes: 1 addition & 1 deletion apps/execution-service/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/go-chi/chi/v5 v5.1.0
github.com/go-chi/cors v1.2.1
github.com/joho/godotenv v1.5.1
github.com/rabbitmq/amqp091-go v1.10.0
github.com/traefik/yaegi v0.16.1
google.golang.org/api v0.203.0
)
Expand All @@ -31,7 +32,6 @@ require (
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
github.com/rabbitmq/amqp091-go v1.10.0 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions apps/execution-service/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHy
go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok=
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
Expand Down
119 changes: 119 additions & 0 deletions apps/execution-service/handlers/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package handlers

import (
"cloud.google.com/go/firestore"
"encoding/json"
"execution-service/models"
"execution-service/utils"
"google.golang.org/api/iterator"
"net/http"
)

func (s *Service) CreateTest(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()

var test models.Test
if err := utils.DecodeJSONBody(w, r, &test); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

// Basic validation for question title and question ID
if test.QuestionDocRefId == "" || test.QuestionTitle == "" {
http.Error(w, "QuestionDocRefId and QuestionTitle are required", http.StatusBadRequest)
return
}

// Normalise test cases
test.VisibleTestCases = utils.NormaliseTestCaseFormat(test.VisibleTestCases)
test.HiddenTestCases = utils.NormaliseTestCaseFormat(test.HiddenTestCases)

// Automatically populate validation for input and output in test case
test.InputValidation = utils.GetDefaultValidation()
test.OutputValidation = utils.GetDefaultValidation()

// Validate test case format
if _, err := utils.ValidateTestCaseFormat(test.VisibleTestCases, test.InputValidation,
test.OutputValidation); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if _, err := utils.ValidateTestCaseFormat(test.HiddenTestCases, test.InputValidation,
test.OutputValidation); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

// Check if a test already exists for the question
iter := s.Client.Collection("tests").Where("questionDocRefId", "==", test.QuestionDocRefId).Documents(ctx)
for {
_, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
http.Error(w, "Error fetching test", http.StatusInternalServerError)
return
}
http.Error(w, "Test already exists for the question", http.StatusConflict)
return
}
defer iter.Stop()

// Save test to Firestore
docRef, _, err := s.Client.Collection("tests").Add(ctx, map[string]interface{}{
"questionDocRefId": test.QuestionDocRefId,
"questionTitle": test.QuestionTitle,
"visibleTestCases": test.VisibleTestCases,
"hiddenTestCases": test.HiddenTestCases,
"inputValidation": test.InputValidation,
"outputValidation": test.OutputValidation,
"createdAt": firestore.ServerTimestamp,
"updatedAt": firestore.ServerTimestamp,
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

// Get data
doc, err := docRef.Get(ctx)
if err != nil {
if err != iterator.Done {
http.Error(w, "Test not found", http.StatusInternalServerError)
return
}
http.Error(w, "Failed to get test", http.StatusInternalServerError)
return
}

// Map data
if err := doc.DataTo(&test); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(test)
}

// Manual test cases

//curl -X POST http://localhost:8083/tests \
//-H "Content-Type: application/json" \
//-d '{
//"questionDocRefId": "sampleDocRefId123",
//"questionTitle": "Sample Question Title",
//"visibleTestCases": "2\nhello\nolleh\nHannah\nhannaH",
//"hiddenTestCases": "2\nHannah\nhannaH\nabcdefg\ngfedcba"
//}'

//curl -X POST http://localhost:8083/tests \
//-H "Content-Type: application/json" \
//-d "{
//\"questionDocRefId\": \"sampleDocRefId12345\",
//\"questionTitle\": \"Sample Question Title\",
//\"visibleTestCases\": \"2\\nhello\\nolleh\\nHannah\\nhannaH\",
//\"hiddenTestCases\": \"2\\nHannah\\nhannaH\\nabcdefg\\ngfedcba\"
//}"
40 changes: 40 additions & 0 deletions apps/execution-service/handlers/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package handlers

import (
"github.com/go-chi/chi/v5"
"google.golang.org/api/iterator"
"net/http"
)

func (s *Service) DeleteTest(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()

// Parse request
docRefID := chi.URLParam(r, "questionDocRefId")

docRef := s.Client.Collection("tests").Where("questionDocRefId", "==", docRefID).Limit(1).Documents(ctx)
doc, err := docRef.Next()
if err != nil {
if err == iterator.Done {
http.Error(w, "Test not found", http.StatusNotFound)
return
}
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer docRef.Stop()

_, err = doc.Ref.Delete(ctx)
if err != nil {
http.Error(w, "Error deleting test", http.StatusInternalServerError)
return
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
}

// Manual test cases

//curl -X DELETE http://localhost:8083/tests/sampleDocRefId123 \
//-H "Content-Type: application/json"
71 changes: 71 additions & 0 deletions apps/execution-service/handlers/readall.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package handlers

import (
"encoding/json"
"execution-service/models"
"execution-service/utils"
"net/http"

"github.com/go-chi/chi/v5"
"google.golang.org/api/iterator"
)

func (s *Service) ReadAllTests(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()

questionDocRefId := chi.URLParam(r, "questionDocRefId")
if questionDocRefId == "" {
http.Error(w, "questionDocRefId is required", http.StatusBadRequest)
return
}

iter := s.Client.Collection("tests").Where("questionDocRefId", "==", questionDocRefId).Limit(1).Documents(ctx)
doc, err := iter.Next()
if err != nil {
if err == iterator.Done {
http.Error(w, "Test not found", http.StatusNotFound)
return
}
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer iter.Stop()

var test models.Test
if err := doc.DataTo(&test); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

_, hiddenTestCases, err := utils.GetTestLengthAndUnexecutedCases(test.HiddenTestCases)

var hiddenTests []models.HiddenTest
for _, hiddenTestCase := range hiddenTestCases {
hiddenTests = append(hiddenTests, models.HiddenTest{
Input: hiddenTestCase.Input,
Expected: hiddenTestCase.Expected,
})
}

_, visibleTestCases, err := utils.GetTestLengthAndUnexecutedCases(test.VisibleTestCases)

var visibleTests []models.VisibleTest
for _, visibleTestCase := range visibleTestCases {
visibleTests = append(visibleTests, models.VisibleTest{
Input: visibleTestCase.Input,
Expected: visibleTestCase.Expected,
})
}

allTests := models.AllTests{
VisibleTests: visibleTests,
HiddenTests: hiddenTests,
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(allTests)
}

//curl -X GET http://localhost:8083/tests/bmzFyLMeSOoYU99pi4yZ/ \
//-H "Content-Type: application/json"
File renamed without changes.
Loading

0 comments on commit 6b55beb

Please sign in to comment.