Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
132 commits
Select commit Hold shift + click to select a range
2def7e0
test(accommodation): add tests for mapper; use custom exception in se…
isanort Sep 29, 2025
208eb88
test(accommodation-filter): add tests for edge cases
isanort Sep 29, 2025
61413cb
test(accommodation-service): add AccommodationServiceHelperTest for u…
isanort Sep 29, 2025
643eef6
test(accommodation-filter): replace constructors with builders
isanort Sep 29, 2025
1dc44fd
test(data.sql): update passwords to hashed
isanort Sep 29, 2025
0389d6f
test(application.properties): disable mail
isanort Sep 29, 2025
015d60a
feat(jwt-service): add randon uuid and systemtimemillis to create uni…
isanort Sep 29, 2025
044fa2c
test(auth-controller): add register test with mocked email
isanort Sep 29, 2025
6e26e9f
test(cloudinary-service): add test for upload file with null folder
isanort Sep 29, 2025
45e9483
chore(application.properties): add optional variables for some proper…
isanort Sep 29, 2025
9333c26
refactor(AppProperties): remove dafault initializations
isanort Sep 29, 2025
173e1cd
feat(StartupValidationRunner): add logging info; add validateCloudina…
isanort Sep 29, 2025
2bfd15c
refactor(app-properties): separate into different specific files
isanort Sep 29, 2025
9cf2aae
feat(email): add validateEmailConfig method
isanort Sep 29, 2025
9f0e6e5
refactor(properties): update uses of DatasourceProperties and Cloudin…
isanort Sep 29, 2025
bc424c4
test(exceptions): add unit tests for GlobalExceptionHandler
isanort Sep 29, 2025
d4216d7
test(security-health): add tests for DatabaseHealthIndicator and Heal…
isanort Sep 29, 2025
1a8ab1e
test(jwt-filter): create unit test for JwtAuthFilter
isanort Sep 29, 2025
78f9153
test(jwt-service): add unit tests for refreash and isValid methods
isanort Sep 29, 2025
4a56100
test(security): add unit test for CustomAccessDeniedHandler
isanort Sep 29, 2025
6d7e0b3
test(email): add unit tests for EmailServiceHelper
isanort Sep 29, 2025
928f14a
fix(tests-email): remove unused AppProperties
isanort Sep 29, 2025
7610ea2
style: remove unused imports and commented lines
isanort Sep 29, 2025
0684a99
chore: trigger workflow with empty commit
isanort Sep 29, 2025
e7e78a7
feat(apimessage): add api message class, constructor and getter
thaisrqueiroz Sep 29, 2025
7c4cdea
refactor(reservation-service): change cancelReservation signature to …
thaisrqueiroz Sep 29, 2025
d1f05a6
refactor(reservation-service-impl): implement cancelReservation to re…
thaisrqueiroz Sep 29, 2025
b3f249a
refactor(reservation-controller): update cancelReservation endpoint t…
thaisrqueiroz Sep 29, 2025
6485691
test(reservation-service-impl-test): update cancelReservation test to…
thaisrqueiroz Sep 29, 2025
a49c791
test:(ReservationAdminServiceImplTest): add getAllReservationstoAdmin…
brunasonda Sep 29, 2025
3934eaa
feat: implement test send
PCalvoGarcia Sep 29, 2025
02283fa
feat(reservations): update status emails
PCalvoGarcia Sep 29, 2025
212461c
test(email): add test for email service and helper
PCalvoGarcia Sep 29, 2025
506a464
test(reservation): update reservation test
PCalvoGarcia Sep 29, 2025
9242dc1
chore: trigger workflow with empty commit
isanort Sep 29, 2025
cd6f0b7
test(email-service): fix existing EmailServiceHelperTest; move inside…
isanort Sep 30, 2025
10d6f0e
fix(reservation-service): fix return in cancel reservation method
isanort Sep 30, 2025
ef96df1
Merge branch 'dev' into refactor/tests
isanort Sep 30, 2025
1d526ba
chore: deployment update profile
PCalvoGarcia Sep 30, 2025
23c50eb
chore: update properties
PCalvoGarcia Sep 30, 2025
da6a1dd
chore: update properties
PCalvoGarcia Sep 30, 2025
d53c81a
chore: empty commit to trigger workflow
isanort Sep 30, 2025
f003148
chore: empty commit to trigger workflow
isanort Sep 30, 2025
9b4e338
fix(validator): comment cloudinary validator
isanort Sep 30, 2025
84c1b1f
Merge pull request #114 from Femcoders-SleepUp/refactor/tests
isanort Sep 30, 2025
90d3dc6
feat(reservation-email): add discount in amount
PCalvoGarcia Sep 30, 2025
99b51d7
Merge branch 'dev' into feature/implement-discount
PCalvoGarcia Sep 30, 2025
089b7f6
feat(reservation-owner): rename owner folder to accommodation owner; …
isanort Sep 30, 2025
9ef42c9
feat(reservation-guest): create folder for revervation guest; add rel…
isanort Sep 30, 2025
d38618b
refactor(reservation): replace ApiMessage with ApiMessageDto
isanort Sep 30, 2025
7708c6e
test(reservation): comment modified methods in code
isanort Sep 30, 2025
a361684
Merge pull request #118 from Femcoders-SleepUp/feature/reservation-up…
isanort Sep 30, 2025
c6ddbc1
test: update test for discout methods
PCalvoGarcia Sep 30, 2025
fb5e102
merge and resolve conflicts dev to feature/implement-discount
PCalvoGarcia Sep 30, 2025
430a54b
refactor(reservation): update refactor methods
PCalvoGarcia Sep 30, 2025
56fdf79
Merge pull request #119 from Femcoders-SleepUp/feature/implement-disc…
PCalvoGarcia Sep 30, 2025
224d890
fix: change status on reservations when you delete a user
PCalvoGarcia Sep 30, 2025
8cb6cb1
refactor(reservation-update): move updateStatus method to accommodati…
isanort Sep 30, 2025
603e5ed
Merge pull request #120 from Femcoders-SleepUp/fix/delete-user-cancel…
PCalvoGarcia Sep 30, 2025
4052f6e
feat(reservation-controller): add getAllReservationsForMyAccommodatio…
isanort Sep 30, 2025
a31de33
test(reservation): create ReservationOwner test and move related tests
isanort Sep 30, 2025
9a257f1
Merge pull request #121 from Femcoders-SleepUp/feature/reservation-up…
isanort Sep 30, 2025
ab4b602
test:method delete reservation
PaolaAPL17 Sep 30, 2025
4d13ab3
chore(swagger-config): add custom openapi swagger configuration
thaisrqueiroz Sep 30, 2025
4bc63b7
test(reservation): fix get and cancel tests in ReservationGuestContro…
isanort Sep 30, 2025
38c60b0
chore(accommodation-controller): enhance accommodation API documentat…
thaisrqueiroz Sep 30, 2025
092d03d
chore(accommodation-controller): enhance filter accommodation API doc…
thaisrqueiroz Sep 30, 2025
418ba9f
chore(accommodation-controller): enhance owner accommodation API docu…
thaisrqueiroz Sep 30, 2025
28dd730
chore(reservation-controller): enhance reservation API documentation …
thaisrqueiroz Sep 30, 2025
f7d78ec
chore(reservation-guest-controller): enhance guest reservation API do…
thaisrqueiroz Sep 30, 2025
0b1fcc0
chore(user-admin-controller): enhance admin user API documentation wi…
thaisrqueiroz Sep 30, 2025
9260323
chore(user-controller): enhance user API documentation with Swagger a…
thaisrqueiroz Sep 30, 2025
801a096
fix(reservation-repository): change oneYearAgo parameter in existsRes…
isanort Sep 30, 2025
0d71b2e
test(reservation-guest-controller): add update integration test
isanort Sep 30, 2025
2870d7d
test(reservation-controller): uncomment passing create reservation test
isanort Sep 30, 2025
9e90d7a
Merge pull request #124 from Femcoders-SleepUp/feature/reservation-up…
isanort Sep 30, 2025
88a81f6
test(validator): add unit test for StartupValidationRunner
isanort Sep 30, 2025
186ad2c
Merge branch 'dev' into feature/swagger
isanort Sep 30, 2025
2e84834
Merge pull request #123 from Femcoders-SleepUp/feature/swagger
isanort Sep 30, 2025
94a96db
Merge branch 'dev' into feature/delete-reservation-admin
isanort Sep 30, 2025
1dd06d2
Merge pull request #122 from Femcoders-SleepUp/feature/delete-reserva…
isanort Sep 30, 2025
8b9e18a
test(reservation-admin-controller): add integration tests
isanort Sep 30, 2025
315fc9c
ci: replace dummy Cloudinary env vars with GitHub Secrets in CI workflow
isanort Sep 30, 2025
50e088a
ci: trigger GitHub Actions run
isanort Sep 30, 2025
8357b03
Merge pull request #125 from Femcoders-SleepUp/refactor/tests
isanort Sep 30, 2025
9e76245
feat(accommodation-filter): add petFriendly field to filter and speci…
isanort Sep 30, 2025
b42c3f0
test(accommodation-filter): update tests with petFriendly field in se…
isanort Sep 30, 2025
ebf2401
Merge pull request #126 from Femcoders-SleepUp/feature/accommodation-…
isanort Sep 30, 2025
e5d39a2
test(accommodation-controller): add tests for edge security cases
isanort Sep 30, 2025
d340f64
feat(security-config): add request matchers for users/admin paths
isanort Sep 30, 2025
d44d0d9
test(user-admin-controller): add edge cases to test security; extract…
isanort Sep 30, 2025
2a40890
refactor(auth-service): modify register method to use common create u…
isanort Sep 30, 2025
d71d2b9
refactor(user-admin-service): modify create user with role method to …
isanort Sep 30, 2025
c9b8fc0
test(user-admin, auth): fix tests related to common create method in …
isanort Sep 30, 2025
8290d00
Merge pull request #127 from Femcoders-SleepUp/refactor/tests
isanort Sep 30, 2025
25e705f
refactor(user): modify update method in helper and update implementat…
isanort Sep 30, 2025
adcd967
test(user): modify tests with refactored update user method
isanort Sep 30, 2025
23bb59c
Merge pull request #128 from Femcoders-SleepUp/refactor/tests
isanort Sep 30, 2025
a236414
test(user-user-controller): refactor tests for full integration
isanort Oct 1, 2025
d111610
Merge pull request #129 from Femcoders-SleepUp/refactor/tests
isanort Oct 1, 2025
7fc7e70
chore(application.properties): change ddl-auto to update
isanort Oct 1, 2025
6c46099
feat(reservation-security): add isReservationGuestOrOwner and isReser…
isanort Oct 1, 2025
b22e416
refactor(access-evaluators): remove custom exception
isanort Oct 1, 2025
b072584
test(reservation-security): add methods to ReservationAccessEvaluator…
isanort Oct 1, 2025
4de0b08
feat(reservation-security): implement custom @PreAuthorize in control…
isanort Oct 1, 2025
27c0e98
refactor(security-access): refactor to use helper methods from helpers
isanort Oct 1, 2025
fcfaab6
test: rename methods in AuthServiceTest
PaolaAPL17 Oct 1, 2025
d9bee34
test(security-access): modify tests to use helpers
isanort Oct 1, 2025
947d3aa
test: refactor AuthControllerTest
PaolaAPL17 Oct 1, 2025
2293056
feat(security-acces): add custom access denied exceptions
isanort Oct 1, 2025
464f19d
test(security-access): update tests with custom access denied exceptions
isanort Oct 1, 2025
508a644
test(reservation-controller): fix cancel tests
isanort Oct 1, 2025
00225d7
test(exceptions): update access denied test
isanort Oct 1, 2025
8f18ed6
Merge pull request #130 from Femcoders-SleepUp/feature/reservations-s…
isanort Oct 1, 2025
d129a49
test: refactor TokenBlacklistServiceTest
PaolaAPL17 Oct 1, 2025
6858043
feat(reservation-service): extract common calculateReservationPrice m…
isanort Oct 1, 2025
f7ccb1e
Merge pull request #131 from Femcoders-SleepUp/test/refactor-auth
PaolaAPL17 Oct 1, 2025
2872861
feat(reservation-entity): add totalPrice field with BigDecimal data t…
isanort Oct 1, 2025
66f4c16
fix(reservation-service): set ptice before saving
isanort Oct 1, 2025
8d6c9a7
test(resources): add total_price to reservations in data and schema sql
isanort Oct 1, 2025
4bbb0fc
test(reservation): update tests to add total_price BigDecimal field
isanort Oct 1, 2025
de0c2be
Merge pull request #132 from Femcoders-SleepUp/feature/reservation-price
isanort Oct 1, 2025
5ed81c9
refactor(test-user): refactor test names to follow method_situation_e…
brunasonda Oct 1, 2025
6442c8b
Merge pull request #134 from Femcoders-SleepUp/refactor/fix-test-user
brunasonda Oct 1, 2025
8e6b28f
feat(reservation-dto): add total price to dto responenses
isanort Oct 1, 2025
d33c92f
Merge pull request #135 from Femcoders-SleepUp/feature/reservation-price
isanort Oct 1, 2025
9c35f8d
refactor(email): refactor email helpers to take only reservation as p…
isanort Oct 1, 2025
a31a45e
test(email): refactor tests with helper methods
isanort Oct 1, 2025
4c5df35
Merge pull request #136 from Femcoders-SleepUp/refactor/email
isanort Oct 2, 2025
e9b3df3
Comment failing test create reservation
isanort Oct 2, 2025
8cce116
Update test.yml to add cloudinary variables
isanort Oct 2, 2025
3d08f43
Update docker-compose-test.yml
PCalvoGarcia Oct 2, 2025
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
7 changes: 4 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@ jobs:
DB_PASSWORD: password
SERVER_PORT: 8080
# Cloudinary dummy variables for testing
CLOUDINARY_CLOUD_NAME: dummy_cloud_name
CLOUDINARY_API_KEY: dummy_api_key
CLOUDINARY_API_SECRET: dummy_api_secret
CLOUDINARY_CLOUD_NAME: ${{ secrets.CLOUDINARY_CLOUD_NAME }}
CLOUDINARY_API_KEY: ${{ secrets.CLOUDINARY_API_KEY }}
CLOUDINARY_API_SECRET: ${{ secrets.CLOUDINARY_API_SECRET }}

