Skip to content

Commit fbdccbe

Browse files
committed
chore: refactored application submission handlers, services and repository
1 parent 43a8e6c commit fbdccbe

10 files changed

Lines changed: 432 additions & 413 deletions

File tree

apps/api/cmd/api/main.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,13 @@ func main() {
6363
eventInterestRepo := repository.NewEventInterestRepository(database)
6464
eventRepo := repository.NewEventRespository(database)
6565
applicationRepo := repository.NewApplicationRepository(database)
66-
// applicationRepo := repository.NewApplicationRepository(database)
6766

6867
// Injections into services
6968
authService := services.NewAuthService(userRepo, accountRepo, sessionRepo, txm, client, logger, &cfg.Auth)
7069
eventInterestService := services.NewEventInterestService(eventInterestRepo, logger)
7170
eventService := services.NewEventService(eventRepo, userRepo, logger)
7271
emailService := services.NewEmailService(taskQueueClient, logger)
73-
applicationService := services.NewApplicationService(applicationRepo, r2Client)
72+
applicationService := services.NewApplicationService(applicationRepo, txm, r2Client, &cfg.CoreBuckets, logger)
7473

7574
// Injections into handlers
7675
apiHandlers := handlers.NewHandlers(authService, eventInterestService, eventService, emailService, applicationService, cfg, logger)

apps/api/internal/api/api.go

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -85,19 +85,12 @@ func (api *API) setupRoutes(mw *mw.Middleware) {
8585
r.Get("/", api.Handlers.Event.GetEventByID)
8686
r.Post("/interest", api.Handlers.EventInterest.AddEmailToEvent)
8787

88-
// This route below is not preferred over using path parameters
89-
// r.With(mw.Auth.RequireAuth).Get("/application", api.Handlers.Event.GetApplicationByUserAndEventID)
90-
91-
r.Route("/applications", func(r chi.Router) {
88+
r.Route("/application", func(r chi.Router) {
9289
r.Use(mw.Auth.RequireAuth)
9390

94-
r.Post("/submit", api.Handlers.Event.SubmitApplication)
95-
r.Post("/save", api.Handlers.Event.SaveApplication)
96-
97-
r.Route("/{applicationId}", func(r chi.Router) {
98-
// Where we will put application and event specific routes
99-
})
100-
// r.Post("/upload", api.Handlers.Event.UploadAttachment)
91+
r.Get("/", api.Handlers.Application.GetApplicationByUserAndEventID)
92+
r.Post("/submit", api.Handlers.Application.SubmitApplication)
93+
r.Post("/save", api.Handlers.Application.SaveApplication)
10194
})
10295
})
10396
})

apps/api/internal/api/handlers/application.go

Lines changed: 209 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,23 @@
11
package handlers
22

3-
import "github.com/swamphacks/core/apps/api/internal/services"
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"errors"
7+
"io"
8+
"net/http"
9+
"strconv"
10+
11+
"github.com/go-chi/chi/v5"
12+
"github.com/go-playground/validator/v10"
13+
"github.com/google/uuid"
14+
"github.com/rs/zerolog/log"
15+
res "github.com/swamphacks/core/apps/api/internal/api/response"
16+
"github.com/swamphacks/core/apps/api/internal/ctxutils"
17+
"github.com/swamphacks/core/apps/api/internal/db/repository"
18+
"github.com/swamphacks/core/apps/api/internal/db/sqlc"
19+
"github.com/swamphacks/core/apps/api/internal/services"
20+
)
421

