Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementing a new EventResponseDTO with field rearrangement #8082

Open
wants to merge 10 commits into
base: dev
Choose a base branch
from
1 change: 1 addition & 0 deletions core/src/main/java/greencity/config/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
EVENTS,
EVENTS + "/addresses",
EVENTS + EVENT_ID,
EVENTS + "/v2" + EVENT_ID,
EVENTS + EVENT_ID + ATTENDERS,
"/languages/codes",
SEARCH + ECO_NEWS,
Expand Down
24 changes: 23 additions & 1 deletion core/src/main/java/greencity/controller/EventController.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import greencity.dto.event.AddressDto;
import greencity.dto.event.EventAttenderDto;
import greencity.dto.event.EventDto;
import greencity.dto.event.EventResponseDto;
import greencity.dto.event.UpdateEventRequestDto;
import greencity.dto.filter.FilterEventDto;
import greencity.dto.user.UserForListDto;
Expand Down Expand Up @@ -165,6 +166,27 @@ public ResponseEntity<EventDto> getEvent(
return ResponseEntity.ok().body(eventService.getEvent(eventId, principal));
}

/**
* Method for getting the event by event id version 2.
*
* @return {@link EventResponseDto} instance.
* @author Yurii Osovskyi.
*/
@Operation(summary = "Get the event")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = HttpStatuses.OK),
@ApiResponse(responseCode = "400", description = HttpStatuses.BAD_REQUEST,
content = @Content(examples = @ExampleObject(HttpStatuses.BAD_REQUEST))),
@ApiResponse(responseCode = "404", description = HttpStatuses.NOT_FOUND,
content = @Content(examples = @ExampleObject(HttpStatuses.NOT_FOUND)))
})
@GetMapping("/v2/{eventId}")
public ResponseEntity<EventResponseDto> getEventV2(
@PathVariable Long eventId,
@Parameter(hidden = true) Principal principal) {
return ResponseEntity.ok().body(eventService.getEventV2(eventId, principal));
}