# Email dummy variables for testing
EMAIL: test@example.com
EMAIL_PASSWORD: dummy_password
Expand Down
9 changes: 8 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ on:

jobs:
test-docker:

env:
# Cloudinary dummy variables for testing
CLOUDINARY_CLOUD_NAME: ${{ secrets.CLOUDINARY_CLOUD_NAME }}
CLOUDINARY_API_KEY: ${{ secrets.CLOUDINARY_API_KEY }}
CLOUDINARY_API_SECRET: ${{ secrets.CLOUDINARY_API_SECRET }}

# Use the latest Ubuntu runner for consistent test environment
runs-on: ubuntu-latest

Expand All @@ -36,4 +43,4 @@ jobs:
if: always() # Run even if previous steps fail
run: docker compose -f docker-compose-test.yml down -v
# down: Stop and remove containers
# -v: Remove associated volumes
# -v: Remove associated volumes
6 changes: 5 additions & 1 deletion docker-compose-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ services:
- DB_URL_TEST=jdbc:mysql://sleepUp-test-db:3306/sleepUp # Database connection URL using service name
- DB_USER=sleepUp # Database username
- DB_PASSWORD=sleepUp123 # Database password
- CLOUDINARY_CLOUD_NAME=${CLOUDINARY_CLOUD_NAME}
- CLOUDINARY_API_KEY=${CLOUDINARY_API_KEY}
- CLOUDINARY_API_SECRET=${CLOUDINARY_API_SECRET}


