From 198890a20575bddb97b4e7ec0eb43f1af52043fc Mon Sep 17 00:00:00 2001 From: josh Date: Thu, 23 Apr 2026 20:53:15 -0700 Subject: [PATCH 01/10] add comment --- internal/api/services/board.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/api/services/board.go b/internal/api/services/board.go index d9bf2d07..0f2b7632 100644 --- a/internal/api/services/board.go +++ b/internal/api/services/board.go @@ -73,6 +73,7 @@ func (s *BoardService) ListOfficers(ctx context.Context, filters ...any) ([]doma return nil, err } + // TODO: these filters should probably operate on domain models result := officers for _, filter := range filters { if officerFilter, ok := filter.(OfficerFilter); ok { From 7aac9579b64aa6a6f0ab1bd7e78e4f46fab02524 Mon Sep 17 00:00:00 2001 From: josh Date: Thu, 23 Apr 2026 20:57:32 -0700 Subject: [PATCH 02/10] update service layer --- internal/api/services/event.go | 37 +++++++++++++++++------------ internal/api/store/events_mapper.go | 1 - internal/domain/event.go | 1 - 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/internal/api/services/event.go b/internal/api/services/event.go index a9692ef5..39d9ac99 100644 --- a/internal/api/services/event.go +++ b/internal/api/services/event.go @@ -4,11 +4,13 @@ package services import ( "context" + "github.com/acmcsufoss/api.acmcsuf.com/internal/api/store" "github.com/acmcsufoss/api.acmcsuf.com/internal/api/store/dbmodels" + "github.com/acmcsufoss/api.acmcsuf.com/internal/domain" ) type EventsServicer interface { - Service[dbmodels.Event, string, dbmodels.CreateEventParams, dbmodels.UpdateEventParams] + Service[domain.Event, string, domain.Event, domain.UpdateEvent] } type EventsService struct { @@ -22,24 +24,24 @@ func NewEventsService(q *dbmodels.Queries) *EventsService { return &EventsService{q: q} } -func (s *EventsService) Get(ctx context.Context, uuid string) (dbmodels.Event, error) { +func (s *EventsService) Get(ctx context.Context, uuid string) (domain.Event, error) { event, err := s.q.GetEvent(ctx, uuid) if err != nil { - return dbmodels.Event{}, err + return domain.Event{}, err } - return event, nil + return store.EventDBToDomain(event), nil } -func (s *EventsService) Create(ctx context.Context, params dbmodels.CreateEventParams) error { - if err := s.q.CreateEvent(ctx, params); err != nil { +func (s *EventsService) Create(ctx context.Context, params domain.Event) error { + dbParams := store.EventDomainToDB(params) + if err := s.q.CreateEvent(ctx, dbParams); err != nil { return err } return nil } -// TODO: Move filters to their own file or module or something type EventFilter interface { - Apply(events []dbmodels.Event) []dbmodels.Event + Apply(events []domain.Event) []domain.Event } type HostFilter struct { @@ -49,12 +51,12 @@ type HostFilter struct { // Ensure HostFilter implements EventFilter var _ EventFilter = (*HostFilter)(nil) -func (f *HostFilter) Apply(events []dbmodels.Event) []dbmodels.Event { +func (f *HostFilter) Apply(events []domain.Event) []domain.Event { if f.Host == "" { return events } - filtered := make([]dbmodels.Event, 0) + filtered := make([]domain.Event, 0) for _, event := range events { if event.Host == f.Host { filtered = append(filtered, event) @@ -63,13 +65,17 @@ func (f *HostFilter) Apply(events []dbmodels.Event) []dbmodels.Event { return filtered } -func (s *EventsService) List(ctx context.Context, filters ...any) ([]dbmodels.Event, error) { +func (s *EventsService) List(ctx context.Context, filters ...any) ([]domain.Event, error) { events, err := s.q.GetEvents(ctx) if err != nil { return nil, err } + domainEvents := make([]domain.Event, len(events)) + for i, event := range events { + domainEvents[i] = store.EventDBToDomain(event) + } - result := events + result := domainEvents for _, filter := range filters { if eventFilter, ok := filter.(EventFilter); ok { result = eventFilter.Apply(result) @@ -79,9 +85,10 @@ func (s *EventsService) List(ctx context.Context, filters ...any) ([]dbmodels.Ev return result, nil } -func (s *EventsService) Update(ctx context.Context, uuid string, params dbmodels.UpdateEventParams) error { - params.Uuid = uuid - if err := s.q.UpdateEvent(ctx, params); err != nil { +func (s *EventsService) Update(ctx context.Context, uuid string, params domain.UpdateEvent) error { + dbParams := store.UpdateEventDomainToDB(params) + dbParams.Uuid = uuid + if err := s.q.UpdateEvent(ctx, dbParams); err != nil { return err } return nil diff --git a/internal/api/store/events_mapper.go b/internal/api/store/events_mapper.go index 2122c77d..35d5987d 100644 --- a/internal/api/store/events_mapper.go +++ b/internal/api/store/events_mapper.go @@ -20,7 +20,6 @@ func EventDomainToDB(event domain.Event) dbmodels.CreateEventParams { func UpdateEventDomainToDB(event domain.UpdateEvent) dbmodels.UpdateEventParams { return dbmodels.UpdateEventParams{ - Uuid: event.Uuid, Location: stringToNullString(event.Location), StartAt: timeToNullInt64(event.StartAt), EndAt: timeToNullInt64(event.EndAt), diff --git a/internal/domain/event.go b/internal/domain/event.go index d8591374..705db86a 100644 --- a/internal/domain/event.go +++ b/internal/domain/event.go @@ -14,7 +14,6 @@ type Event struct { } type UpdateEvent struct { - Uuid string Location *string StartAt *time.Time EndAt *time.Time From c2d85c0ea7d7bc7a4dc447173ff40c146c5b79f9 Mon Sep 17 00:00:00 2001 From: josh Date: Thu, 23 Apr 2026 21:56:46 -0700 Subject: [PATCH 03/10] update http layer --- internal/api/handlers/event.go | 40 +++++++++++++--------------------- internal/dto/event.go | 24 +++++++++++++++----- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/internal/api/handlers/event.go b/internal/api/handlers/event.go index 5f8d5cd7..5a9d56fd 100644 --- a/internal/api/handlers/event.go +++ b/internal/api/handlers/event.go @@ -7,7 +7,7 @@ import ( "net/http" "github.com/acmcsufoss/api.acmcsuf.com/internal/api/services" - "github.com/acmcsufoss/api.acmcsuf.com/internal/api/store/dbmodels" + "github.com/acmcsufoss/api.acmcsuf.com/internal/dto" "github.com/gin-gonic/gin" ) @@ -29,7 +29,7 @@ func NewEventHandler(eventService services.EventsServicer) *EventsHandler { // @Accept json // @Produce json // @Param id path string true "Event ID" -// @Success 200 {object} dbmodels.Event "Event details" +// @Success 200 {object} dto.Event "Event details" // @Failure 404 {object} map[string]string // @Failure 500 {object} map[string]string // @Router /v1/events/{id} [get] @@ -53,7 +53,7 @@ func (h *EventsHandler) GetEvent(c *gin.Context) { return } - c.JSON(http.StatusOK, event) + c.JSON(http.StatusOK, dto.EventDomainToDto(&event)) } // CreateEvent godoc @@ -63,39 +63,33 @@ func (h *EventsHandler) GetEvent(c *gin.Context) { // @Tags Events // @Accept json // @Produce json -// @Param body body dbmodels.CreateEventParams true "Event data" +// @Param body body dto.Event true "Event data" // @Success 200 {object} map[string]interface{} "Success message with UUID" // @Failure 400 {object} map[string]string // @Failure 500 {object} map[string]string // @Router /v1/events [post] func (h *EventsHandler) CreateEvent(c *gin.Context) { ctx := c.Request.Context() - var params dbmodels.CreateEventParams + var body dto.Event - if err := c.ShouldBindJSON(¶ms); err != nil { + if err := c.ShouldBindJSON(&body); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "Invalid request body. " + err.Error(), }) return } - if params.Location == "" || params.Host == "" { - c.JSON(http.StatusBadRequest, gin.H{ - "error": "Location and Host are required fields", - }) - return - } - - err := h.eventsService.Create(ctx, params) + err := h.eventsService.Create(ctx, body.ToDomain()) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "error": "Failed to create event. " + err.Error(), }) return } + // TODO: Get event and return it here. Issue is that the generated sqlc func + // isn't returning the resource, so the service isn't either. c.JSON(http.StatusOK, gin.H{ "message": "Event created successfully", - "uuid": params.Uuid, }) } @@ -107,7 +101,7 @@ func (h *EventsHandler) CreateEvent(c *gin.Context) { // @Accept json // @Produce json // @Param host query string false "Filter by host" -// @Success 200 {array} dbmodels.Event "List of events" +// @Success 200 {array} dto.Event "List of events" // @Failure 500 {object} map[string]string // @Router /v1/events [get] func (h *EventsHandler) GetEvents(c *gin.Context) { @@ -137,7 +131,7 @@ func (h *EventsHandler) GetEvents(c *gin.Context) { // @Accept json // @Produce json // @Param id path string true "Event ID" -// @Param body body dbmodels.UpdateEventParams true "Updated event data" +// @Param body body dto.UpdateEvent true "Updated event data" // @Success 200 {object} map[string]string "Success message" // @Failure 400 {object} map[string]string // @Failure 404 {object} map[string]string @@ -145,27 +139,23 @@ func (h *EventsHandler) GetEvents(c *gin.Context) { // @Router /v1/events/{id} [put] func (h *EventsHandler) UpdateEvent(c *gin.Context) { ctx := c.Request.Context() - var params dbmodels.UpdateEventParams + var body dto.UpdateEvent id := c.Param("id") - - fmt.Println("UPDATE:", id) - if err := c.ShouldBindJSON(¶ms); err != nil { + if err := c.ShouldBindJSON(&body); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "Invalid request body. " + err.Error(), }) return } - if err := h.eventsService.Update(ctx, id, params); err != nil { - error := fmt.Sprint("Failed to update event: ", err, " | ", ctx, " | ", id, " | ", params) + if err := h.eventsService.Update(ctx, id, body.ToDomain()); err != nil { c.JSON(http.StatusInternalServerError, gin.H{ - "error": error, + "error": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "message": "Event updated successfully", - "uuid": params.Uuid, }) } diff --git a/internal/dto/event.go b/internal/dto/event.go index c808f398..8f637cf7 100644 --- a/internal/dto/event.go +++ b/internal/dto/event.go @@ -1,6 +1,8 @@ package dto import ( + "time" + "github.com/acmcsufoss/api.acmcsuf.com/internal/domain" "github.com/acmcsufoss/api.acmcsuf.com/utils" ) @@ -14,11 +16,7 @@ type Event struct { Host string `json:"host"` } -func (e *Event) ToDomain() domain.Event { - if e == nil { - return domain.Event{} - } - +func (e Event) ToDomain() domain.Event { return domain.Event{ Uuid: e.Uuid, Location: e.Location, @@ -41,10 +39,24 @@ func EventDomainToDto(e *domain.Event) Event { } type UpdateEvent struct { - Uuid string `json:"uuid"` Location *string `json:"location"` StartAt *int64 `json:"start_at"` EndAt *int64 `json:"end_at"` IsAllDay *bool `json:"is_all_day"` Host *string `json:"host"` } + +func (e UpdateEvent) ToDomain() domain.UpdateEvent { + d := domain.UpdateEvent{ + Location: e.Location, + IsAllDay: e.IsAllDay, + Host: e.Host, + } + if e.StartAt != nil { + *d.StartAt = utils.UnixToTime(*e.StartAt) + } + if e.EndAt != nil { + *d.EndAt = utils.UnixToTime(*e.EndAt) + } + return d +} From ebc2f3eae9437505b10a9cab13bd3e39d0a45164 Mon Sep 17 00:00:00 2001 From: josh Date: Thu, 23 Apr 2026 21:57:33 -0700 Subject: [PATCH 04/10] gen swag docs --- internal/api/docs/docs.go | 136 ++++++++++++--------------------- internal/api/docs/swagger.json | 136 ++++++++++++--------------------- internal/api/docs/swagger.yaml | 93 ++++++++-------------- 3 files changed, 126 insertions(+), 239 deletions(-) diff --git a/internal/api/docs/docs.go b/internal/api/docs/docs.go index 20f97c5f..9e89b96a 100644 --- a/internal/api/docs/docs.go +++ b/internal/api/docs/docs.go @@ -1055,7 +1055,7 @@ const docTemplate = `{ "schema": { "type": "array", "items": { - "$ref": "#/definitions/dbmodels.Event" + "$ref": "#/definitions/dto.Event" } } }, @@ -1089,7 +1089,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/dbmodels.CreateEventParams" + "$ref": "#/definitions/dto.Event" } } ], @@ -1148,7 +1148,7 @@ const docTemplate = `{ "200": { "description": "Event details", "schema": { - "$ref": "#/definitions/dbmodels.Event" + "$ref": "#/definitions/dto.Event" } }, "404": { @@ -1197,7 +1197,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/dbmodels.UpdateEventParams" + "$ref": "#/definitions/dto.UpdateEvent" } } ], @@ -1294,31 +1294,6 @@ const docTemplate = `{ } }, "definitions": { - "dbmodels.CreateEventParams": { - "type": "object", - "properties": { - "endAt": { - "type": "integer", - "format": "int64" - }, - "host": { - "type": "string" - }, - "isAllDay": { - "type": "boolean" - }, - "location": { - "type": "string" - }, - "startAt": { - "type": "integer", - "format": "int64" - }, - "uuid": { - "type": "string" - } - } - }, "dbmodels.CreatePositionParams": { "type": "object", "properties": { @@ -1376,31 +1351,6 @@ const docTemplate = `{ } } }, - "dbmodels.Event": { - "type": "object", - "properties": { - "endAt": { - "type": "integer", - "format": "int64" - }, - "host": { - "type": "string" - }, - "isAllDay": { - "type": "boolean" - }, - "location": { - "type": "string" - }, - "startAt": { - "type": "integer", - "format": "int64" - }, - "uuid": { - "type": "string" - } - } - }, "dbmodels.Position": { "type": "object", "properties": { @@ -1443,29 +1393,6 @@ const docTemplate = `{ } } }, - "dbmodels.UpdateEventParams": { - "type": "object", - "properties": { - "endAt": { - "$ref": "#/definitions/sql.NullInt64" - }, - "host": { - "$ref": "#/definitions/sql.NullString" - }, - "isAllDay": { - "$ref": "#/definitions/sql.NullBool" - }, - "location": { - "$ref": "#/definitions/sql.NullString" - }, - "startAt": { - "$ref": "#/definitions/sql.NullInt64" - }, - "uuid": { - "type": "string" - } - } - }, "dbmodels.UpdatePositionParams": { "type": "object", "properties": { @@ -1528,6 +1455,29 @@ const docTemplate = `{ } } }, + "dto.Event": { + "type": "object", + "properties": { + "end_at": { + "type": "integer" + }, + "host": { + "type": "string" + }, + "is_all_day": { + "type": "boolean" + }, + "location": { + "type": "string" + }, + "start_at": { + "type": "integer" + }, + "uuid": { + "type": "string" + } + } + }, "dto.Officer": { "type": "object", "properties": { @@ -1568,6 +1518,26 @@ const docTemplate = `{ } } }, + "dto.UpdateEvent": { + "type": "object", + "properties": { + "end_at": { + "type": "integer" + }, + "host": { + "type": "string" + }, + "is_all_day": { + "type": "boolean" + }, + "location": { + "type": "string" + }, + "start_at": { + "type": "integer" + } + } + }, "dto.UpdateOfficer": { "type": "object", "properties": { @@ -1585,18 +1555,6 @@ const docTemplate = `{ } } }, - "sql.NullBool": { - "type": "object", - "properties": { - "bool": { - "type": "boolean" - }, - "valid": { - "description": "Valid is true if Bool is not NULL", - "type": "boolean" - } - } - }, "sql.NullInt64": { "type": "object", "properties": { diff --git a/internal/api/docs/swagger.json b/internal/api/docs/swagger.json index 7063bc9b..604f3be5 100644 --- a/internal/api/docs/swagger.json +++ b/internal/api/docs/swagger.json @@ -1044,7 +1044,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/definitions/dbmodels.Event" + "$ref": "#/definitions/dto.Event" } } }, @@ -1078,7 +1078,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/dbmodels.CreateEventParams" + "$ref": "#/definitions/dto.Event" } } ], @@ -1137,7 +1137,7 @@ "200": { "description": "Event details", "schema": { - "$ref": "#/definitions/dbmodels.Event" + "$ref": "#/definitions/dto.Event" } }, "404": { @@ -1186,7 +1186,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/dbmodels.UpdateEventParams" + "$ref": "#/definitions/dto.UpdateEvent" } } ], @@ -1283,31 +1283,6 @@ } }, "definitions": { - "dbmodels.CreateEventParams": { - "type": "object", - "properties": { - "endAt": { - "type": "integer", - "format": "int64" - }, - "host": { - "type": "string" - }, - "isAllDay": { - "type": "boolean" - }, - "location": { - "type": "string" - }, - "startAt": { - "type": "integer", - "format": "int64" - }, - "uuid": { - "type": "string" - } - } - }, "dbmodels.CreatePositionParams": { "type": "object", "properties": { @@ -1365,31 +1340,6 @@ } } }, - "dbmodels.Event": { - "type": "object", - "properties": { - "endAt": { - "type": "integer", - "format": "int64" - }, - "host": { - "type": "string" - }, - "isAllDay": { - "type": "boolean" - }, - "location": { - "type": "string" - }, - "startAt": { - "type": "integer", - "format": "int64" - }, - "uuid": { - "type": "string" - } - } - }, "dbmodels.Position": { "type": "object", "properties": { @@ -1432,29 +1382,6 @@ } } }, - "dbmodels.UpdateEventParams": { - "type": "object", - "properties": { - "endAt": { - "$ref": "#/definitions/sql.NullInt64" - }, - "host": { - "$ref": "#/definitions/sql.NullString" - }, - "isAllDay": { - "$ref": "#/definitions/sql.NullBool" - }, - "location": { - "$ref": "#/definitions/sql.NullString" - }, - "startAt": { - "$ref": "#/definitions/sql.NullInt64" - }, - "uuid": { - "type": "string" - } - } - }, "dbmodels.UpdatePositionParams": { "type": "object", "properties": { @@ -1517,6 +1444,29 @@ } } }, + "dto.Event": { + "type": "object", + "properties": { + "end_at": { + "type": "integer" + }, + "host": { + "type": "string" + }, + "is_all_day": { + "type": "boolean" + }, + "location": { + "type": "string" + }, + "start_at": { + "type": "integer" + }, + "uuid": { + "type": "string" + } + } + }, "dto.Officer": { "type": "object", "properties": { @@ -1557,6 +1507,26 @@ } } }, + "dto.UpdateEvent": { + "type": "object", + "properties": { + "end_at": { + "type": "integer" + }, + "host": { + "type": "string" + }, + "is_all_day": { + "type": "boolean" + }, + "location": { + "type": "string" + }, + "start_at": { + "type": "integer" + } + } + }, "dto.UpdateOfficer": { "type": "object", "properties": { @@ -1574,18 +1544,6 @@ } } }, - "sql.NullBool": { - "type": "object", - "properties": { - "bool": { - "type": "boolean" - }, - "valid": { - "description": "Valid is true if Bool is not NULL", - "type": "boolean" - } - } - }, "sql.NullInt64": { "type": "object", "properties": { diff --git a/internal/api/docs/swagger.yaml b/internal/api/docs/swagger.yaml index f8ab2102..3b2e7c79 100644 --- a/internal/api/docs/swagger.yaml +++ b/internal/api/docs/swagger.yaml @@ -1,21 +1,4 @@ definitions: - dbmodels.CreateEventParams: - properties: - endAt: - format: int64 - type: integer - host: - type: string - isAllDay: - type: boolean - location: - type: string - startAt: - format: int64 - type: integer - uuid: - type: string - type: object dbmodels.CreatePositionParams: properties: fullName: @@ -54,23 +37,6 @@ definitions: format: int64 type: integer type: object - dbmodels.Event: - properties: - endAt: - format: int64 - type: integer - host: - type: string - isAllDay: - type: boolean - location: - type: string - startAt: - format: int64 - type: integer - uuid: - type: string - type: object dbmodels.Position: properties: fullName: @@ -99,21 +65,6 @@ definitions: title: $ref: '#/definitions/sql.NullString' type: object - dbmodels.UpdateEventParams: - properties: - endAt: - $ref: '#/definitions/sql.NullInt64' - host: - $ref: '#/definitions/sql.NullString' - isAllDay: - $ref: '#/definitions/sql.NullBool' - location: - $ref: '#/definitions/sql.NullString' - startAt: - $ref: '#/definitions/sql.NullInt64' - uuid: - type: string - type: object dbmodels.UpdatePositionParams: properties: fullName: @@ -155,6 +106,21 @@ definitions: visibility: type: string type: object + dto.Event: + properties: + end_at: + type: integer + host: + type: string + is_all_day: + type: boolean + location: + type: string + start_at: + type: integer + uuid: + type: string + type: object dto.Officer: properties: discord: @@ -181,6 +147,19 @@ definitions: visibility: type: string type: object + dto.UpdateEvent: + properties: + end_at: + type: integer + host: + type: string + is_all_day: + type: boolean + location: + type: string + start_at: + type: integer + type: object dto.UpdateOfficer: properties: discord: @@ -192,14 +171,6 @@ definitions: picture: type: string type: object - sql.NullBool: - properties: - bool: - type: boolean - valid: - description: Valid is true if Bool is not NULL - type: boolean - type: object sql.NullInt64: properties: int64: @@ -910,7 +881,7 @@ paths: description: List of events schema: items: - $ref: '#/definitions/dbmodels.Event' + $ref: '#/definitions/dto.Event' type: array "500": description: Internal Server Error @@ -931,7 +902,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/dbmodels.CreateEventParams' + $ref: '#/definitions/dto.Event' produces: - application/json responses: @@ -1006,7 +977,7 @@ paths: "200": description: Event details schema: - $ref: '#/definitions/dbmodels.Event' + $ref: '#/definitions/dto.Event' "404": description: Not Found schema: @@ -1037,7 +1008,7 @@ paths: name: body required: true schema: - $ref: '#/definitions/dbmodels.UpdateEventParams' + $ref: '#/definitions/dto.UpdateEvent' produces: - application/json responses: From 29657088ed24195b5ee17b25389a3c4ef76150e3 Mon Sep 17 00:00:00 2001 From: josh Date: Thu, 23 Apr 2026 21:59:11 -0700 Subject: [PATCH 05/10] rm unused imports --- internal/api/handlers/event.go | 1 - internal/dto/event.go | 2 -- 2 files changed, 3 deletions(-) diff --git a/internal/api/handlers/event.go b/internal/api/handlers/event.go index 5a9d56fd..c5650708 100644 --- a/internal/api/handlers/event.go +++ b/internal/api/handlers/event.go @@ -3,7 +3,6 @@ package handlers import ( - "fmt" "net/http" "github.com/acmcsufoss/api.acmcsuf.com/internal/api/services" diff --git a/internal/dto/event.go b/internal/dto/event.go index 8f637cf7..5fab41f9 100644 --- a/internal/dto/event.go +++ b/internal/dto/event.go @@ -1,8 +1,6 @@ package dto import ( - "time" - "github.com/acmcsufoss/api.acmcsuf.com/internal/domain" "github.com/acmcsufoss/api.acmcsuf.com/utils" ) From a982e0dcfecd753bdaaeed78087e09881326d31d Mon Sep 17 00:00:00 2001 From: josh Date: Thu, 23 Apr 2026 22:05:32 -0700 Subject: [PATCH 06/10] update fixtures --- fixtures/event_update_full.json | 15 +++------------ fixtures/event_update_location.json | 5 +---- fixtures/event_update_partial.json | 15 +++------------ 3 files changed, 7 insertions(+), 28 deletions(-) diff --git a/fixtures/event_update_full.json b/fixtures/event_update_full.json index 24cf154a..a0f53868 100644 --- a/fixtures/event_update_full.json +++ b/fixtures/event_update_full.json @@ -1,17 +1,8 @@ { "uuid": "workshop-intro-to-git", - "location": { - "string": "Virtual - Discord", - "valid": true - }, + "location": "Virtual - Discord", "start_at": 1712865600000, "end_at": 1712872800000, - "is_all_day": { - "bool": false, - "valid": true - }, - "host": { - "string": "ACM AI", - "valid": true - } + "is_all_day": false, + "host": "ACM AI" } diff --git a/fixtures/event_update_location.json b/fixtures/event_update_location.json index f02e8378..da52c026 100644 --- a/fixtures/event_update_location.json +++ b/fixtures/event_update_location.json @@ -1,7 +1,4 @@ { "uuid": "workshop-intro-to-git", - "location": { - "string": "CS-101", - "valid": true - } + "location": "CS-101" } diff --git a/fixtures/event_update_partial.json b/fixtures/event_update_partial.json index 1f1cf5bf..afa5d011 100644 --- a/fixtures/event_update_partial.json +++ b/fixtures/event_update_partial.json @@ -1,17 +1,8 @@ { "uuid": "event3", - "location": { - "string": "Updated Location", - "valid": true - }, + "location": "Updated Location", "start_at": 1712851200000, "end_at": 1712851200000, - "is_all_day": { - "bool": true, - "valid": true - }, - "host": { - "string": "Updated Host", - "valid": true - } + "is_all_day": true, + "host": "Updated Host" } From 43027a45314b965ee5125210f1cc7f64ebd2d5a5 Mon Sep 17 00:00:00 2001 From: josh Date: Thu, 23 Apr 2026 22:07:39 -0700 Subject: [PATCH 07/10] getEvents returns correct tyupe --- internal/api/handlers/event.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/api/handlers/event.go b/internal/api/handlers/event.go index c5650708..747f0e81 100644 --- a/internal/api/handlers/event.go +++ b/internal/api/handlers/event.go @@ -119,7 +119,12 @@ func (h *EventsHandler) GetEvents(c *gin.Context) { }) return } - c.JSON(http.StatusOK, events) + + dtoEvents := make([]dto.Event, len(events)) + for i, event := range events { + dtoEvents[i] = dto.EventDomainToDto(&event) + } + c.JSON(http.StatusOK, dtoEvents) } // UpdateEvent godoc From afca468c98bc14fbd5b8b02660337843cb524843 Mon Sep 17 00:00:00 2001 From: josh Date: Fri, 24 Apr 2026 10:08:22 -0700 Subject: [PATCH 08/10] update event cli --- internal/cli/events/put.go | 50 +++++++++++++++++--------------------- utils/time.go | 13 +++++++++- 2 files changed, 34 insertions(+), 29 deletions(-) diff --git a/internal/cli/events/put.go b/internal/cli/events/put.go index a15315d8..ce49a9a7 100644 --- a/internal/cli/events/put.go +++ b/internal/cli/events/put.go @@ -6,14 +6,15 @@ import ( "fmt" "net/http" "os" + "time" "github.com/charmbracelet/huh" "github.com/spf13/cobra" - "github.com/acmcsufoss/api.acmcsuf.com/internal/api/store/dbmodels" "github.com/acmcsufoss/api.acmcsuf.com/internal/cli/client" "github.com/acmcsufoss/api.acmcsuf.com/internal/cli/config" "github.com/acmcsufoss/api.acmcsuf.com/internal/cli/forms" + "github.com/acmcsufoss/api.acmcsuf.com/internal/dto" "github.com/acmcsufoss/api.acmcsuf.com/utils" ) @@ -35,7 +36,7 @@ func init() { func putEvents(id string, cfg *config.Config) { resourceURL := config.GetBaseURL(cfg).JoinPath("v1", "events", id) - var oldPayload dbmodels.CreateEventParams + var oldPayload dto.Event if body, err := client.SendRequestAndReadResponse(resourceURL, false, http.MethodGet, nil); err != nil { fmt.Fprintln(os.Stderr, "Error:", err) if body != nil { @@ -63,13 +64,13 @@ func putEvents(id string, cfg *config.Config) { } } -func putForm(oldPayload *dbmodels.CreateEventParams) (*dbmodels.UpdateEventParams, error) { - var payload dbmodels.UpdateEventParams +func putForm(oldPayload *dto.Event) (dto.UpdateEvent, error) { + var payload dto.UpdateEvent var err error var ( locationStr string = oldPayload.Location - startAtStr string - endAtStr string + startAtStr string = utils.FormatUnix(oldPayload.StartAt) + endAtStr string = utils.FormatUnix(oldPayload.EndAt) isAllDay bool = oldPayload.IsAllDay hostStr string = oldPayload.Host ) @@ -82,13 +83,11 @@ func putForm(oldPayload *dbmodels.CreateEventParams) (*dbmodels.UpdateEventParam Validate(forms.ValidateNonEmpty()), huh.NewInput(). Title("Starts At\n"+ - "Format: \x1b[93mMM/DD/YY HH:MM[PM | AM]\x1b[0m\n"+ - "Leave empty to keep existing value"). + "Format: \x1b[93mMM/DD/YY HH:MM[PM | AM]\x1b[0m\n"). Value(&startAtStr), huh.NewInput(). Title("Ends At\n"+ - "Format: \x1b[93mMM/DD/YY HH:MM[PM | AM]\x1b[0m\n"+ - "Leave empty to keep existing value"). + "Format: \x1b[93mMM/DD/YY HH:MM[PM | AM]\x1b[0m\n"). Value(&endAtStr), huh.NewConfirm(). Title("All day event?"). @@ -100,27 +99,22 @@ func putForm(oldPayload *dbmodels.CreateEventParams) (*dbmodels.UpdateEventParam ), ) if err = form.Run(); err != nil { - return nil, err + return dto.UpdateEvent{}, err } - payload.Uuid = oldPayload.Uuid - payload.Location = utils.StringtoNullString(locationStr) - payload.IsAllDay = utils.BooltoNullBool(isAllDay) - payload.Host = utils.StringtoNullString(hostStr) - if startAtStr != "" { - timestamp, err := utils.ByteSlicetoUnix([]byte(startAtStr)) - if err != nil { - return nil, err - } - payload.StartAt = utils.Int64toNullInt64(timestamp) + payload.Location = forms.NonEmptyPtr(locationStr) + startUnix, err := utils.ParseTime(startAtStr) + if err != nil { + return dto.UpdateEvent{}, err } - if endAtStr != "" { - timestamp, err := utils.ByteSlicetoUnix([]byte(endAtStr)) - if err != nil { - return nil, err - } - payload.EndAt = utils.Int64toNullInt64(timestamp) + payload.StartAt = &startUnix + endUnix, err := utils.ParseTime(endAtStr) + if err != nil { + return dto.UpdateEvent{}, err } + payload.EndAt = &endUnix + payload.IsAllDay = &isAllDay + payload.Host = &hostStr - return &payload, nil + return payload, nil } diff --git a/utils/time.go b/utils/time.go index f4f2b1ef..2e468c13 100644 --- a/utils/time.go +++ b/utils/time.go @@ -7,10 +7,21 @@ import ( "time" ) +const timeFormat = "01/02/06 03:04PM" + // For unix times of int64 to readable format of 03:04:05PM 01/02/06 func FormatUnix(unixTime int64) string { t := time.Unix(unixTime, 0) - return t.Format("01/02/06 03:04PM") + return t.Format(timeFormat) +} + +// Parses readable time format to Unix time integer +func ParseTime(timeStr string) (int64, error) { + t, err := time.Parse(timeFormat, timeStr) + if err != nil { + return 0, fmt.Errorf("failed to parse time: %v", err) + } + return t.Unix(), nil } func TimeAfterDuration(startTime int64, duration string) (int64, error) { From 4314c6a1b8dcc7a72587280d20c1181f912f8c77 Mon Sep 17 00:00:00 2001 From: josh Date: Fri, 24 Apr 2026 10:24:28 -0700 Subject: [PATCH 09/10] finish + mess with helper funcs --- internal/cli/announcements/post.go | 2 +- internal/cli/announcements/put.go | 2 +- internal/cli/events/post.go | 24 ++++++++-------- internal/cli/events/put.go | 5 ++-- utils/convert.go | 44 ++++++++---------------------- utils/time.go | 18 ++++++++---- 6 files changed, 39 insertions(+), 56 deletions(-) diff --git a/internal/cli/announcements/post.go b/internal/cli/announcements/post.go index 02b95f47..c8cd7a7b 100644 --- a/internal/cli/announcements/post.go +++ b/internal/cli/announcements/post.go @@ -88,7 +88,7 @@ func postForm() (*dto.Announcement, error) { return nil, err } - payload.AnnounceAt, err = utils.ByteSlicetoUnix([]byte(announceAtStr)) + payload.AnnounceAt, err = utils.ParseTime(announceAtStr) if err != nil { return nil, err } diff --git a/internal/cli/announcements/put.go b/internal/cli/announcements/put.go index 1ff2f33c..9be02f8e 100644 --- a/internal/cli/announcements/put.go +++ b/internal/cli/announcements/put.go @@ -109,7 +109,7 @@ func putForm(oldPayload *dto.Announcement) (*dto.UpdateAnnouncement, error) { payload.Uuid = oldPayload.Uuid payload.Visibility = &visibilityStr if announceAtStr != "" { - timestamp, err := utils.ByteSlicetoUnix([]byte(announceAtStr)) + timestamp, err := utils.ParseTime(announceAtStr) if err != nil { return nil, err } diff --git a/internal/cli/events/post.go b/internal/cli/events/post.go index c31a17aa..46ff9cbd 100644 --- a/internal/cli/events/post.go +++ b/internal/cli/events/post.go @@ -10,10 +10,10 @@ import ( "github.com/charmbracelet/huh" "github.com/spf13/cobra" - "github.com/acmcsufoss/api.acmcsuf.com/internal/api/store/dbmodels" "github.com/acmcsufoss/api.acmcsuf.com/internal/cli/client" "github.com/acmcsufoss/api.acmcsuf.com/internal/cli/config" "github.com/acmcsufoss/api.acmcsuf.com/internal/cli/forms" + "github.com/acmcsufoss/api.acmcsuf.com/internal/dto" "github.com/acmcsufoss/api.acmcsuf.com/utils" ) @@ -51,8 +51,8 @@ func postEvent(cfg *config.Config) { } } -func postForm() (*dbmodels.CreateEventParams, error) { - var payload dbmodels.CreateEventParams +func postForm() (dto.Event, error) { + var payload dto.Event var err error var ( startAtStr string @@ -71,14 +71,12 @@ func postForm() (*dbmodels.CreateEventParams, error) { Validate(forms.ValidateNonEmpty()), huh.NewInput(). Title("Starts At\n"+ - "Format: \x1b[93mMM/DD/YY HH:MM[PM | AM]\x1b[0m\n"+ - "Example: \x1b[93m01/02/06 03:04PM\x1b[0m"). + "Format: \x1b[93m01/02/06 03:04PM\x1b[0m"). Value(&startAtStr). Validate(forms.ValidateNonEmpty()), huh.NewInput(). Title("Ends At\n"+ - "Format: \x1b[93mMM/DD/YY HH:MM[PM | AM]\x1b[0m\n"+ - "Example: \x1b[93m01/02/06 04:04PM\x1b[0m"). + "Format: \x1b[93m01/02/06 03:04PM\x1b[0m"). Value(&endAtStr). Validate(forms.ValidateNonEmpty()), huh.NewConfirm(). @@ -91,17 +89,17 @@ func postForm() (*dbmodels.CreateEventParams, error) { ), ) if err = form.Run(); err != nil { - return nil, err + return dto.Event{}, err } - payload.StartAt, err = utils.ByteSlicetoUnix([]byte(startAtStr)) + payload.StartAt, err = utils.ParseTime(startAtStr) if err != nil { - return nil, err + return dto.Event{}, err } - payload.EndAt, err = utils.ByteSlicetoUnix([]byte(endAtStr)) + payload.EndAt, err = utils.ParseTime(endAtStr) if err != nil { - return nil, err + return dto.Event{}, err } - return &payload, nil + return payload, nil } diff --git a/internal/cli/events/put.go b/internal/cli/events/put.go index ce49a9a7..0417e213 100644 --- a/internal/cli/events/put.go +++ b/internal/cli/events/put.go @@ -6,7 +6,6 @@ import ( "fmt" "net/http" "os" - "time" "github.com/charmbracelet/huh" "github.com/spf13/cobra" @@ -83,11 +82,11 @@ func putForm(oldPayload *dto.Event) (dto.UpdateEvent, error) { Validate(forms.ValidateNonEmpty()), huh.NewInput(). Title("Starts At\n"+ - "Format: \x1b[93mMM/DD/YY HH:MM[PM | AM]\x1b[0m\n"). + "Format: \x1b[93m01/02/06 03:04PM\x1b[0m"). Value(&startAtStr), huh.NewInput(). Title("Ends At\n"+ - "Format: \x1b[93mMM/DD/YY HH:MM[PM | AM]\x1b[0m\n"). + "Format: \x1b[93m01/02/06 03:04PM\x1b[0m"). Value(&endAtStr), huh.NewConfirm(). Title("All day event?"). diff --git a/utils/convert.go b/utils/convert.go index af2f9272..d1495a4c 100644 --- a/utils/convert.go +++ b/utils/convert.go @@ -1,39 +1,19 @@ package utils import ( - "fmt" - "strconv" - "time" + // "fmt" + // "strconv" + // "time" ) // Common Byte Slice Conversions -func ByteSlicetoInt64(data []byte) (int64, error) { - fmt.Printf("%d, %v", data, data) - number, err := strconv.Atoi(string(data)) - if err != nil { - return 0, fmt.Errorf("error converting bytes to int: %s", err) - } - - return int64(number), nil -} - -func ByteSlicetoUnix(data []byte) (int64, error) { - // 12 hour format. Note: AM AND PM HAVE TO BE CAPITALIZED - layout := "01/02/06 03:04PM" - loc, err := time.LoadLocation("America/Los_Angeles") - if err != nil { - return 0, fmt.Errorf("error in getting location for time: %s", err) - } - - timeString := string(data) - - // Parse time in relation to los angeles time, or more familiarly, PST time - startTime, err := time.ParseInLocation(layout, timeString, loc) - if err != nil { - return 0, fmt.Errorf("error parsing time format: %s", err) - - } - - return startTime.Unix(), nil -} +// func ByteSlicetoInt64(data []byte) (int64, error) { +// fmt.Printf("%d, %v", data, data) +// number, err := strconv.Atoi(string(data)) +// if err != nil { +// return 0, fmt.Errorf("error converting bytes to int: %s", err) +// } +// +// return int64(number), nil +// } diff --git a/utils/time.go b/utils/time.go index 2e468c13..65c62474 100644 --- a/utils/time.go +++ b/utils/time.go @@ -7,21 +7,27 @@ import ( "time" ) -const timeFormat = "01/02/06 03:04PM" +const timeLayout = "01/02/06 03:04PM" // For unix times of int64 to readable format of 03:04:05PM 01/02/06 func FormatUnix(unixTime int64) string { t := time.Unix(unixTime, 0) - return t.Format(timeFormat) + return t.Format(timeLayout) } -// Parses readable time format to Unix time integer func ParseTime(timeStr string) (int64, error) { - t, err := time.Parse(timeFormat, timeStr) + // 12 hour format. Note: AM AND PM HAVE TO BE CAPITALIZED + loc, err := time.LoadLocation("America/Los_Angeles") if err != nil { - return 0, fmt.Errorf("failed to parse time: %v", err) + return 0, fmt.Errorf("error in getting location for time: %s", err) } - return t.Unix(), nil + + // Parse time in PST + startTime, err := time.ParseInLocation(timeLayout, timeStr, loc) + if err != nil { + return 0, fmt.Errorf("error parsing time format: %s", err) + } + return startTime.Unix(), nil } func TimeAfterDuration(startTime int64, duration string) (int64, error) { From ebc84a5c95ad904dace715b800eae947ed0e7221 Mon Sep 17 00:00:00 2001 From: josh Date: Fri, 24 Apr 2026 11:04:01 -0700 Subject: [PATCH 10/10] format --- utils/convert.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/convert.go b/utils/convert.go index d1495a4c..5baa7e3a 100644 --- a/utils/convert.go +++ b/utils/convert.go @@ -1,9 +1,9 @@ package utils import ( - // "fmt" - // "strconv" - // "time" +// "fmt" +// "strconv" +// "time" ) // Common Byte Slice Conversions