Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/back/auth0 #32

Merged
merged 5 commits into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
DATABASE_URL=<CONTAINER_URL># This is for the .env to get the Docker container URL
HOST=<DB_HOST>
POSTGRES_DB=<DB_NAME>
POSTGRES_USER=<DB_USER>
POSTGRES_PASSWORD=<DB_PASSWORD>
DATABASE_PORT=<DB_PORT>
SERVER_PORT=<API_PORT>
DATABASE_URL_LOCAL=<LOCAL_URL># This is for the .env to get the local URL

# The URL of our Auth0 Tenant Domain.
# If you're using a Custom Domain, be sure to set this to that value instead.
AUTH0_DOMAIN=<AUTH0_DOMAIN>

# Our Auth0 application's Client ID.
AUTH0_CLIENT_ID=<AUTH0_CLIENT_ID>

# Our Auth0 application's Client Secret.
AUTH0_CLIENT_SECRET=<AUTH0_CLIENT_SECRET>

# The Callback URL of our application. Customizable.
AUTH0_CALLBACK_URL=<AUTH0_CALLBACK_URL>
AUTH0_AUDIENCE=<CUSTOM_API_IDENTIFIER>

# These two are different from the previous ones, from these ones we're getting the Management API JWT
AUTH0_TEST_CLIENT_ID=<AUTH0_TEST_APP_CLIENT_ID>
AUTH0_TEST_CLIENT_SECRET=<AUTH0_TEST_APP_CLIENT_SECRET>
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.23 AS builder
FROM golang:1.23-alpine AS builder

WORKDIR /app

Expand All @@ -15,4 +15,6 @@ WORKDIR /root/

COPY --from=builder /app/rpl-service .

COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/

CMD ["./rpl-service"]
61 changes: 61 additions & 0 deletions config/databaseConfig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package config

import (
"fmt"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"log"
"os"
"rpl-service/constants"
"rpl-service/models"
)

func StartDatabase() *gorm.DB {
// Uncomment these lines when running in local
// // Retrieve environment variables
// err := godotenv.Load(".env")
// if err != nil {
// log.Fatalf("Error loading .env file: %v", err)
// return nil
//}

host := os.Getenv("HOST")
user := os.Getenv("POSTGRES_USER")
password := os.Getenv("POSTGRES_PASSWORD")
dbname := os.Getenv("POSTGRES_DB")
port := os.Getenv("DATABASE_PORT")

envVariables := []string{host, user, password, dbname, port}

for _, envVar := range envVariables {
if envVar == constants.EmptyString {
log.Fatal("One or more database environment variables are not set")
}
}

// Database connection string
dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=disable", host, user, password, dbname, port)

// Open the database connection
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatalf("failed to connect to the database: %v", err)
}

// Enable uuid-ossp extension
err = db.Exec("CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"").Error
if err != nil {
log.Fatalf("failed to enable uuid-ossp extension: %v", err)
}

migrateSchemas(db)

return db
}

func migrateSchemas(db *gorm.DB) {
err := db.AutoMigrate(&models.Course{}, &models.Exercise{}, &models.Test{}, &models.IsEnrolled{})
if err != nil {
log.Fatalf("failed to migrate database: %v", err)
}
}
50 changes: 50 additions & 0 deletions config/routerConfig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package config

import (
"fmt"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"net/http"
"rpl-service/controllers/course"
"rpl-service/models"
"rpl-service/platform/middleware"
)

func InitializeRoutes(router *gin.Engine, db *gorm.DB) {
for _, endpoint := range course.Endpoints {
mapToGinRoute(router, endpoint, db)
}
}

func mapToGinRoute(router *gin.Engine, endpoint models.Endpoint, db *gorm.DB) {
methods := map[string]func(string, ...gin.HandlerFunc) gin.IRoutes{
"GET": router.GET,
"POST": router.POST,
"PUT": router.PUT,
"DELETE": router.DELETE,
}

// Validate method exists
method, exists := methods[endpoint.Method]
if !exists {
panic(fmt.Sprintf("Unsupported method: %s", endpoint.Method))
}

// Create the base handler
handler := func(w http.ResponseWriter, r *http.Request) {
endpoint.HandlerFunction(w, r, db)
}

// Apply middleware if the route is protected
if endpoint.IsProtected {
method(endpoint.Path, gin.WrapH(middleware.EnsureValidToken()(
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
handler(w, r)
}))))
} else {
// Unprotected route
method(endpoint.Path, gin.WrapF(func(w http.ResponseWriter, r *http.Request) {
handler(w, r)
}))
}
}
3 changes: 3 additions & 0 deletions constants/constants.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
package constants

import "time"

const EmptyString = ""
const ProviderDuration = 5 * time.Minute
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package controllers
package course