# Restart policy - never restart (single-run test container)
restart: no
Expand Down Expand Up @@ -77,4 +81,4 @@ services:
# Network configuration
networks:
sleepUp-network:
driver: bridge # Use bridge driver for container-to-container communication
driver: bridge # Use bridge driver for container-to-container communication
5 changes: 4 additions & 1 deletion kubernetes/deployment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ spec:
env:
# Spring Boot profile configuration
- name: SPRING_PROFILES_ACTIVE
value: "kubernetes"
valueFrom:
configMapKeyRef:
name: config
key: spring-profiles-active
# Application server port
- name: SERVER_PORT
value: "8080"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
import com.SleepUp.SU.accommodation.dto.AccommodationResponseSummary;
import com.SleepUp.SU.accommodation.service.AccommodationService;
import com.SleepUp.SU.user.entity.CustomUserDetails;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
Expand All @@ -16,26 +19,49 @@

import java.util.List;

@Tag(name = "Accommodation", description = "Operation related to accommodations")
@RestController
@RequestMapping("/accommodations")
@RequiredArgsConstructor
public class AccommodationController {
private final AccommodationService accommodationService;

@GetMapping
@Operation(summary = "Get All Accommodations", description = "Retrieve a list of all accommodations with summary details.",
responses = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved list of accommodations"),
@ApiResponse(responseCode = "204", ref = "#/components/responses/NoContent"),
@ApiResponse(responseCode = "403", ref = "#/components/responses/Forbidden"),
@ApiResponse(responseCode = "500", ref = "#/components/responses/InternalServerError")
})
@ResponseStatus(HttpStatus.OK)
public List<AccommodationResponseSummary> getAllAccommodations(@AuthenticationPrincipal CustomUserDetails customUserDetails){
return accommodationService.getAllAccommodations();
}

