From 51529fc34699e8866db1e218da16cbea21f12d28 Mon Sep 17 00:00:00 2001 From: Djesika Vacheva Date: Tue, 4 Aug 2026 10:55:29 +0300 Subject: [PATCH 1/9] Adjust building entity and DTOs for building module implementation --- output.log | Bin 0 -> 40016 bytes .../org/unilab/uniplan/building/Building.java | 33 ++++++++++++ .../building/dto/BuildingRequestDto.java | 18 +++++++ .../building/dto/BuildingResponseDto.java | 10 ++++ .../org/unilab/uniplan/category/Category.java | 4 ++ .../unilab/uniplan/university/University.java | 10 ++++ .../20260723144301-create-building-table.yaml | 47 ++++++++++++++++++ .../db/changelog/db.changelog-master.yaml | 8 +++ .../uniplan/faculty/FacultyMapperTest.java | 3 +- .../university/UniversityMapperTest.java | 4 +- 10 files changed, 134 insertions(+), 3 deletions(-) create mode 100644 output.log create mode 100644 src/main/java/org/unilab/uniplan/building/Building.java create mode 100644 src/main/java/org/unilab/uniplan/building/dto/BuildingRequestDto.java create mode 100644 src/main/java/org/unilab/uniplan/building/dto/BuildingResponseDto.java create mode 100644 src/main/resources/db/changelog/20260723144301-create-building-table.yaml diff --git a/output.log b/output.log new file mode 100644 index 0000000000000000000000000000000000000000..9316c9e38a35836e966c02bd719a217d66092c41 GIT binary patch literal 40016 zcmeI5`)?b^5y$V>7U=OcqHA!*r`EDMPdS(Y0IwyVl^(?T**N}?1wdPS0By9n~9 zxBY&0G~RoWJW@xRatMOty>@qYc6Q#o_rL$Pl6G>G{E{3eZ<9u{tnc^9O0tnWOCIa{ z*KlP;SI?4ea-Ll1{~+1Xe>d6H)g%31BtIr6;p{-QG?LdzNA-1*R&uD{<78h)&y)9| zbXO%BdcLExMzXEa-B4ynR}S@|);}eG6a*K#f2g*Ob!|DB*V#@ecNWfg<}bRIh5kG_ z4e-%sQ?;MybD`(Yk~g81KPUH9=0s-~dXisYJkir$-)~7hAL?sdet>gT{R3zFYVAz8 zcr1AKbnMTh{~3d4x_2S0ovO@^u&}2uxZ&R1fDvk`SFHg!URK>Umf-3O)w-FyO1{+p zhT1s_t#^~PkPpY%6c8`)<`hTmoIvO3Qk~=zHRLNcaf=8||2v>J>?WWG=^}D0?`E7?0-_}1P zV|Z%nUQ186^rYeMOcYi=p4Q8TplSx_I%)?zGy}$*QktrJM;~x-o_sCt@I;?x@>28U zna-YydL9aw7a9Yw{)_$(1UbhIVW|^FjM3ZIIrEM&iaZA_`$KIVw=JAR?i9gU^Y18^CnP~b(NJtzZ?3ohW5oI^*PgM&r}Ka`I6p(i}SXgfZQ ztCsfy?rv0lq>@J2%;-iKdpIgQ$oQAhZ;|Jp2k4+n+TPK)zf?``gE;p$%} zGyO!kWM&|b9;s#UwVV7+ZSJXUpd2J?oCoZvt|LJWe?}gF=a%X<4h83Hg|T5I?fS9q z!X>~CT%jd8+*Ye_RYn7d;%I)J!3WZfnvta62B>WYe>&V?Uwt<%0Dc3o{|b4{*-Bf%oyu#pORWFh5R2C2#?>6q{4i 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 J740@g;{R0p@tObt literal 0 HcmV?d00001 diff --git a/src/main/java/org/unilab/uniplan/building/Building.java b/src/main/java/org/unilab/uniplan/building/Building.java new file mode 100644 index 00000000..3212029e --- /dev/null +++ b/src/main/java/org/unilab/uniplan/building/Building.java @@ -0,0 +1,33 @@ +package org.unilab.uniplan.building; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.unilab.uniplan.common.model.BaseEntity; +import org.unilab.uniplan.university.University; + +@Entity +@Table(name = "building") +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class Building extends BaseEntity { + + @Column(name = "name", nullable = false, length = 50) + private String name; + + @Column(name = "address", nullable = false, length = 100) + private String address; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "university_id", referencedColumnName = "id") + private University university; +} diff --git a/src/main/java/org/unilab/uniplan/building/dto/BuildingRequestDto.java b/src/main/java/org/unilab/uniplan/building/dto/BuildingRequestDto.java new file mode 100644 index 00000000..73716bbb --- /dev/null +++ b/src/main/java/org/unilab/uniplan/building/dto/BuildingRequestDto.java @@ -0,0 +1,18 @@ +package org.unilab.uniplan.building.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import java.util.UUID; + +public class BuildingRequestDto { + UUID id; + @NotBlank(message="Name is required") + @Size(max = 100) + String name; + @NotBlank(message="Address is required") + @Size(max = 100) + String address; + @NotBlank(message = "University is required") + @Size(max = 100) + String university; +} diff --git a/src/main/java/org/unilab/uniplan/building/dto/BuildingResponseDto.java b/src/main/java/org/unilab/uniplan/building/dto/BuildingResponseDto.java new file mode 100644 index 00000000..90c5540d --- /dev/null +++ b/src/main/java/org/unilab/uniplan/building/dto/BuildingResponseDto.java @@ -0,0 +1,10 @@ +package org.unilab.uniplan.building.dto; + +import java.util.UUID; + +public class BuildingResponseDto { + UUID id; + String name; + String address; + String university; +} diff --git a/src/main/java/org/unilab/uniplan/category/Category.java b/src/main/java/org/unilab/uniplan/category/Category.java index 437268f9..ff0ca278 100644 --- a/src/main/java/org/unilab/uniplan/category/Category.java +++ b/src/main/java/org/unilab/uniplan/category/Category.java @@ -19,6 +19,10 @@ public class Category extends BaseEntity { @Column(name = "room_type", nullable = false, length = 50) private String roomType; + @Column(name = "capacity", nullable = false) private short capacity; + + @Column(name = "description", nullable = true, length = 100) + private String description; } \ No newline at end of file diff --git a/src/main/java/org/unilab/uniplan/university/University.java b/src/main/java/org/unilab/uniplan/university/University.java index e110fd80..b2078532 100644 --- a/src/main/java/org/unilab/uniplan/university/University.java +++ b/src/main/java/org/unilab/uniplan/university/University.java @@ -2,12 +2,16 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.OneToMany; import jakarta.persistence.Table; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import org.unilab.uniplan.building.Building; import org.unilab.uniplan.common.model.BaseEntity; +import java.util.ArrayList; +import java.util.List; @Entity @Table(name = "university") @@ -19,13 +23,19 @@ public class University extends BaseEntity { @Column(name = "uni_name", nullable = false) private String uniName; + @Column(name = "location", length = 500) private String location; + @Column(name = "established_year") private short establishedYear; + @Column(name = "accreditation", length = 200) private String accreditation; + @Column(name = "website", length = 2048) private String website; + @OneToMany(mappedBy="university") + private List buildings= new ArrayList<>(); } \ No newline at end of file diff --git a/src/main/resources/db/changelog/20260723144301-create-building-table.yaml b/src/main/resources/db/changelog/20260723144301-create-building-table.yaml new file mode 100644 index 00000000..e0aa5dcb --- /dev/null +++ b/src/main/resources/db/changelog/20260723144301-create-building-table.yaml @@ -0,0 +1,47 @@ +databaseChangeLog: + - changeSet: + id: 20260723144300-1 + author: pipetkova19 + changes: + - createTable: + tableName: building + columns: + - column: + name: id + type: UUID + constraints: + nullable: false + primaryKey: true + primaryKeyName: pk_building + - column: + name: name + type: varchar(50) + constraints: + nullable: false + - column: + name: address + type: varchar(100) + constraints: + nullable: false + - column: + name: university_id + type: UUID + - column: + constraints: + nullable: false + name: created_at + type: DATETIME + - column: + name: updated_at + type: DATETIME + + - changeSet: + id: 20260723144300-2 + author: pipetkova19 + changes: + - addForeignKeyConstraint: + baseTableName: building + baseColumnNames: university_id + constraintName: FK_BUILDING_ON_UNIVERSITY + referencedTableName: university + referencedColumnNames: id \ No newline at end of file diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index 10432196..6f9b8cd7 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -13,3 +13,11 @@ databaseChangeLog: file: db/changelog/20260626144400-drop-column-deleted-at-major.yaml - include: file: db/changelog/20260626144500-add-column-is-deleted-to-course.yaml + - include: + file: db/changelog/20260723130700-add-available-seats-to-room.yaml + - include: + file: db/changelog/20260723134100-drop-not-null-constraint-faculty-room.yaml + - include: + file: db/changelog/20260723141400-add-description-column-to-category.yaml + - include: + file: db/changelog/20260723144301-create-building-table.yaml \ No newline at end of file diff --git a/src/test/java/org/unilab/uniplan/faculty/FacultyMapperTest.java b/src/test/java/org/unilab/uniplan/faculty/FacultyMapperTest.java index 7873a7e1..f76b0566 100644 --- a/src/test/java/org/unilab/uniplan/faculty/FacultyMapperTest.java +++ b/src/test/java/org/unilab/uniplan/faculty/FacultyMapperTest.java @@ -31,7 +31,8 @@ void setUp() { "Burgas", (short) 1999, "accreditation", - "www.pu.com" + "www.pu.com", + null ); university.setId(universityId); diff --git a/src/test/java/org/unilab/uniplan/university/UniversityMapperTest.java b/src/test/java/org/unilab/uniplan/university/UniversityMapperTest.java index fba6840f..457dd52c 100644 --- a/src/test/java/org/unilab/uniplan/university/UniversityMapperTest.java +++ b/src/test/java/org/unilab/uniplan/university/UniversityMapperTest.java @@ -27,10 +27,10 @@ void setUp() { "New Name", "New Location", (short) 2000, "New Acc", "https://new.bg" ); university = new University( - "Sofia University", "Sofia, Bulgaria", (short) 1888, "NEAA", "https://uni-sofia.bg" + "Sofia University", "Sofia, Bulgaria", (short) 1888, "NEAA", "https://uni-sofia.bg", null ); university2 = new University( - "Plovdiv University", "Plovdiv", (short) 1961, "NEAA", "https://uni-plovdiv.bg" + "Plovdiv University", "Plovdiv", (short) 1961, "NEAA", "https://uni-plovdiv.bg", null ); id = UUID.randomUUID(); } From d1ed76deec2d6d010f1b2d8cf86220e5bccc130a Mon Sep 17 00:00:00 2001 From: Djesika Vacheva Date: Tue, 4 Aug 2026 11:03:58 +0300 Subject: [PATCH 2/9] create repository and service layers --- .../uniplan/building/BuildingRepository.java | 10 ++++++ .../uniplan/building/BuildingService.java | 35 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 src/main/java/org/unilab/uniplan/building/BuildingRepository.java create mode 100644 src/main/java/org/unilab/uniplan/building/BuildingService.java diff --git a/src/main/java/org/unilab/uniplan/building/BuildingRepository.java b/src/main/java/org/unilab/uniplan/building/BuildingRepository.java new file mode 100644 index 00000000..5ae9ed3f --- /dev/null +++ b/src/main/java/org/unilab/uniplan/building/BuildingRepository.java @@ -0,0 +1,10 @@ +package org.unilab.uniplan.building; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.util.UUID; + +@Repository +public interface BuildingRepository extends JpaRepository { + +} diff --git a/src/main/java/org/unilab/uniplan/building/BuildingService.java b/src/main/java/org/unilab/uniplan/building/BuildingService.java new file mode 100644 index 00000000..7b9a4992 --- /dev/null +++ b/src/main/java/org/unilab/uniplan/building/BuildingService.java @@ -0,0 +1,35 @@ +package org.unilab.uniplan.building; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.unilab.uniplan.common.model.BaseService; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +@Service +@RequiredArgsConstructor +public class BuildingService implements BaseService { + + private final BuildingRepository buildingRepository; + + @Override + public void save(final Building building) { + buildingRepository.save(building); + } + + @Override + public List getAll() { + return buildingRepository.findAll(); + } + + @Override + public Optional getById(final UUID id) { + return buildingRepository.findById(id); + } + + @Override + public void delete(Building building) { + buildingRepository.delete(building); + } +} From 9d39bf5bd7186098f3587bd49db9e682486a6993 Mon Sep 17 00:00:00 2001 From: Djesika Vacheva Date: Tue, 4 Aug 2026 11:15:20 +0300 Subject: [PATCH 3/9] update DTOs, add BuildingMapper --- .../uniplan/building/BuildingMapper.java | 22 +++++++++++++++++++ .../building/dto/BuildingRequestDto.java | 16 +++++++------- .../building/dto/BuildingResponseDto.java | 13 ++++++----- 3 files changed, 37 insertions(+), 14 deletions(-) create mode 100644 src/main/java/org/unilab/uniplan/building/BuildingMapper.java diff --git a/src/main/java/org/unilab/uniplan/building/BuildingMapper.java b/src/main/java/org/unilab/uniplan/building/BuildingMapper.java new file mode 100644 index 00000000..0507843c --- /dev/null +++ b/src/main/java/org/unilab/uniplan/building/BuildingMapper.java @@ -0,0 +1,22 @@ +package org.unilab.uniplan.building; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.unilab.uniplan.building.dto.BuildingRequestDto; +import org.unilab.uniplan.building.dto.BuildingResponseDto; +import java.util.List; + +@Mapper +public interface BuildingMapper { + @Mapping(target = "id", ignore = true) + Building toEntity(final BuildingRequestDto buildingRequestDto); + + BuildingResponseDto toResponseDto(final Building building); + + List toResponseDtoList(final List buildings); + + @Mapping(target = "id", ignore = true) + void updateEntityFromDto(final BuildingRequestDto buildingRequestDto, + @MappingTarget final Building building); +} diff --git a/src/main/java/org/unilab/uniplan/building/dto/BuildingRequestDto.java b/src/main/java/org/unilab/uniplan/building/dto/BuildingRequestDto.java index 73716bbb..eb25078d 100644 --- a/src/main/java/org/unilab/uniplan/building/dto/BuildingRequestDto.java +++ b/src/main/java/org/unilab/uniplan/building/dto/BuildingRequestDto.java @@ -1,18 +1,18 @@ package org.unilab.uniplan.building.dto; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import java.util.UUID; -public class BuildingRequestDto { - UUID id; +public record BuildingRequestDto ( + UUID id, @NotBlank(message="Name is required") @Size(max = 100) - String name; + String name, @NotBlank(message="Address is required") @Size(max = 100) - String address; - @NotBlank(message = "University is required") - @Size(max = 100) - String university; -} + String address, + @NotNull(message = "University is required") + UUID universityId +){} diff --git a/src/main/java/org/unilab/uniplan/building/dto/BuildingResponseDto.java b/src/main/java/org/unilab/uniplan/building/dto/BuildingResponseDto.java index 90c5540d..961a7c0b 100644 --- a/src/main/java/org/unilab/uniplan/building/dto/BuildingResponseDto.java +++ b/src/main/java/org/unilab/uniplan/building/dto/BuildingResponseDto.java @@ -2,9 +2,10 @@ import java.util.UUID; -public class BuildingResponseDto { - UUID id; - String name; - String address; - String university; -} +public record BuildingResponseDto ( + UUID id, + String name, + String address, + UUID universityId, + String universityName +){} From 91f5ddb5ae478318d7ad909737c9ff0bd1ac9f5c Mon Sep 17 00:00:00 2001 From: Djesika Vacheva Date: Tue, 4 Aug 2026 11:31:20 +0300 Subject: [PATCH 4/9] add BUILDING_NOT_FOUND error constant, add BuildingWebFacade --- .../uniplan/building/BuildingWebFacade.java | 72 +++++++++++++++++++ .../unilab/uniplan/utils/ErrorConstants.java | 3 +- 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/unilab/uniplan/building/BuildingWebFacade.java diff --git a/src/main/java/org/unilab/uniplan/building/BuildingWebFacade.java b/src/main/java/org/unilab/uniplan/building/BuildingWebFacade.java new file mode 100644 index 00000000..64f23c75 --- /dev/null +++ b/src/main/java/org/unilab/uniplan/building/BuildingWebFacade.java @@ -0,0 +1,72 @@ +package org.unilab.uniplan.building; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; +import org.unilab.uniplan.building.dto.BuildingRequestDto; +import org.unilab.uniplan.building.dto.BuildingResponseDto; +import org.unilab.uniplan.exception.ResourceNotFoundException; +import org.unilab.uniplan.university.University; +import org.unilab.uniplan.university.UniversityService; +import java.util.List; +import java.util.UUID; + +import static org.unilab.uniplan.utils.ErrorConstants.BUILDING_NOT_FOUND; +import static org.unilab.uniplan.utils.ErrorConstants.UNIVERSITY_NOT_FOUND; + +@Component +@Slf4j +@RequiredArgsConstructor +public class BuildingWebFacade { + private final BuildingService buildingService; + private final BuildingMapper buildingMapper; + private final UniversityService universityService; + + @Transactional + public void createBuilding(final BuildingRequestDto request) { + final Building building = buildingMapper.toEntity(request); + building.setUniversity(getUniversityOrThrow(request.universityId())); + buildingService.save(building); + } + + @Transactional + public void updateBuilding(final UUID id, final BuildingRequestDto request) { + final Building building = getBuildingOrThrow(id); + buildingMapper.updateEntityFromDto(request, building); + building.setUniversity(getUniversityOrThrow(request.universityId())); + buildingService.save(building); + log.info("updated building with ID: {}", building.getId()); + } + + @Transactional(readOnly = true) + public BuildingResponseDto getBuildingById(final UUID id) { + final Building building = getBuildingOrThrow(id); + return buildingMapper.toResponseDto(building); + } + + @Transactional + public void deleteBuilding(final UUID id) { + final Building building = getBuildingOrThrow(id); + buildingService.delete(building); + log.info("deleted building with ID: {}", id); + } + + @Transactional(readOnly = true) + public List getAllBuildings() { + return buildingMapper.toResponseDtoList(buildingService.getAll()); + } + + private Building getBuildingOrThrow(final UUID id) { + return buildingService.getById(id) + .orElseThrow(() -> new ResourceNotFoundException( + BUILDING_NOT_FOUND.getMessage(String.valueOf(id))) + ); + } + + private University getUniversityOrThrow(final UUID universityId) { + return universityService.getById(universityId) + .orElseThrow(() -> new ResourceNotFoundException( + UNIVERSITY_NOT_FOUND.getMessage(String.valueOf(universityId)))); + } +} diff --git a/src/main/java/org/unilab/uniplan/utils/ErrorConstants.java b/src/main/java/org/unilab/uniplan/utils/ErrorConstants.java index a5d9bdca..77c58ed8 100644 --- a/src/main/java/org/unilab/uniplan/utils/ErrorConstants.java +++ b/src/main/java/org/unilab/uniplan/utils/ErrorConstants.java @@ -19,7 +19,8 @@ public enum ErrorConstants { STUDENT_GROUP_NOT_FOUND("Student group with ID {0} not found."), ROOM_CATEGORY_NOT_FOUND("Room category with ID {0} not found."), PROGRAM_DISCIPLINE_NOT_FOUND("Program discipline with ID {0} not found."), - PROGRAM_DISCIPLINE_LECTOR_NOT_FOUND("Program discipline lector with ID {0} not found."); + PROGRAM_DISCIPLINE_LECTOR_NOT_FOUND("Program discipline lector with ID {0} not found."), + BUILDING_NOT_FOUND("Building with ID {0} not found."); private final String message; From e70bd27924390b2db044cabff5e1a6bc79e5e8c9 Mon Sep 17 00:00:00 2001 From: Djesika Vacheva Date: Tue, 4 Aug 2026 11:52:30 +0300 Subject: [PATCH 5/9] add BuildingController --- .../uniplan/building/BuildingController.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/main/java/org/unilab/uniplan/building/BuildingController.java diff --git a/src/main/java/org/unilab/uniplan/building/BuildingController.java b/src/main/java/org/unilab/uniplan/building/BuildingController.java new file mode 100644 index 00000000..d2bb2179 --- /dev/null +++ b/src/main/java/org/unilab/uniplan/building/BuildingController.java @@ -0,0 +1,58 @@ +package org.unilab.uniplan.building; + +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.unilab.uniplan.building.dto.BuildingRequestDto; +import org.unilab.uniplan.building.dto.BuildingResponseDto; +import java.util.List; +import java.util.UUID; + +@RestController +@RequestMapping("/buildings") +@RequiredArgsConstructor +@Tag(name = "Buildings", description = "Manage university buildings, including name, address, and associated university") +public class BuildingController { + private final BuildingWebFacade buildingWebFacade; + + @PostMapping + public ResponseEntity createBuilding(@Valid @NotNull @RequestBody final BuildingRequestDto buildingRequestDto) { + buildingWebFacade.createBuilding(buildingRequestDto); + return ResponseEntity.status(HttpStatus.CREATED).build(); + } + + @GetMapping + public ResponseEntity> getAllBuildings() { + return ResponseEntity.ok(buildingWebFacade.getAllBuildings()); + } + + @GetMapping("/{id}") + public ResponseEntity getBuildingById(@PathVariable final UUID id) { + return ResponseEntity.ok(buildingWebFacade.getBuildingById(id)); + } + + @PutMapping("/{id}") + public ResponseEntity updateBuilding( + @PathVariable final UUID id, + @Valid @NotNull @RequestBody final BuildingRequestDto buildingRequestDto) { + buildingWebFacade.updateBuilding(id, buildingRequestDto); + return ResponseEntity.noContent().build(); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteBuilding(@PathVariable final UUID id) { + buildingWebFacade.deleteBuilding(id); + return ResponseEntity.noContent().build(); + } +} From 72287177a4eeb10f3dd48a34443dca98e952d26a Mon Sep 17 00:00:00 2001 From: Djesika Vacheva Date: Tue, 4 Aug 2026 13:17:26 +0300 Subject: [PATCH 6/9] ad BuildingServiceTest --- .../uniplan/building/BuildingServiceTest.java | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 src/test/java/org/unilab/uniplan/building/BuildingServiceTest.java diff --git a/src/test/java/org/unilab/uniplan/building/BuildingServiceTest.java b/src/test/java/org/unilab/uniplan/building/BuildingServiceTest.java new file mode 100644 index 00000000..6edc6145 --- /dev/null +++ b/src/test/java/org/unilab/uniplan/building/BuildingServiceTest.java @@ -0,0 +1,79 @@ +package org.unilab.uniplan.building; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class BuildingServiceTest { + + @Mock + private BuildingRepository buildingRepository; + + @InjectMocks + private BuildingService buildingService; + + private Building building; + private UUID buildingId; + + @BeforeEach + void setUp() { + buildingId = UUID.randomUUID(); + building = new Building(); + } + + @Test + void saveShouldSaveBuilding() { + buildingService.save(building); + + verify(buildingRepository).save(building); + } + + @Test + void getByIdShouldReturnBuildingOptionalIfBuildingExists() { + when(buildingRepository.findById(buildingId)).thenReturn(Optional.of(building)); + + final Optional result = buildingService.getById(buildingId); + + assertEquals(Optional.of(building), result); + verify(buildingRepository).findById(buildingId); + } + + @Test + void getByIdShouldReturnEmptyOptionalIfBuildingDoesNotExist() { + when(buildingRepository.findById(buildingId)).thenReturn(Optional.empty()); + + final Optional result = buildingService.getById(buildingId); + + assertEquals(Optional.empty(), result); + verify(buildingRepository).findById(buildingId); + } + + @Test + void getAllShouldReturnListOfBuildings() { + final List buildings = List.of(building); + when(buildingRepository.findAll()).thenReturn(buildings); + + final List result = buildingService.getAll(); + + assertEquals(buildings, result); + verify(buildingRepository).findAll(); + } + + @Test + void deleteShouldDeleteBuilding() { + buildingService.delete(building); + + verify(buildingRepository).delete(building); + } +} \ No newline at end of file From 2d4c41bc54b99c9a38d5594b69292c05e1e48a28 Mon Sep 17 00:00:00 2001 From: Djesika Vacheva Date: Tue, 4 Aug 2026 13:20:59 +0300 Subject: [PATCH 7/9] add BuildingWebFacadeTest --- .../building/BuildingWebFacadeTest.java | 185 ++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 src/test/java/org/unilab/uniplan/building/BuildingWebFacadeTest.java diff --git a/src/test/java/org/unilab/uniplan/building/BuildingWebFacadeTest.java b/src/test/java/org/unilab/uniplan/building/BuildingWebFacadeTest.java new file mode 100644 index 00000000..0ca67d83 --- /dev/null +++ b/src/test/java/org/unilab/uniplan/building/BuildingWebFacadeTest.java @@ -0,0 +1,185 @@ +package org.unilab.uniplan.building; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InOrder; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.unilab.uniplan.building.dto.BuildingRequestDto; +import org.unilab.uniplan.building.dto.BuildingResponseDto; +import org.unilab.uniplan.exception.ResourceNotFoundException; +import org.unilab.uniplan.university.University; +import org.unilab.uniplan.university.UniversityService; + +@ExtendWith(MockitoExtension.class) +class BuildingWebFacadeTest { + + @Mock + private BuildingService buildingService; + + @Mock + private BuildingMapper buildingMapper; + + @Mock + private UniversityService universityService; + + @InjectMocks + private BuildingWebFacade buildingWebFacade; + + private UUID buildingId; + private UUID universityId; + private Building building; + private University university; + private BuildingRequestDto requestDto; + private BuildingResponseDto responseDto; + + @BeforeEach + void setUp() { + buildingId = UUID.randomUUID(); + universityId = UUID.randomUUID(); + building = new Building(); + university = new University(); + requestDto = mock(BuildingRequestDto.class); + responseDto = mock(BuildingResponseDto.class); + } + + @Test + void createBuildingShouldMapResolveUniversityAndSaveBuilding() { + when(requestDto.universityId()).thenReturn(universityId); + when(buildingMapper.toEntity(requestDto)).thenReturn(building); + when(universityService.getById(universityId)).thenReturn(Optional.of(university)); + + buildingWebFacade.createBuilding(requestDto); + + final InOrder inOrder = inOrder(buildingMapper, universityService, buildingService); + inOrder.verify(buildingMapper).toEntity(requestDto); + inOrder.verify(universityService).getById(universityId); + inOrder.verify(buildingService).save(building); + assertEquals(university, building.getUniversity()); + } + + @Test + void createBuildingShouldThrowIfUniversityDoesNotExist() { + when(requestDto.universityId()).thenReturn(universityId); + when(buildingMapper.toEntity(requestDto)).thenReturn(building); + when(universityService.getById(universityId)).thenReturn(Optional.empty()); + + assertThrows(ResourceNotFoundException.class, + () -> buildingWebFacade.createBuilding(requestDto)); + + verify(buildingService, never()).save(any(Building.class)); + } + + @Test + void getAllBuildingsShouldReturnResponseDtoList() { + final List buildings = List.of(building); + final List responseDtos = List.of(responseDto); + + when(buildingService.getAll()).thenReturn(buildings); + when(buildingMapper.toResponseDtoList(buildings)).thenReturn(responseDtos); + + final List result = buildingWebFacade.getAllBuildings(); + + assertEquals(responseDtos, result); + verify(buildingService).getAll(); + verify(buildingMapper).toResponseDtoList(buildings); + } + + @Test + void getBuildingByIdShouldReturnResponseDtoIfFound() { + when(buildingService.getById(buildingId)).thenReturn(Optional.of(building)); + when(buildingMapper.toResponseDto(building)).thenReturn(responseDto); + + final BuildingResponseDto result = buildingWebFacade.getBuildingById(buildingId); + + assertEquals(responseDto, result); + verify(buildingService).getById(buildingId); + verify(buildingMapper).toResponseDto(building); + } + + @Test + void getBuildingByIdShouldThrowIfBuildingDoesNotExist() { + when(buildingService.getById(buildingId)).thenReturn(Optional.empty()); + + assertThrows(ResourceNotFoundException.class, + () -> buildingWebFacade.getBuildingById(buildingId)); + + verify(buildingService).getById(buildingId); + verify(buildingMapper, never()).toResponseDto(any(Building.class)); + } + + @Test + void updateBuildingShouldUpdateResolveUniversityAndSaveBuildingIfFound() { + when(requestDto.universityId()).thenReturn(universityId); + when(buildingService.getById(buildingId)).thenReturn(Optional.of(building)); + when(universityService.getById(universityId)).thenReturn(Optional.of(university)); + + buildingWebFacade.updateBuilding(buildingId, requestDto); + + final InOrder inOrder = inOrder(buildingService, buildingMapper, universityService); + inOrder.verify(buildingService).getById(buildingId); + inOrder.verify(buildingMapper).updateEntityFromDto(requestDto, building); + inOrder.verify(universityService).getById(universityId); + inOrder.verify(buildingService).save(building); + assertEquals(university, building.getUniversity()); + } + + @Test + void updateBuildingShouldThrowIfBuildingDoesNotExist() { + when(buildingService.getById(buildingId)).thenReturn(Optional.empty()); + + assertThrows(ResourceNotFoundException.class, + () -> buildingWebFacade.updateBuilding(buildingId, requestDto)); + + verify(buildingService).getById(buildingId); + verify(buildingMapper, never()).updateEntityFromDto(any(BuildingRequestDto.class), any(Building.class)); + verify(buildingService, never()).save(any(Building.class)); + } + + @Test + void updateBuildingShouldThrowIfUniversityDoesNotExist() { + when(requestDto.universityId()).thenReturn(universityId); + when(buildingService.getById(buildingId)).thenReturn(Optional.of(building)); + when(universityService.getById(universityId)).thenReturn(Optional.empty()); + + assertThrows(ResourceNotFoundException.class, + () -> buildingWebFacade.updateBuilding(buildingId, requestDto)); + + verify(buildingService, never()).save(any(Building.class)); + } + + @Test + void deleteBuildingShouldDeleteBuildingIfFound() { + when(buildingService.getById(buildingId)).thenReturn(Optional.of(building)); + + buildingWebFacade.deleteBuilding(buildingId); + + verify(buildingService).getById(buildingId); + verify(buildingService).delete(building); + } + + @Test + void deleteBuildingShouldThrowIfBuildingDoesNotExist() { + when(buildingService.getById(buildingId)).thenReturn(Optional.empty()); + + assertThrows(ResourceNotFoundException.class, + () -> buildingWebFacade.deleteBuilding(buildingId)); + + verify(buildingService).getById(buildingId); + verify(buildingService, never()).delete(any(Building.class)); + } +} \ No newline at end of file From 5b3c3c407dd79173282c1a568d9eb7296ac14128 Mon Sep 17 00:00:00 2001 From: Djesika Vacheva Date: Wed, 5 Aug 2026 10:26:42 +0300 Subject: [PATCH 8/9] Add BuildingValidator and BuildingValidatorTest, update BuildingWebFacade and tests --- .../uniplan/building/BuildingValidator.java | 29 ++++++++++ .../uniplan/building/BuildingWebFacade.java | 7 +++ .../building/BuildingValidatorTest.java | 58 +++++++++++++++++++ .../building/BuildingWebFacadeTest.java | 11 +++- 4 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/unilab/uniplan/building/BuildingValidator.java create mode 100644 src/test/java/org/unilab/uniplan/building/BuildingValidatorTest.java diff --git a/src/main/java/org/unilab/uniplan/building/BuildingValidator.java b/src/main/java/org/unilab/uniplan/building/BuildingValidator.java new file mode 100644 index 00000000..60a617c2 --- /dev/null +++ b/src/main/java/org/unilab/uniplan/building/BuildingValidator.java @@ -0,0 +1,29 @@ +package org.unilab.uniplan.building; + +import static org.unilab.uniplan.utils.ErrorConstants.UNIVERSITY_NOT_FOUND; + +import java.util.UUID; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.unilab.uniplan.building.dto.BuildingRequestDto; +import org.unilab.uniplan.exception.ResourceNotFoundException; +import org.unilab.uniplan.university.UniversityRepository; + +@Component +@RequiredArgsConstructor +public class BuildingValidator { + + private final UniversityRepository universityRepository; + + public void validate(final BuildingRequestDto requestDto) { + validateUniversityExists(requestDto.universityId()); + } + + private void validateUniversityExists(final UUID universityId) { + if (!universityRepository.existsById(universityId)) { + throw new ResourceNotFoundException( + UNIVERSITY_NOT_FOUND.getMessage(String.valueOf(universityId)) + ); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/unilab/uniplan/building/BuildingWebFacade.java b/src/main/java/org/unilab/uniplan/building/BuildingWebFacade.java index 64f23c75..ce295a55 100644 --- a/src/main/java/org/unilab/uniplan/building/BuildingWebFacade.java +++ b/src/main/java/org/unilab/uniplan/building/BuildingWebFacade.java @@ -22,16 +22,23 @@ public class BuildingWebFacade { private final BuildingService buildingService; private final BuildingMapper buildingMapper; private final UniversityService universityService; + private final BuildingValidator buildingValidator; @Transactional public void createBuilding(final BuildingRequestDto request) { + buildingValidator.validate(request); + final Building building = buildingMapper.toEntity(request); building.setUniversity(getUniversityOrThrow(request.universityId())); buildingService.save(building); + + log.info("created building with ID: {}", building.getId()); } @Transactional public void updateBuilding(final UUID id, final BuildingRequestDto request) { + buildingValidator.validate(request); + final Building building = getBuildingOrThrow(id); buildingMapper.updateEntityFromDto(request, building); building.setUniversity(getUniversityOrThrow(request.universityId())); diff --git a/src/test/java/org/unilab/uniplan/building/BuildingValidatorTest.java b/src/test/java/org/unilab/uniplan/building/BuildingValidatorTest.java new file mode 100644 index 00000000..63f05784 --- /dev/null +++ b/src/test/java/org/unilab/uniplan/building/BuildingValidatorTest.java @@ -0,0 +1,58 @@ +package org.unilab.uniplan.building; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.unilab.uniplan.building.dto.BuildingRequestDto; +import org.unilab.uniplan.exception.ResourceNotFoundException; +import org.unilab.uniplan.university.UniversityRepository; + +@ExtendWith(MockitoExtension.class) +class BuildingValidatorTest { + + @Mock + private UniversityRepository universityRepository; + + @InjectMocks + private BuildingValidator buildingValidator; + + private UUID universityId; + private BuildingRequestDto requestDto; + + @BeforeEach + void setUp() { + universityId = UUID.randomUUID(); + requestDto = mock(BuildingRequestDto.class); + + when(requestDto.universityId()).thenReturn(universityId); + } + + @Test + void validate_shouldPass_whenUniversityExists() { + when(universityRepository.existsById(universityId)).thenReturn(true); + + assertDoesNotThrow(() -> buildingValidator.validate(requestDto)); + + verify(universityRepository).existsById(universityId); + } + + @Test + void validate_shouldThrow_whenUniversityDoesNotExist() { + when(universityRepository.existsById(universityId)).thenReturn(false); + + assertThrows(ResourceNotFoundException.class, + () -> buildingValidator.validate(requestDto)); + + verify(universityRepository).existsById(universityId); + } +} \ No newline at end of file diff --git a/src/test/java/org/unilab/uniplan/building/BuildingWebFacadeTest.java b/src/test/java/org/unilab/uniplan/building/BuildingWebFacadeTest.java index 0ca67d83..a46ebc2b 100644 --- a/src/test/java/org/unilab/uniplan/building/BuildingWebFacadeTest.java +++ b/src/test/java/org/unilab/uniplan/building/BuildingWebFacadeTest.java @@ -37,6 +37,9 @@ class BuildingWebFacadeTest { @Mock private UniversityService universityService; + @Mock + private BuildingValidator buildingValidator; + @InjectMocks private BuildingWebFacade buildingWebFacade; @@ -65,7 +68,8 @@ void createBuildingShouldMapResolveUniversityAndSaveBuilding() { buildingWebFacade.createBuilding(requestDto); - final InOrder inOrder = inOrder(buildingMapper, universityService, buildingService); + final InOrder inOrder = inOrder(buildingValidator, buildingMapper, universityService, buildingService); + inOrder.verify(buildingValidator).validate(requestDto); inOrder.verify(buildingMapper).toEntity(requestDto); inOrder.verify(universityService).getById(universityId); inOrder.verify(buildingService).save(building); @@ -81,6 +85,7 @@ void createBuildingShouldThrowIfUniversityDoesNotExist() { assertThrows(ResourceNotFoundException.class, () -> buildingWebFacade.createBuilding(requestDto)); + verify(buildingValidator).validate(requestDto); verify(buildingService, never()).save(any(Building.class)); } @@ -130,7 +135,8 @@ void updateBuildingShouldUpdateResolveUniversityAndSaveBuildingIfFound() { buildingWebFacade.updateBuilding(buildingId, requestDto); - final InOrder inOrder = inOrder(buildingService, buildingMapper, universityService); + final InOrder inOrder = inOrder(buildingValidator, buildingService, buildingMapper, universityService); + inOrder.verify(buildingValidator).validate(requestDto); inOrder.verify(buildingService).getById(buildingId); inOrder.verify(buildingMapper).updateEntityFromDto(requestDto, building); inOrder.verify(universityService).getById(universityId); @@ -145,6 +151,7 @@ void updateBuildingShouldThrowIfBuildingDoesNotExist() { assertThrows(ResourceNotFoundException.class, () -> buildingWebFacade.updateBuilding(buildingId, requestDto)); + verify(buildingValidator).validate(requestDto); verify(buildingService).getById(buildingId); verify(buildingMapper, never()).updateEntityFromDto(any(BuildingRequestDto.class), any(Building.class)); verify(buildingService, never()).save(any(Building.class)); From 901aad1f2579e5a1b6dca4c20945baf24d5f0775 Mon Sep 17 00:00:00 2001 From: Djesika Vacheva Date: Mon, 10 Aug 2026 11:26:41 +0300 Subject: [PATCH 9/9] fix mapper N+1, request DTO id field, DTO size constraints --- pr.diff | Bin 0 -> 65124 bytes .../unilab/uniplan/building/BuildingMapper.java | 4 ++++ .../building/dto/BuildingRequestDto.java | 5 ++--- 3 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 pr.diff diff --git a/pr.diff b/pr.diff new file mode 100644 index 0000000000000000000000000000000000000000..036f8e134aa2c46fc33a6fc8160b321c1c37699c GIT binary patch literal 65124 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 t4AA$O#z5E$`EAvFkMF}%y37|J^ toResponseDtoList(final List buildings); @Mapping(target = "id", ignore = true) + @Mapping(target = "university", ignore = true) void updateEntityFromDto(final BuildingRequestDto buildingRequestDto, @MappingTarget final Building building); } diff --git a/src/main/java/org/unilab/uniplan/building/dto/BuildingRequestDto.java b/src/main/java/org/unilab/uniplan/building/dto/BuildingRequestDto.java index eb25078d..1d0f1f7a 100644 --- a/src/main/java/org/unilab/uniplan/building/dto/BuildingRequestDto.java +++ b/src/main/java/org/unilab/uniplan/building/dto/BuildingRequestDto.java @@ -6,12 +6,11 @@ import java.util.UUID; public record BuildingRequestDto ( - UUID id, @NotBlank(message="Name is required") - @Size(max = 100) + @Size(max = 50) String name, @NotBlank(message="Address is required") - @Size(max = 100) + @Size(max = 50) String address, @NotNull(message = "University is required") UUID universityId