522
type ApplicationHandler struct {
623
appService *services.ApplicationService
@@ -11,3 +28,194 @@ func NewApplicationHandler(appService *services.ApplicationService) *Application
1128
appService: appService,
1229
}
1330
}
31+
32+
func getEventID(w http.ResponseWriter, r *http.Request) (uuid.UUID, error) {
33+
eventIdStr := chi.URLParam(r, "eventId")
34+
if eventIdStr == "" {
35+
err := errors.New("missing_event_id")
36+
errMsg := "The event ID is missing from the URL!"
37+
38+
log.Err(err).Msg(errMsg)
39+
40+
res.SendError(w, http.StatusBadRequest,
41+
res.NewError(err.Error(), errMsg))
42+
43+
return uuid.Nil, err
44+
}
45+
46+
eventId, err := uuid.Parse(eventIdStr)
47+
if err != nil {
48+
err = errors.New("invalid_event_id")
49+
errMsg := "The event ID is not a valid UUID"
50+
51+
log.Err(err).Msg(errMsg)
52+
53+
res.SendError(w, http.StatusBadRequest,
54+
res.NewError(err.Error(), errMsg))
55+
56+
return uuid.Nil, err
57+
}
58+
59+
return eventId, nil
60+
}
61+
62+
func (h *ApplicationHandler) GetApplicationByUserAndEventID(w http.ResponseWriter, r *http.Request) {
63+
eventId, err := getEventID(w, r)
64+
65+
if err != nil {
66+
return
67+
}
68+
69+
userIdPtr := ctxutils.GetUserIdFromCtx(r.Context())
70+
71+
if userIdPtr == nil {
72+
return
73+
}
74+
75+
userId := *userIdPtr
76+
77+
params := sqlc.GetApplicationByUserAndEventIDParams{
78+
UserID: userId,
79+
EventID: eventId,
80+
}
81+
82+
application, err := h.appService.GetApplicationByUserAndEventID(r.Context(), params)
83+
84+
if err != nil {
85+
if err == repository.ErrApplicationNotFound {
86+
params := sqlc.CreateApplicationParams{
87+
UserID: userId,
88+
EventID: eventId,
89+
}
90+
newApplication, err := h.appService.CreateApplication(r.Context(), params)
91+
if err != nil {
92+
res.SendError(w, http.StatusBadRequest, res.NewError("create_application_error", "can't create application"))
93+
} else {
94+
res.Send(w, http.StatusOK, newApplication)
95+
}
96+
res.SendError(w, http.StatusBadRequest, res.NewError("application_not_found", "can't find application"))
97+
} else {
98+
res.SendError(w, http.StatusBadRequest, res.NewError("get_application_error", "error retrieving application"))
99+
}
100+
101+
return
102+
}
103+
104+
res.Send(w, http.StatusOK, application)
105+
}
106+
107+
func (h *ApplicationHandler) SubmitApplication(w http.ResponseWriter, r *http.Request) {
108+
// Parse multipart form (10 MB max memory)
109+
err := r.ParseMultipartForm(10 << 20)
110+
if err != nil {
111+
http.Error(w, "Failed to parse form: "+err.Error(), http.StatusBadRequest)
112+
return
113+
}
114+
115+
var submission services.ApplicationSubmissionFields
116+
117+
// Map form values
118+
submission.FirstName = r.FormValue("firstName")
119+
submission.LastName = r.FormValue("lastName")
120+
121+
if ageStr := r.FormValue("age"); ageStr != "" {
122+
if age, err := strconv.Atoi(ageStr); err == nil {
123+
submission.Age = age
124+
}
125+
}
126+
127+
submission.Phone = r.FormValue("phone")
128+
submission.PreferredEmail = r.FormValue("preferredEmail")
129+
submission.UniversityEmail = r.FormValue("universityEmail")
130+
submission.Linkedin = r.FormValue("linkedin")
131+
submission.Github = r.FormValue("github")
132+
133+
if ageCertStr := r.FormValue("ageCertification"); ageCertStr != "" {
134+
submission.AgeCertification = (ageCertStr == "true" || ageCertStr == "1")
135+
}
136+
137+
submission.School = r.FormValue("school")
138+
submission.Level = r.FormValue("level")
139+
submission.Year = r.FormValue("year")
140+
submission.GraduationYear = r.FormValue("graduationYear")
141+
submission.Majors = r.FormValue("majors")
142+
submission.Minors = r.FormValue("minors")
143+
submission.Experience = r.FormValue("experience")
144+
submission.ProjectExperience = r.FormValue("projectExperience")
145+
submission.ShirtSize = r.FormValue("shirtSize")
146+
submission.Essay1 = r.FormValue("essay1")
147+
submission.Essay2 = r.FormValue("essay2")
148+
submission.Referral = r.FormValue("referral")
149+
submission.PictureConsent = r.FormValue("pictureConsent")
150+
submission.InpersonAcknowledgement = r.FormValue("inpersonAcknowledgement")
151+
submission.AgreeToConduct = r.FormValue("agreeToConduct")
152+
submission.InfoShareAuthorization = r.FormValue("infoShareAuthorization")
153+
submission.AgreeToMLHEmails = r.FormValue("agreeToMLHEmails")
154+
155+
resumeFile, _, err := r.FormFile("resume[]")
156+
if err == nil {
157+
defer resumeFile.Close()
158+
}
159+
160+
resumeFileBuffer := bytes.NewBuffer(nil)
161+
162+
if _, err := io.Copy(resumeFileBuffer, resumeFile); err != nil {
163+
return
164+
}
165+
166+
validate := validator.New()
167+
if err := validate.Struct(submission); err != nil {
168+
res.SendError(w, http.StatusBadRequest, res.NewError("invalid_request", err.Error()))
169+
return
170+
}
171+
172+
eventId, err := getEventID(w, r)
173+
174+
if err == nil {
175+
w.WriteHeader(http.StatusBadRequest)
176+
return
177+
}
178+
179+
userIdPtr := ctxutils.GetUserIdFromCtx(r.Context())
180+
181+
if userIdPtr == nil {
182+
w.WriteHeader(http.StatusBadRequest)
183+
return
184+
}
185+
186+
userId := *userIdPtr
187+
188+
h.appService.SubmitApplication(r.Context(), submission, resumeFileBuffer.Bytes(), userId, eventId)
189+
190+
w.WriteHeader(http.StatusOK)
191+
}
192+
193+
func (h *ApplicationHandler) SaveApplication(w http.ResponseWriter, r *http.Request) {
194+
var data any
195+
196+
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
197+
res.SendError(w, http.StatusBadRequest, res.NewError("invalid_form_data", "Something went wrong while parsing form submission"))
198+
return
199+
}
200+
201+
eventId, err := getEventID(w, r)
202+
203+
if err != nil {
204+
return
205+
}
206+
207+
userIdPtr := ctxutils.GetUserIdFromCtx(r.Context())
208+
209+
if userIdPtr == nil {
210+
return
211+
}
212+
213+
userId := *userIdPtr
214+
215+
h.appService.SaveApplication(r.Context(), data, sqlc.UpdateApplicationParams{
216+
UserID: userId,
217+
EventID: eventId,
218+
})
219+
220+
w.WriteHeader(http.StatusOK)
221+
}

0 commit comments

Comments
 (0)