@GetMapping("/{id}")
@Operation(summary = "Get Accommodation Details", description = "Retrieve detailed information about a specific accommodation by its ID.",
responses = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved accommodation details"),
@ApiResponse(responseCode = "403", ref = "#/components/responses/Forbidden"),
@ApiResponse(responseCode = "404", ref = "#/components/responses/AccommodationFound"),
@ApiResponse(responseCode = "500", ref = "#/components/responses/InternalServerError")
})
@ResponseStatus(HttpStatus.OK)
public AccommodationResponseDetail getAccommodationDetailById(@PathVariable Long id) {
return accommodationService.getAccommodationById(id);
}

@PreAuthorize("isAuthenticated()")
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation(summary = "Create Accommodation", description = "Create a new accommodation with the provided details.",
responses = {
@ApiResponse(responseCode = "201", description = "Successfully created a new accommodation"),
@ApiResponse(responseCode = "400", ref = "#/components/responses/BadRequest"),
@ApiResponse(responseCode = "401", ref = "#/components/responses/Unauthorized"),
@ApiResponse(responseCode = "403", ref = "#/components/responses/Forbidden"),
@ApiResponse(responseCode = "500", ref = "#/components/responses/InternalServerError")
})
@ResponseStatus(HttpStatus.CREATED)
public AccommodationResponseDetail createAccommodation(
@Valid @ModelAttribute AccommodationRequest accommodationRequest,
Expand All @@ -45,6 +71,15 @@ public AccommodationResponseDetail createAccommodation(

@PreAuthorize("hasRole('ADMIN') or @accommodationAccessEvaluator.isOwner(#id, principal.id)")
@PutMapping(path = "/{id}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation(summary = "Update Accommodation", description = "Update the details of an existing accommodation by its ID.",
responses = {
@ApiResponse(responseCode = "200", description = "Successfully updated accommodation details"),
@ApiResponse(responseCode = "400", ref = "#/components/responses/BadRequest"),
@ApiResponse(responseCode = "401", ref = "#/components/responses/Unauthorized"),
@ApiResponse(responseCode = "403", ref = "#/components/responses/Forbidden"),
@ApiResponse(responseCode = "404", ref = "#/components/responses/AccommodationFound"),
@ApiResponse(responseCode = "500", ref = "#/components/responses/InternalServerError")
})
@ResponseStatus(HttpStatus.OK)
public AccommodationResponseDetail updateAccommodation(
@PathVariable Long id,
Expand All @@ -54,6 +89,14 @@ public AccommodationResponseDetail updateAccommodation(

@PreAuthorize("hasRole('ADMIN') or @accommodationAccessEvaluator.isOwner(#id, principal.id)")
@DeleteMapping("/{id}")
@Operation(summary = "Delete Accommodation", description = "Delete an existing accommodation by its ID.",
responses = {
@ApiResponse(responseCode = "204", description = "Successfully deleted the accommodation"),
@ApiResponse(responseCode = "401", ref = "#/components/responses/Unauthorized"),
@ApiResponse(responseCode = "403", ref = "#/components/responses/Forbidden"),
@ApiResponse(responseCode = "404", ref = "#/components/responses/AccommodationFound"),
@ApiResponse(responseCode = "500", ref = "#/components/responses/InternalServerError")
})
@ResponseStatus(HttpStatus.NO_CONTENT)
public ResponseEntity<Object> deleteAccommodation(@PathVariable Long id){
accommodationService.deleteAccommodation(id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,7 @@ public record FilterAccommodationDTO(
String location,

LocalDate fromDate,
LocalDate toDate
LocalDate toDate,

Boolean petFriendly
) {}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import com.SleepUp.SU.accommodation.dto.AccommodationResponseSummary;
import com.SleepUp.SU.accommodation.dto.FilterAccommodationDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
Expand All @@ -10,6 +13,7 @@
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

@Tag(name = "Accommodation Filter", description = "Operations related to filtering accommodations")
@RestController
@RequestMapping("/accommodations")
@RequiredArgsConstructor
Expand All @@ -18,6 +22,12 @@ public class AccommodationFilterController {
private final AccommodationFilterService accommodationFilterService;

@GetMapping("/filter")
@Operation(summary = "Filter Accommodations", description = "Retrieve a paginated list of accommodations based on filter criteria.",
responses = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved filtered accommodations"),
@ApiResponse(responseCode = "400", ref = "#/components/responses/BadRequest"),
@ApiResponse(responseCode = "500", ref = "#/components/responses/InternalServerError")
})
@ResponseStatus(HttpStatus.OK)
public Page<AccommodationResponseSummary> getAllFilteredAccommodationsWithPagination(
@ModelAttribute @Valid FilterAccommodationDTO filter,
Expand All @@ -28,4 +38,4 @@ public Page<AccommodationResponseSummary> getAllFilteredAccommodationsWithPagina
return accommodationFilterService.getAllFilteredAccommodationsWithPagination(filter, pageable);
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,30 +62,39 @@ public static Specification<Accommodation> noBookingOverlap(LocalDate newStartDa
return null;
}

assert query != null;
Subquery<Long> subquery = query.subquery(Long.class);
Root<Reservation> reservationRoot = subquery.from(Reservation.class);
subquery.select(cb.count(reservationRoot));

// Match reservations for the same accommodation
Predicate sameAccommodation = cb.equal(reservationRoot.get("accommodation").get("id"), root.get("id"));

// Exclude cancelled reservations
Predicate notCancelled = cb.notEqual(reservationRoot.get("bookingStatus"), BookingStatus.CANCELLED);

// Overlapping date ranges
Predicate overlap = cb.not(cb.or(
cb.greaterThan(reservationRoot.get("checkInDate"), newEndDate),
cb.lessThan(reservationRoot.get("checkOutDate"), newStartDate)
));

// Compose subquery where clause: same accommodation AND not cancelled AND overlapping
subquery.where(cb.and(sameAccommodation, notCancelled, overlap));

// Main predicate: count of overlapping, non-cancelled reservations must be zero
return cb.equal(subquery, 0L);
};
}

public static Specification<Accommodation> petFriendly(Boolean petFriendly) {
return (root, query, cb) -> {
if (petFriendly == null) {
return null;
}
if (petFriendly) {
return cb.isTrue(root.get("petFriendly"));
} else {
return cb.isFalse(root.get("petFriendly"));
}
};
}

public Specification<Accommodation> buildSpecification(FilterAccommodationDTO filter) {
return Specification.<Accommodation>unrestricted()
.and(AccommodationSpecification.hasName(filter.name()))
Expand All @@ -94,6 +103,7 @@ public Specification<Accommodation> buildSpecification(FilterAccommodationDTO fi
.and(AccommodationSpecification.guestNumber(filter.guestNumber()))
.and(AccommodationSpecification.locatedAt(filter.location()))
.and(AccommodationSpecification.availableBetween(filter.fromDate(), filter.toDate()))
.and(noBookingOverlap(filter.fromDate(), filter.toDate()));
.and(noBookingOverlap(filter.fromDate(), filter.toDate()))
.and(AccommodationSpecification.petFriendly(filter.petFriendly()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import com.SleepUp.SU.accommodation.dto.AccommodationResponseSummary;
import com.SleepUp.SU.user.entity.CustomUserDetails;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
Expand All @@ -13,6 +16,7 @@

import java.util.List;

@Tag(name = "Accommodation Owner", description = "Operations related to accommodations by owners")
@RestController
@RequestMapping("/accommodations")
@RequiredArgsConstructor
Expand All @@ -21,6 +25,13 @@ public class AccommodationOwnerController {
private final AccommodationOwnerService accommodationOwnerService;

@GetMapping("/me")
@Operation(summary = "Get All My Accommodations", description = "Retrieve a list of accommodations owned by the authenticated user.",
responses = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved list of accommodations"),
@ApiResponse(responseCode = "400", ref = "#/components/responses/BadRequest"),
@ApiResponse(responseCode = "403", ref = "#/components/responses/Forbidden"),
@ApiResponse(responseCode = "500", ref = "#/components/responses/InternalServerError")
})
@ResponseStatus(HttpStatus.OK)
public List<AccommodationResponseSummary> getAllOwnerAccommodations(@AuthenticationPrincipal CustomUserDetails customUserDetails){
return accommodationOwnerService.getAllAccommodationsByOwnerId(customUserDetails.getId());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
package com.SleepUp.SU.accommodation.security;

import com.SleepUp.SU.accommodation.repository.AccommodationRepository;
import com.SleepUp.SU.accommodation.utils.AccommodationServiceHelper;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class AccommodationAccessEvaluator {
private final AccommodationRepository accommodationRepository;
private final AccommodationServiceHelper accommodationServiceHelper;

public boolean isOwner(Long accommodationId, Long userId) {
boolean owner = accommodationRepository.existsByIdAndManagedBy_Id(accommodationId, userId);
boolean owner = accommodationServiceHelper.isAccommodationOwnedByUser(accommodationId, userId);
if (!owner) {
throw new AccessDeniedException(
"User ID " + userId + " cannot access Accommodation ID " + accommodationId +
". Only the owner is authorized to access this resource."
"Cause: You are not the owner of this accommodation."
);
}
return true;
return owner;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ public Accommodation getAccommodationEntityById(Long id) {
return accommodationRepository.findById(id).orElseThrow(() -> new AccommodationNotFoundByIdException(id));
}

public boolean isAccommodationOwnedByUser(Long accommodationId, Long userId){
return accommodationRepository.existsByIdAndManagedBy_Id(accommodationId, userId);
}

public void cloudinaryManagement(AccommodationRequest accommodationRequest, Accommodation accommodationIsExisting) {
if (accommodationRequest.image() != null && !accommodationRequest.image().isEmpty()) {
deleteImageCloudinary(accommodationIsExisting.getImageUrl());
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/SleepUp/SU/auth/AuthController.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import com.SleepUp.SU.user.entity.CustomUserDetails;
import com.SleepUp.SU.user.dto.UserRequest;
import com.SleepUp.SU.user.dto.UserResponse;
import com.SleepUp.SU.utils.ApiMessageDto;
import com.SleepUp.SU.utils.dto.ApiMessageDto;
import jakarta.mail.MessagingException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
Expand Down
10 changes: 4 additions & 6 deletions src/main/java/com/SleepUp/SU/auth/AuthService.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import com.SleepUp.SU.user.dto.UserRequest;
import com.SleepUp.SU.user.dto.UserResponse;
import com.SleepUp.SU.user.role.Role;
import com.SleepUp.SU.utils.ApiMessageDto;
import com.SleepUp.SU.utils.dto.ApiMessageDto;
import com.SleepUp.SU.utils.email.EmailServiceHelper;
import jakarta.mail.MessagingException;
import jakarta.transaction.Transactional;
Expand Down Expand Up @@ -42,11 +42,9 @@ public class AuthService {

@Transactional
public UserResponse register(UserRequest request) throws MessagingException {
userServiceHelper.validateUserDoesNotExist(request.username(), request.email());
String encodedPassword = passwordEncoder.encode(request.password());
User user = userMapper.toEntity(request, encodedPassword, Role.USER);
emailServiceHelper.sendWelcomeEmail(request, user);
return userMapper.toResponse(userRepository.save(user));
User savedUser = userServiceHelper.createUser(request, Role.USER);
emailServiceHelper.sendWelcomeEmail(savedUser);
return userMapper.toResponse(savedUser);
}

public AuthResponse login(LoginRequest loginRequest) {
Expand Down
Loading
Loading