Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 4 additions & 4 deletions apps/api/docs/docs.go

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

8 changes: 4 additions & 4 deletions apps/api/docs/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -906,12 +906,12 @@
"description": "Gets events with a nullable event role for authenticated users.",
"parameters": [
{
"description": "If true, include unpublished events as well. Superusers ONLY.",
"description": "Can be scoped to either published, scoped, or all. Scoped means admins and staff can see unpublished events",
"in": "query",
"name": "include_published",
"name": "scope",
"schema": {
"default": false,
"type": "boolean"
"default": "\"published\"",
"type": "string"
}
}
],
Expand Down
9 changes: 5 additions & 4 deletions apps/api/docs/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -640,12 +640,13 @@ paths:
get:
description: Gets events with a nullable event role for authenticated users.
parameters:
- description: If true, include unpublished events as well. Superusers ONLY.
- description: Can be scoped to either published, scoped, or all. Scoped means
admins and staff can see unpublished events
in: query
name: include_published
name: scope
schema:
default: false
type: boolean
default: '"published"'
type: string
requestBody:
content:
application/json:
Expand Down
19 changes: 8 additions & 11 deletions apps/api/internal/api/handlers/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,9 @@
"github.com/swamphacks/core/apps/api/internal/db/repository"
"github.com/swamphacks/core/apps/api/internal/db/sqlc"
"github.com/swamphacks/core/apps/api/internal/email"
"github.com/swamphacks/core/apps/api/internal/parse"

Check failure on line 22 in apps/api/internal/api/handlers/events.go

View workflow job for this annotation

GitHub Actions / API Lint

ST1019: package "github.com/swamphacks/core/apps/api/internal/parse" is being imported more than once (staticcheck)
. "github.com/swamphacks/core/apps/api/internal/parse"

Check failure on line 23 in apps/api/internal/api/handlers/events.go

View workflow job for this annotation

GitHub Actions / API Lint

ST1001: should not use dot imports (staticcheck)
"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 @@ -322,7 +320,7 @@
if err != nil {
if errors.Is(err, repository.ErrEventRoleNotFound) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(NullableEventRole{

Check failure on line 323 in apps/api/internal/api/handlers/events.go

View workflow job for this annotation

GitHub Actions / API Lint

Error return value of `(*encoding/json.Encoder).Encode` is not checked (errcheck)
UserID: *userId,
EventID: eventId,
Role: nil,
Expand All @@ -335,7 +333,7 @@
}

w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(NullableEventRole{

Check failure on line 336 in apps/api/internal/api/handlers/events.go

View workflow job for this annotation

GitHub Actions / API Lint

Error return value of `(*encoding/json.Encoder).Encode` is not checked (errcheck)
UserID: eventRole.UserID,
EventID: eventRole.EventID,
Role: &eventRole.Role,
Expand Down Expand Up @@ -386,22 +384,21 @@
// @Tags Event
// @Accept json
// @Produce json
// @Param include_published query boolean false "If true, include unpublished events as well. Superusers ONLY." default(false)
// @Success 200 {array} sqlc.GetEventsWithUserInfoRow "OK: Events returned"
// @Param scope query string false "Can be scoped to either published, scoped, or all. Scoped means admins and staff can see unpublished events" 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))
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
2 changes: 2 additions & 0 deletions apps/web/src/lib/auth/services/oauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export function _oauthSignIn<T extends AuthConfig>(config: T) {
path: "/",
sameSite: "lax",
secure: import.meta.env.DEV ? false : true,
//TODO: change this to an env variable
domain: import.meta.env.DEV ? "localhost" : ".swamphacks.com",
});

const params = createOAuthRequestParams(
Expand Down
12 changes: 6 additions & 6 deletions infra/Caddyfile.api
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

# Development API
dev-api.swamphacks.com {
reverse_proxy localhost:8081 {
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
reverse_proxy api-dev:8080 {
header_up X-Real-IP {remote}
header_up X-Forwarded-For {remote}
header_up X-Forwarded-Port {server_port}
header_up X-Forwarded-Proto {scheme}
}
Expand All @@ -27,9 +27,9 @@ dev-api.swamphacks.com {

# Production API
api.swamphacks.com {
reverse_proxy localhost:8080 {
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
reverse_proxy api:8080 {
header_up X-Real-IP {remote}
header_up X-Forwarded-For {remote}
header_up X-Forwarded-Port {server_port}
header_up X-Forwarded-Proto {scheme}
}
Expand Down
Loading