/**
* Method for getting pages of events.
*
Expand Down Expand Up @@ -600,4 +622,4 @@ public ResponseEntity<Object> declineRequest(@PathVariable Long eventId, @PathVa
eventService.declineRequest(eventId, principal.getName(), userId);
return ResponseEntity.status(HttpStatus.OK).build();
}
}
}
61 changes: 60 additions & 1 deletion core/src/test/java/greencity/ModelUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@
import greencity.dto.event.AddEventDtoRequest;
import greencity.dto.event.AddressDto;
import greencity.dto.event.EventAuthorDto;
import greencity.dto.event.EventDateInformationDto;
import greencity.dto.event.EventDateLocationDto;
import greencity.dto.event.EventDto;
import greencity.dto.event.EventInformationDto;
import greencity.dto.event.EventResponseDto;
import greencity.dto.event.UpdateEventDateLocationDto;
import greencity.dto.event.UpdateEventRequestDto;
import greencity.dto.favoriteplace.FavoritePlaceDto;
Expand All @@ -39,6 +42,7 @@
import greencity.dto.location.LocationDto;
import greencity.dto.location.MapBoundsDto;
import greencity.dto.place.PlaceByBoundsDto;
import greencity.dto.tag.TagDto;
import greencity.dto.todolistitem.CustomToDoListItemResponseDto;
import greencity.dto.todolistitem.ToDoListItemPostDto;
import greencity.dto.todolistitem.ToDoListItemRequestDto;
Expand All @@ -59,15 +63,19 @@
import greencity.enums.ArticleType;
import greencity.enums.CommentStatus;
import greencity.enums.EventStatus;
import greencity.enums.EventType;
import greencity.enums.Role;
import greencity.enums.ToDoListItemStatus;
import greencity.enums.TagType;
import greencity.enums.UserStatus;
import java.security.Principal;
import java.sql.Date;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
Expand Down Expand Up @@ -588,4 +596,55 @@ public static List<PlaceByBoundsDto> getPlaceByBoundsDto() {
.location(new LocationDto())
.build());
}
}

public static EventResponseDto getEventResponseDto() {
return EventResponseDto.builder()
.id(1L)
.eventInformation(EventInformationDto.builder()
.title("Test Event")
.description("New Test Event")
.tags(List.of(TagUaEnDto.builder()
.id(2L)
.nameUa("Соціальний")
.nameEn("Social")
.build()))
.build())
.organizer(EventAuthorDto.builder().id(1L).name("Test").email("[email protected]").build())
.creationDate(LocalDate.of(2025, 1, 10))
.isOpen(true)
.dates(List.of(
EventDateInformationDto.builder()
.startDate(ZonedDateTime.of(2025, 12, 26, 12, 30, 0, 0, ZoneOffset.UTC))
.finishDate(ZonedDateTime.of(2025, 12, 26, 21, 59, 0, 0, ZoneOffset.UTC))
.onlineLink("www.testlink.com")
.coordinates(AddressDto.builder()
.latitude(50.44628775288652)
.longitude(30.49364829378446)
.streetEn("Halytska Square")
.streetUa("Галицька площа")
.houseNumber("1")
.cityEn("Kyiv")
.cityUa("Київ")
.regionEn("Kyiv")
.regionUa("місто Київ")
.countryEn("Ukraine")
.countryUa("Україна")
.formattedAddressEn("Halytska Sq, 1, Kyiv, Ukraine, 02000")
.formattedAddressUa("Галицька пл., 1, Київ, Україна, 02000")
.build())
.build()))
.titleImage("https://test.png")
.additionalImages(List.of("https://test1.png", "https://test2.png"))
.type(EventType.OFFLINE)
.isRelevant(true)
.likes(3)
.dislikes(1)
.countComments(1)
.eventRate(20.0)
.currentUserGrade(50)
.isSubscribed(false)
.isFavorite(false)
.isOrganizedByFriend(false)
.build();
}
}
31 changes: 31 additions & 0 deletions core/src/test/java/greencity/controller/EventControllerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import greencity.dto.PageableAdvancedDto;
import greencity.dto.event.AddEventDtoRequest;
import greencity.dto.event.EventDto;
import greencity.dto.event.EventResponseDto;
import greencity.dto.event.UpdateEventRequestDto;
import greencity.dto.filter.FilterEventDto;
import greencity.dto.user.UserVO;
Expand Down Expand Up @@ -756,4 +757,34 @@ void declineRequestTest() {
.andExpect(status().isOk());
verify(eventService).declineRequest(eventId, principal.getName(), userId);
}

@Test
@SneakyThrows
void getEventV2Test() {
Long eventId = 1L;
EventResponseDto eventResponseDto = ModelUtils.getEventResponseDto();

when(eventService.getEventV2(eventId, principal)).thenReturn(eventResponseDto);

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();
String expectedJson = objectMapper.writeValueAsString(eventResponseDto);

mockMvc.perform(get(EVENTS_CONTROLLER_LINK + "/v2/{eventId}", eventId)
.principal(principal)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json(expectedJson));

verify(eventService, times(1)).getEventV2(eventId, principal);
}

@Test
@SneakyThrows
void getEventV2FailedTest() {
mockMvc.perform(get(EVENTS_CONTROLLER_LINK + "/v2/{eventId}", "not_number").principal(principal))
.andExpect(status().isBadRequest());

verify(eventService, times(0)).getEventV2(1L, principal);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package greencity.dto.event;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;

@SuperBuilder
@NoArgsConstructor
@Getter
@Setter
@EqualsAndHashCode(callSuper = true)
public class EventDateInformationDto extends AbstractEventDateLocationDto {
private Long id;
private EventResponseDto event;
private AddressDto coordinates;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package greencity.dto.event;

import greencity.dto.tag.TagUaEnDto;
import jakarta.validation.constraints.NotEmpty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;

@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class EventInformationDto {
private String title;
private String description;
@NotEmpty
private List<TagUaEnDto> tags;
}
Comment on lines +16 to +20
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add validation constraints to prevent potential security issues.

The title and description fields should have appropriate validation constraints to prevent security issues and ensure data quality:

 public class EventInformationDto {
+    @NotBlank
+    @Size(min = 1, max = 255)
     private String title;
+    @NotBlank
+    @Size(max = 5000)
     private String description;
     @NotEmpty
+    @Size(max = 100)
     private List<TagUaEnDto> tags;
 }

This ensures:

  • Title is not blank and has a reasonable length
  • Description is not blank and has a maximum length
  • Tags list has a reasonable size limit
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private String title;
private String description;
@NotEmpty
private List<TagUaEnDto> tags;
}
@NotBlank
@Size(min = 1, max = 255)
private String title;
@NotBlank
@Size(max = 5000)
private String description;
@NotEmpty
@Size(max = 100)
private List<TagUaEnDto> tags;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package greencity.dto.event;

import com.fasterxml.jackson.annotation.JsonProperty;
import greencity.enums.EventType;
import jakarta.validation.constraints.Max;
import lombok.Builder;
import lombok.Data;
import org.springframework.lang.Nullable;
import java.time.LocalDate;
import java.util.List;

@Data
@Builder
public class EventResponseDto {
private Long id;

private EventInformationDto eventInformation;

private EventAuthorDto organizer;

private LocalDate creationDate;

private Boolean isOpen;

@Max(7)
private List<EventDateInformationDto> dates;

@Nullable
private String titleImage;

@Nullable
@Max(4)
private List<String> additionalImages;

private EventType type;

@JsonProperty("isSubscribed")
private boolean isSubscribed;

@JsonProperty("isFavorite")
private boolean isFavorite;

private Boolean isRelevant;

private Integer likes;

private Integer dislikes;

private Integer countComments;

@JsonProperty("isOrganizedByFriend")
private boolean isOrganizedByFriend;

private Double eventRate;

private Integer currentUserGrade;
}
9 changes: 9 additions & 0 deletions service-api/src/main/java/greencity/service/EventService.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import greencity.dto.event.AddressDto;
import greencity.dto.event.EventAttenderDto;
import greencity.dto.event.EventDto;
import greencity.dto.event.EventResponseDto;
import greencity.dto.event.EventVO;
import greencity.dto.event.UpdateEventRequestDto;
import greencity.dto.filter.FilterEventDto;
Expand Down Expand Up @@ -45,6 +46,14 @@ public interface EventService {
*/
EventDto getEvent(Long eventId, Principal principal);

