Skip to content
4 changes: 3 additions & 1 deletion cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,14 @@ func main() {
subjectRepo := repository.NewSubjectRepository(db)
syllabusRepo := repository.NewSyllabusRepository(db)
facultyRepo := repository.NewFacultyRepository(db)
roomRepo := repository.NewRoomRepository(db)
// Services
subjectSvc := service.NewSubjectService(subjectRepo, syllabusRepo)
facultySvc := service.NewFacultyService(facultyRepo)
roomSvc := service.NewRoomService(roomRepo)

// Handler + Router
h := handler.NewHandler(subjectSvc, facultySvc)
h := handler.NewHandler(subjectSvc, facultySvc, roomSvc)
strictHandler := api.NewStrictHandler(h, nil)
api.RegisterHandlers(router, strictHandler)

Expand Down
1 change: 1 addition & 0 deletions internal/database/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ func AutoMigrate(db *gorm.DB) error {
&SubjectFaculty{},
&SubjectEligibleAttribute{},
&SubjectRequirement{},
&Room{},
)
}
32 changes: 32 additions & 0 deletions internal/database/room.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package database

import (
"time"

"github.com/fun-dotto/academic-api/internal/domain"
)

type Room struct {
ID string `gorm:"type:uuid;primaryKey"`
Name string `gorm:"not null"`
Floor string `gorm:"not null"`

CreatedAt time.Time
UpdatedAt time.Time
}

func RoomToDomain(m Room) domain.Room {
return domain.Room{
ID: m.ID,
Name: m.Name,
Floor: domain.Floor(m.Floor),
}
}

func RoomFromDomain(d domain.Room) Room {
return Room{
ID: d.ID,
Name: d.Name,
Floor: string(d.Floor),
}
}
24 changes: 24 additions & 0 deletions internal/domain/room.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package domain

type Floor string

const (
FloorFloor1 Floor = "Floor1"
FloorFloor2 Floor = "Floor2"
FloorFloor3 Floor = "Floor3"
FloorFloor4 Floor = "Floor4"
FloorFloor5 Floor = "Floor5"
FloorFloor6 Floor = "Floor6"
FloorFloor7 Floor = "Floor7"
)

type Room struct {
ID string
Name string
Floor Floor
}

type RoomListFilter struct {
IDs []string
Floors []Floor
}
41 changes: 41 additions & 0 deletions internal/handler/converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,44 @@ func toDomainFacultyFromRequest(id string, req api.FacultyRequest) domain.Facult
Email: req.Email,
}
}

// Room

func buildRoomListFilter(params api.RoomsV1ListParams) domain.RoomListFilter {
filter := domain.RoomListFilter{}
if params.Ids != nil {
filter.IDs = *params.Ids
}
if params.Floor != nil {
floors := make([]domain.Floor, len(*params.Floor))
for i, f := range *params.Floor {
floors[i] = domain.Floor(f)
}
filter.Floors = floors
}
return filter
}

func roomToAPI(room domain.Room) api.Room {
return api.Room{
Id: room.ID,
Name: room.Name,
Floor: api.DottoFoundationV1Floor(room.Floor),
}
}

func roomsToAPI(rooms []domain.Room) []api.Room {
result := make([]api.Room, len(rooms))
for i, room := range rooms {
result[i] = roomToAPI(room)
}
return result
}

func toDomainRoomFromRequest(id string, req api.RoomRequest) domain.Room {
return domain.Room{
ID: id,
Name: req.Name,
Floor: domain.Floor(req.Floor),
}
}
11 changes: 11 additions & 0 deletions internal/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,28 @@ type facultyService interface {
Delete(ctx context.Context, id string) error
}

type roomService interface {
List(ctx context.Context, filter domain.RoomListFilter) ([]domain.Room, error)
GetByID(ctx context.Context, id string) (domain.Room, error)
Create(ctx context.Context, room domain.Room) (domain.Room, error)
Update(ctx context.Context, room domain.Room) (domain.Room, error)
Delete(ctx context.Context, id string) error
}