import (
"encoding/json"
Expand All @@ -10,9 +10,10 @@ import (
"rpl-service/services/users"
)

const BaseURL = "/courses"
// Controllers should have all the functions and logic, routers should expose the endpoints.
// This is the controller for the course entity.

func CourseExists(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
func Exists(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
courseID := r.PathValue("id") // Get the course ID from the URL
if courseID == constants.EmptyString {
http.Error(w, "Course ID is required", http.StatusBadRequest)
Expand All @@ -24,20 +25,20 @@ func CourseExists(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
return
}

// Should return whether a user with that ID exists
// Should return whether a user with that ID Exists
if !users.CourseExists(db, courseUUID) { // TODO: change package name
http.Error(w, "Course not found", http.StatusNotFound)
return
}
w.WriteHeader(http.StatusOK)
_, err = w.Write([]byte("Course exists"))
_, err = w.Write([]byte("Course Exists"))
if err != nil {
return
}
}

func CreateCourse(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
// Should create a new course
func Create(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
// Should Create a new course
var body models.Course
err := json.NewDecoder(r.Body).Decode(&body)
if err != nil {
Expand All @@ -48,7 +49,7 @@ func CreateCourse(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
userID := uuid.New() // TODO: get the actual userID
currentCourse, creatingCourseErr := users.CreateCourse(db, userID, body.Name, body.Description)
if creatingCourseErr != nil {
http.Error(w, "Failed to create course", http.StatusInternalServerError)
http.Error(w, "Failed to Create course", http.StatusInternalServerError)
return
}

Expand All @@ -67,7 +68,7 @@ func CreateCourse(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
}
}

// TODO: Test this function after implementing auth0.
// EnrollToCourse TODO: Test this function after implementing auth0.
func EnrollToCourse(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
var enrollmentRequest struct {
UserID uuid.UUID `json:"UserID"`
Expand All @@ -93,7 +94,7 @@ func EnrollToCourse(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
}
}

// TODO: Test this function after implementing auth0.
// StudentExists TODO: Test this function after implementing auth0.
func StudentExists(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
var enrollmentRequest struct {
UserID uuid.UUID `json:"UserID"`
Expand All @@ -118,7 +119,7 @@ func StudentExists(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
}
}

// TODO: Test this function after implementing auth0.
// DeleteStudent TODO: Test this function after implementing auth0.
func DeleteStudent(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
var deleteRequest struct {
UserID uuid.UUID `json:"UserID"`
Expand All @@ -144,33 +145,3 @@ func DeleteStudent(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
return
}
}

var CourseExistsEndpoint = models.Endpoint{
Method: models.GET,
Path: BaseURL + "/course/exists/{id}",
HandlerFunction: CourseExists,
}

var CreateCourseEndpoint = models.Endpoint{
Method: models.POST,
Path: BaseURL + "/course",
HandlerFunction: CreateCourse,
}

var EnrollToCourseEndpoint = models.Endpoint{
Method: models.POST,
Path: BaseURL + "/enroll",
HandlerFunction: EnrollToCourse,
}

var StudentExistsEndPoint = models.Endpoint{
Method: models.POST,
Path: BaseURL + "/check-enrollment",
HandlerFunction: StudentExists,
}

var DeleteStudentEndpoint = models.Endpoint{
Method: models.DELETE,
Path: BaseURL + "/delete-student",
HandlerFunction: DeleteStudent,
}
1 change: 1 addition & 0 deletions controllers/course/courseController_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package course_test
50 changes: 50 additions & 0 deletions controllers/course/courseRouter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package course

import (
"rpl-service/models"
)

const BaseURL = "/courses"

var ExistsEndpoint = models.Endpoint{
Method: models.GET,
Path: BaseURL + "/course/{id}",
HandlerFunction: Exists,
IsProtected: true,
}

var CreateCourseEndpoint = models.Endpoint{
Method: models.POST,
Path: BaseURL + "/course",
HandlerFunction: Create,
IsProtected: true,
}

var EnrollToCourseEndpoint = models.Endpoint{
Method: models.POST,
Path: BaseURL + "/enroll",
HandlerFunction: EnrollToCourse,
IsProtected: true,
}

var StudentExistsEndPoint = models.Endpoint{
Method: models.GET,
Path: BaseURL + "/course/{id}/is-enrolled",
HandlerFunction: StudentExists,
IsProtected: true,
}

var DeleteStudentEndpoint = models.Endpoint{
Method: models.DELETE,
Path: BaseURL + "/course/{id}/student",
HandlerFunction: DeleteStudent,
IsProtected: true,
}

var Endpoints = []models.Endpoint{
ExistsEndpoint,
CreateCourseEndpoint,
EnrollToCourseEndpoint,
StudentExistsEndPoint,
DeleteStudentEndpoint,
}
1 change: 0 additions & 1 deletion controllers/courseController_test.go

This file was deleted.

4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ services:
context: .
dockerfile: Dockerfile
ports:
- "8080:8080"
- ${SERVER_PORT}:${SERVER_PORT}
env_file:
- .env
depends_on:
Expand All @@ -19,7 +19,7 @@ services:
env_file:
- .env
ports:
- "5433:5432"
- ${DATABASE_PORT}:${DATABASE_PORT}
volumes:
- db_data:/var/lib/postgresql/data

Expand Down
Loading