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
3 changes: 3 additions & 0 deletions infra/helm/team-devoops/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ services:
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: "https://ge83mom-devops26.stud.k8s.aet.cit.tum.de/auth/realms/devops"
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI: "http://keycloak:8080/auth/realms/devops/protocol/openid-connect/certs"
JAVA_TOOL_OPTIONS: "-Xmx300m -Xms64m"
# Keycloak Admin REST API for user provisioning (KeycloakService).
KEYCLOAK_BASE_URL: "http://keycloak:8080/auth"
KEYCLOAK_REALM: "devops"
event-service:
path: /api/v1/events
port: 8080
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ public class EventPartialUpdate {
private @Nullable OffsetDateTime endTime;

@Valid
private List<String> attendees = new ArrayList<>();
private @Nullable List<String> attendees;

@Valid
private List<String> sportsLinked = new ArrayList<>();
private @Nullable List<String> sportsLinked;

@Valid
private List<String> teamsLinked = new ArrayList<>();
private @Nullable List<String> teamsLinked;

public EventPartialUpdate name(@Nullable String name) {
this.name = name;
Expand Down Expand Up @@ -146,7 +146,7 @@ public EventPartialUpdate addAttendeesItem(String attendeesItem) {

@Schema(name = "attendees", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
@JsonProperty("attendees")
public List<String> getAttendees() {
public @Nullable List<String> getAttendees() {
return attendees;
}

Expand Down Expand Up @@ -174,7 +174,7 @@ public EventPartialUpdate addSportsLinkedItem(String sportsLinkedItem) {

@Schema(name = "sports_linked", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
@JsonProperty("sports_linked")
public List<String> getSportsLinked() {
public @Nullable List<String> getSportsLinked() {
return sportsLinked;
}

Expand Down Expand Up @@ -202,7 +202,7 @@ public EventPartialUpdate addTeamsLinkedItem(String teamsLinkedItem) {

@Schema(name = "teams_linked", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
@JsonProperty("teams_linked")
public List<String> getTeamsLinked() {
public @Nullable List<String> getTeamsLinked() {
return teamsLinked;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@EnableMethodSecurity(proxyTargetClass = true)
public class SecurityConfig {

@Bean
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package tum.devoops.eventservice.controller;

import java.util.List;
import java.util.UUID;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.web.bind.annotation.RestController;

import tum.devoops.eventservice.api.EventsApi;
import tum.devoops.eventservice.model.Event;
import tum.devoops.eventservice.model.EventCreate;
import tum.devoops.eventservice.model.EventPartialUpdate;
import tum.devoops.eventservice.model.EventSummary;
import tum.devoops.eventservice.service.EventService;

@RestController
@PreAuthorize("hasAnyRole('admin', 'member')")
public class EventController implements EventsApi {

private final EventService eventService;

public EventController(EventService eventService) {
this.eventService = eventService;
}

@Override
public ResponseEntity<List<EventSummary>> getAllEvents() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
UUID requesterId = extractRequesterId(auth);
boolean isAdmin = extractIsAdmin(auth);
return ResponseEntity.ok(eventService.getAllEvents(requesterId, isAdmin));
}

@Override
public ResponseEntity<Event> createEvent(EventCreate eventCreate) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
UUID requesterId = extractRequesterId(auth);
boolean isAdmin = extractIsAdmin(auth);
Event created = eventService.createEvent(eventCreate, requesterId, isAdmin);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}

@Override
public ResponseEntity<Event> getEventDetails(UUID eventId) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
UUID requesterId = extractRequesterId(auth);
boolean isAdmin = extractIsAdmin(auth);
return ResponseEntity.ok(eventService.getEventDetails(eventId, requesterId, isAdmin));
}

@Override
public ResponseEntity<Event> updateEventDetails(UUID eventId, EventPartialUpdate eventPartialUpdate) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
UUID requesterId = extractRequesterId(auth);
boolean isAdmin = extractIsAdmin(auth);
return ResponseEntity.ok(eventService.updateEventDetails(eventId, eventPartialUpdate, requesterId, isAdmin));
}

@Override
public ResponseEntity<Void> deleteEvent(UUID eventId) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
UUID requesterId = extractRequesterId(auth);
boolean isAdmin = extractIsAdmin(auth);
eventService.deleteEvent(eventId, requesterId, isAdmin);
return ResponseEntity.noContent().build();
}

private UUID extractRequesterId(Authentication auth) {
Jwt jwt = (Jwt) auth.getPrincipal();
return UUID.fromString(jwt.getSubject());
}

private boolean extractIsAdmin(Authentication auth) {
return auth.getAuthorities().stream()
.anyMatch(a -> "ROLE_admin".equals(a.getAuthority()));
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package tum.devoops.eventservice;
package tum.devoops.eventservice.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package tum.devoops.eventservice.converter;

import java.time.ZoneOffset;
import java.util.List;
import java.util.stream.Collectors;

import tum.devoops.eventservice.entity.AttendanceEntity;
import tum.devoops.eventservice.entity.EventEntity;
import tum.devoops.eventservice.entity.SportEventEntity;
import tum.devoops.eventservice.entity.TeamEventEntity;
import tum.devoops.eventservice.model.Event;
import tum.devoops.eventservice.model.EventSummary;

/**
* Maps {@link EventEntity} (and its link entities) to the API models.
*
* <p>Stateless and dependency-free: callers supply the already-fetched link entities so this
* converter performs no data access.
*/
public final class EventConverter {

private EventConverter() {
}

public static Event toEvent(EventEntity entity,
List<AttendanceEntity> attendances,
List<SportEventEntity> sports,
List<TeamEventEntity> teams) {
Event event = new Event(
entity.getId(),
entity.getName(),
entity.getDescription(),
entity.getStartTime().atOffset(ZoneOffset.UTC),
entity.getEndTime().atOffset(ZoneOffset.UTC),
entity.getCreatorId().toString()
);
event.setAttendees(attendances.stream()
.map(a -> a.getId().getMemberId().toString())
.collect(Collectors.toList()));
event.setSportsLinked(sports.stream()
.map(s -> s.getId().getSportName())
.collect(Collectors.toList()));
event.setTeamsLinked(teams.stream()
.map(t -> t.getId().getTeamId().toString())
.collect(Collectors.toList()));
return event;
}

public static EventSummary toSummary(EventEntity entity) {
return new EventSummary(
entity.getId(),
entity.getName(),
entity.getStartTime().atOffset(ZoneOffset.UTC),
entity.getEndTime().atOffset(ZoneOffset.UTC)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package tum.devoops.eventservice.exception;

public class BadRequestException extends RuntimeException {
public BadRequestException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package tum.devoops.eventservice.exception;

public class ForbiddenException extends RuntimeException {
public ForbiddenException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package tum.devoops.eventservice.exception;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import tum.devoops.eventservice.model.BadRequestResponse;
import tum.devoops.eventservice.model.ErrorResponse;

@RestControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(NotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(NotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ErrorResponse().message(ex.getMessage()));
}

@ExceptionHandler(ForbiddenException.class)
public ResponseEntity<ErrorResponse> handleForbidden(ForbiddenException ex) {
return ResponseEntity.status(HttpStatus.FORBIDDEN)
.body(new ErrorResponse().message(ex.getMessage()));
}

@ExceptionHandler(BadRequestException.class)
public ResponseEntity<BadRequestResponse> handleBadRequest(BadRequestException ex) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new BadRequestResponse().message(ex.getMessage()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package tum.devoops.eventservice.exception;

public class NotFoundException extends RuntimeException {
public NotFoundException(String message) {
super(message);
}
}
Loading
Loading