type Handler struct {
subjectSvc subjectService
facultySvc facultyService
roomSvc roomService
}

func NewHandler(
subjectSvc subjectService,
facultySvc facultyService,
roomSvc roomService,
) *Handler {
return &Handler{
subjectSvc: subjectSvc,
facultySvc: facultySvc,
roomSvc: roomSvc,
}
}
10 changes: 8 additions & 2 deletions internal/handler/room_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@ package handler

import (
"context"
"fmt"

api "github.com/fun-dotto/academic-api/generated"
"github.com/google/uuid"
)

func (h *Handler) RoomsV1Create(ctx context.Context, request api.RoomsV1CreateRequestObject) (api.RoomsV1CreateResponseObject, error) {
return nil, fmt.Errorf("not implemented")
id := uuid.New().String()
domainRoom := toDomainRoomFromRequest(id, *request.Body)
created, err := h.roomSvc.Create(ctx, domainRoom)
if err != nil {
return nil, err
}
return api.RoomsV1Create201JSONResponse{Room: roomToAPI(created)}, nil
}
11 changes: 9 additions & 2 deletions internal/handler/room_delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@ package handler

import (
"context"
"fmt"
"errors"

api "github.com/fun-dotto/academic-api/generated"
"gorm.io/gorm"
)

func (h *Handler) RoomsV1Delete(ctx context.Context, request api.RoomsV1DeleteRequestObject) (api.RoomsV1DeleteResponseObject, error) {
return nil, fmt.Errorf("not implemented")
if err := h.roomSvc.Delete(ctx, request.Id); err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return api.RoomsV1Delete404Response{}, nil
}
return nil, err
}
return api.RoomsV1Delete204Response{}, nil
}
12 changes: 10 additions & 2 deletions internal/handler/room_detail.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,19 @@ package handler

import (
"context"
"fmt"
"errors"

api "github.com/fun-dotto/academic-api/generated"
"gorm.io/gorm"
)

func (h *Handler) RoomsV1Detail(ctx context.Context, request api.RoomsV1DetailRequestObject) (api.RoomsV1DetailResponseObject, error) {
return nil, fmt.Errorf("not implemented")
room, err := h.roomSvc.GetByID(ctx, request.Id)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return api.RoomsV1Detail404Response{}, nil
}
return nil, err
}
return api.RoomsV1Detail200JSONResponse{Room: roomToAPI(room)}, nil
}
9 changes: 7 additions & 2 deletions internal/handler/room_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@ package handler

import (
"context"
"fmt"

api "github.com/fun-dotto/academic-api/generated"
)

func (h *Handler) RoomsV1List(ctx context.Context, request api.RoomsV1ListRequestObject) (api.RoomsV1ListResponseObject, error) {
return nil, fmt.Errorf("not implemented")
filter := buildRoomListFilter(request.Params)

rooms, err := h.roomSvc.List(ctx, filter)
if err != nil {
return nil, err
}
return api.RoomsV1List200JSONResponse{Rooms: roomsToAPI(rooms)}, nil
}
13 changes: 11 additions & 2 deletions internal/handler/room_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,20 @@ package handler

import (
"context"
"fmt"
"errors"

api "github.com/fun-dotto/academic-api/generated"
"gorm.io/gorm"
)

func (h *Handler) RoomsV1Update(ctx context.Context, request api.RoomsV1UpdateRequestObject) (api.RoomsV1UpdateResponseObject, error) {
return nil, fmt.Errorf("not implemented")
domainRoom := toDomainRoomFromRequest(request.Id, *request.Body)
updated, err := h.roomSvc.Update(ctx, domainRoom)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return api.RoomsV1Update404Response{}, nil
}
return nil, err
}
return api.RoomsV1Update200JSONResponse{Room: roomToAPI(updated)}, nil
}
91 changes: 91 additions & 0 deletions internal/repository/room.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package repository

