Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
17 changes: 8 additions & 9 deletions apps/api/internal/api/handlers/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ import (
"github.com/swamphacks/core/apps/api/internal/email"
"github.com/swamphacks/core/apps/api/internal/parse"
. "github.com/swamphacks/core/apps/api/internal/parse"
"github.com/swamphacks/core/apps/api/internal/ptr"
"github.com/swamphacks/core/apps/api/internal/services"
"github.com/swamphacks/core/apps/api/internal/web"
)

type EventHandler struct {
Expand Down Expand Up @@ -386,22 +384,23 @@ func (h *EventHandler) DeleteEventById(w http.ResponseWriter, r *http.Request) {
// @Tags Event
// @Accept json
// @Produce json
// @Param include_published query boolean false "If true, include unpublished events as well. Superusers ONLY." default(false)
// @Param include_published query string false "published to only include published. scoped to also include unpublished where the user is a staff or admin. all includes everything (superusers ONLY)." default("published")
// @Success 200 {array} sqlc.GetEventsWithUserInfoRow "OK: Events returned"
// @Router /events [get]
func (h *EventHandler) GetEvents(w http.ResponseWriter, r *http.Request) {
// Parse query params
// include_unpublished="true,false", default: false
queryParams := r.URL.Query()
includeUnpublished, err := web.ParseParamBoolean(queryParams, "include_unpublished", ptr.BoolToPtr(false))
// scope="published,scoped,all", default: published
q := r.URL.Query()
scope, err := parse.ParseGetEventScopeType(q.Get("scope"))

if err != nil {
res.SendError(w, http.StatusBadRequest, res.NewError("missing_fields", "Missing/malformed query. Available parameters: include_unpublished=true,false"))
res.SendError(w, http.StatusBadRequest, res.NewError("missing_fields", "Missing/malformed query. Available parameters: include_unpublished=published, scoped, all, or none (default to published)"))
return
}

events, err := h.eventService.GetEvents(r.Context(), *includeUnpublished)
events, err := h.eventService.GetEvents(r.Context(), scope)
if errors.Is(err, services.ErrMissingFields) {
res.SendError(w, http.StatusBadRequest, res.NewError("missing_fields", "Missing/malformed query. Available parameteres: status=all,published"))
res.SendError(w, http.StatusBadRequest, res.NewError("missing_fields", "Missing/malformed query. Available parameters: include_unpublished=published, scoped, all, or none (default to published)"))
return
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
-- +goose Up
-- +goose StatementBegin
CREATE TYPE get_event_scope_type AS ENUM (
'published',
'scoped',
'all'
);
-- +goose StatementEnd

-- +goose Down
-- +goose StatementBegin
DROP TYPE get_event_scope_type;
-- +goose StatementEnd
10 changes: 9 additions & 1 deletion apps/api/internal/db/queries/events.sql
Original file line number Diff line number Diff line change
Expand Up @@ -89,5 +89,13 @@ LEFT JOIN event_roles er
LEFT JOIN applications a
ON a.event_id = e.id
AND a.user_id = sqlc.narg(user_id)
WHERE (sqlc.arg(include_unpublished)::boolean IS TRUE OR e.is_published = TRUE)
WHERE
CASE sqlc.arg(scope)::get_event_scope_type
WHEN 'all' THEN
TRUE
WHEN 'scoped' THEN
e.is_published = TRUE OR (e.is_published = FALSE AND (er.role = 'staff' or er.role = 'admin'))
ELSE
e.is_published = TRUE
END
ORDER BY e.start_time ASC;
7 changes: 4 additions & 3 deletions apps/api/internal/db/repository/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,11 @@ func (r *EventRepository) GetPublishedEvents(ctx context.Context, userId uuid.UU
return &events, err
}

func (r *EventRepository) GetEventsWithRoles(ctx context.Context, userId *uuid.UUID, includeUnpublished bool) (*[]sqlc.GetEventsWithUserInfoRow, error) {
// find a struct for valid roles to enforce type safety
func (r *EventRepository) GetEventsWithRoles(ctx context.Context, userId *uuid.UUID, scope sqlc.GetEventScopeType) (*[]sqlc.GetEventsWithUserInfoRow, error) {
events, err := r.db.Query.GetEventsWithUserInfo(ctx, sqlc.GetEventsWithUserInfoParams{
UserID: userId,
IncludeUnpublished: includeUnpublished,
UserID: userId,
Scope: scope,
})
return &events, err
}
Expand Down
16 changes: 12 additions & 4 deletions apps/api/internal/db/sqlc/events.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 43 additions & 0 deletions apps/api/internal/db/sqlc/models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

51 changes: 37 additions & 14 deletions apps/api/internal/parse/parse.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,46 @@
package parse

import "github.com/google/uuid"
import (
"fmt"

"github.com/google/uuid"
"github.com/swamphacks/core/apps/api/internal/db/sqlc"
)

// Parses a string ptr to a UUID or nil if failed to parse
func ParseUUIDOrNil(s *string) *uuid.UUID {
if s == nil || *s == "" {
return nil
}
id, err := uuid.Parse(*s)
if err != nil {
return nil
}
return &id
if s == nil || *s == "" {
return nil
}
id, err := uuid.Parse(*s)
if err != nil {
return nil
}
return &id
}

// Parses a string pointer to a pointer or nil
func ParseStrToPtr(s *string) *string {
if s == nil || *s == "" {
return nil
}
return s
}
if s == nil || *s == "" {
return nil
}
return s
}

func ParseGetEventScopeType(s string) (sqlc.GetEventScopeType, error) {
enumType := sqlc.GetEventScopeType(s)

// an empty string should default to published
if s == "" {
return sqlc.GetEventScopeTypePublished, nil
}

// sqlc generates constants for each enum value. We check if the input matches one of the valid, known constants.
switch enumType {
case sqlc.GetEventScopeTypePublished, sqlc.GetEventScopeTypeScoped, sqlc.GetEventScopeTypeAll:
return enumType, nil
default:
// The input string is not a valid enum value.
return "", fmt.Errorf("'%s' is not a valid GetEventScopeType", s)
}
}
8 changes: 4 additions & 4 deletions apps/api/internal/services/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,16 +116,16 @@ func (s *EventService) DeleteEventById(ctx context.Context, id uuid.UUID) error
return err
}

func (s *EventService) GetEvents(ctx context.Context, includeUnpublished bool) (*[]sqlc.GetEventsWithUserInfoRow, error) {
func (s *EventService) GetEvents(ctx context.Context, scope sqlc.GetEventScopeType) (*[]sqlc.GetEventsWithUserInfoRow, error) {
isSuperuser := ctxu.IsSuperuser(ctx)
userId := ctxu.GetUserIdFromCtx(ctx)

// Non-superusers can't get unpublished events
if !isSuperuser && includeUnpublished {
// Non-superusers can't get all unpublished events
if !isSuperuser && scope == "all" {
return nil, ErrMissingPerms
}

return s.eventRepo.GetEventsWithRoles(ctx, userId, includeUnpublished)
return s.eventRepo.GetEventsWithRoles(ctx, userId, scope)

}

Expand Down
3 changes: 2 additions & 1 deletion apps/web/src/features/Event/hooks/useEventsWithUserInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ export type EventsWithUserInfo = EventWithUserInfo[];

export async function fetchEvents(): Promise<EventsWithUserInfo> {
const result = await api
.get<EventsWithUserInfo>("events?include_unpublished=false")
.get<EventsWithUserInfo>("events?scope=scoped") // ADD QUERY PARAMETER
// TODO, find other calls to events and change the query parameter accordingly
.json();

console.log("Fetched events:", result);
Expand Down