/**
* Method for getting Event instance.
*
* @param eventId - event id.
* @return {@link EventResponseDto} instance.
*/
EventResponseDto getEventV2(Long eventId, Principal principal);

/**
* Method for getting all Event instances filtered.
*
Expand Down
21 changes: 4 additions & 17 deletions service/src/main/java/greencity/mapping/events/EventDtoMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@
import greencity.entity.event.Address;
import greencity.entity.event.Event;
import greencity.entity.event.EventDateLocation;
import greencity.entity.event.EventGrade;
import greencity.entity.event.EventImages;
import java.time.ZonedDateTime;
import greencity.service.CommentService;
import greencity.utils.EventUtils;
import org.modelmapper.AbstractConverter;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -51,7 +50,7 @@ public EventDto convert(Event event) {
eventDto.setTitleImage(event.getTitleImage());
eventDto.setOpen(event.isOpen());
eventDto.setType(event.getType());
eventDto.setIsRelevant(isRelevant(event.getDates()));
eventDto.setIsRelevant(EventUtils.isRelevant(event.getDates()));
eventDto.setLikes(event.getUsersLikedEvents().size());
eventDto.setCountComments(commentService.countCommentsForEvent(event.getId()));
User organizer = event.getOrganizer();
Expand Down Expand Up @@ -80,7 +79,7 @@ public EventDto convert(Event event) {
eventDto.setAdditionalImages(event.getAdditionalImages().stream()
.map(EventImages::getLink).collect(Collectors.toList()));
}
eventDto.setEventRate(calculateEventRate(event.getEventGrades()));
eventDto.setEventRate(EventUtils.calculateEventRate(event.getEventGrades()));
return eventDto;
}

Expand Down Expand Up @@ -111,16 +110,4 @@ private EventDateLocationDto convertEventDateLocation(EventDateLocation eventDat
}
return eventDateLocationDto;
}

private double calculateEventRate(List<EventGrade> eventGrades) {
return eventGrades.stream()
.mapToInt(EventGrade::getGrade)
.average()
.orElse(0.0);
}

private boolean isRelevant(List<EventDateLocation> dates) {
return dates.getLast().getFinishDate().isAfter(ZonedDateTime.now())
|| dates.getLast().getFinishDate().isEqual(ZonedDateTime.now());
}
}
}
Loading
Loading