import (
"context"
"errors"

"github.com/fun-dotto/academic-api/internal/database"
"github.com/fun-dotto/academic-api/internal/domain"
"gorm.io/gorm"
)

type RoomRepository struct {
db *gorm.DB
}

func NewRoomRepository(db *gorm.DB) *RoomRepository {
return &RoomRepository{db: db}
}

func (r *RoomRepository) List(ctx context.Context, filter domain.RoomListFilter) ([]domain.Room, error) {
var dbRooms []database.Room
query := r.db.WithContext(ctx)
if len(filter.IDs) > 0 {
query = query.Where("id IN ?", filter.IDs)
}
if len(filter.Floors) > 0 {
floors := make([]string, len(filter.Floors))
for i, f := range filter.Floors {
floors[i] = string(f)
}
query = query.Where("floor IN ?", floors)
}
if err := query.Find(&dbRooms).Error; err != nil {
return nil, err
}

domainRooms := make([]domain.Room, len(dbRooms))
for i, dbRoom := range dbRooms {
domainRooms[i] = database.RoomToDomain(dbRoom)
}

return domainRooms, nil
}

func (r *RoomRepository) GetByID(ctx context.Context, id string) (domain.Room, error) {
var dbRoom database.Room
if err := r.db.WithContext(ctx).First(&dbRoom, "id = ?", id).Error; err != nil {
return domain.Room{}, err
}
return database.RoomToDomain(dbRoom), nil
}

func (r *RoomRepository) Create(ctx context.Context, room domain.Room) (domain.Room, error) {
dbRoom := database.RoomFromDomain(room)
if err := r.db.WithContext(ctx).Create(&dbRoom).Error; err != nil {
return domain.Room{}, err
}
return database.RoomToDomain(dbRoom), nil
}

func (r *RoomRepository) Update(ctx context.Context, room domain.Room) (domain.Room, error) {
dbRoom := database.RoomFromDomain(room)
if err := r.db.WithContext(ctx).Model(&database.Room{}).Where("id = ?", room.ID).Updates(map[string]interface{}{
"name": dbRoom.Name,
"floor": dbRoom.Floor,
}).Error; err != nil {
return domain.Room{}, err
}
return r.GetByID(ctx, room.ID)
}

func (r *RoomRepository) Delete(ctx context.Context, id string) error {
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var room database.Room
if err := tx.Where("id = ?", id).First(&room).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
return err
}

result := tx.Where("id = ?", id).Delete(&database.Room{})
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
return gorm.ErrRecordNotFound
}
return nil
})
}
43 changes: 43 additions & 0 deletions internal/service/room.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package service

import (
"context"

"github.com/fun-dotto/academic-api/internal/domain"
)

type roomRepository interface {
List(ctx context.Context, filter domain.RoomListFilter) ([]domain.Room, error)
GetByID(ctx context.Context, id string) (domain.Room, error)
Create(ctx context.Context, room domain.Room) (domain.Room, error)
Update(ctx context.Context, room domain.Room) (domain.Room, error)
Delete(ctx context.Context, id string) error
}

type RoomService struct {
repo roomRepository
}

func NewRoomService(repo roomRepository) *RoomService {
return &RoomService{repo: repo}
}

func (s *RoomService) List(ctx context.Context, filter domain.RoomListFilter) ([]domain.Room, error) {
return s.repo.List(ctx, filter)
}

func (s *RoomService) GetByID(ctx context.Context, id string) (domain.Room, error) {
return s.repo.GetByID(ctx, id)
}

func (s *RoomService) Create(ctx context.Context, room domain.Room) (domain.Room, error) {
return s.repo.Create(ctx, room)
}

func (s *RoomService) Update(ctx context.Context, room domain.Room) (domain.Room, error) {
return s.repo.Update(ctx, room)
}

func (s *RoomService) Delete(ctx context.Context, id string) error {
return s.repo.Delete(ctx, id)
}
Loading