Skip to content

Commit

Permalink
Merge pull request #32 from WillyTonkas/feature/back/auth0
Browse files Browse the repository at this point in the history
Feature/back/auth0
  • Loading branch information
FranCalveyra authored Jan 28, 2025
2 parents dcde13d + fba83bf commit 02b633d
Show file tree
Hide file tree
Showing 17 changed files with 560 additions and 128 deletions.
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

0 comments on commit 02b633d

Please sign in to comment.