From 6b14afb3f51745a20c4f5967e492b8f28cbff2cf Mon Sep 17 00:00:00 2001 From: XxILUSHAxX <106095042+XxILUSHAxX@users.noreply.github.com> Date: Tue, 7 Jan 2025 11:51:40 +0100 Subject: [PATCH] Enhancement/261 complete commercial endpoint functionality (#283) Co-authored-by: Alexander Stanik --- remsfal-core/pom.xml | 2 +- .../core/api/project/CommercialEndpoint.java | 101 +++++++++++ .../core/json/project/CommercialJson.java | 21 ++- .../core/model/project/CommercialModel.java | 2 +- .../boundary/project/CommercialResource.java | 55 ++++++ .../service/control/CommercialController.java | 91 ++++++++++ .../entity/dao/CommercialRepository.java | 20 ++- .../java/de/remsfal/service/TestData.java | 30 +++- .../project/CommercialResourceTest.java | 169 ++++++++++++++++++ .../control/CommercialControllerTest.java | 141 +++++++++++++++ .../entity/CommercialRepositoryTest.java | 81 +++++++++ 11 files changed, 703 insertions(+), 10 deletions(-) create mode 100644 remsfal-core/src/main/java/de/remsfal/core/api/project/CommercialEndpoint.java create mode 100644 remsfal-service/src/main/java/de/remsfal/service/boundary/project/CommercialResource.java create mode 100644 remsfal-service/src/main/java/de/remsfal/service/control/CommercialController.java create mode 100644 remsfal-service/src/test/java/de/remsfal/service/boundary/project/CommercialResourceTest.java create mode 100644 remsfal-service/src/test/java/de/remsfal/service/control/CommercialControllerTest.java create mode 100644 remsfal-service/src/test/java/de/remsfal/service/entity/CommercialRepositoryTest.java diff --git a/remsfal-core/pom.xml b/remsfal-core/pom.xml index 36c58827..893a8f8b 100644 --- a/remsfal-core/pom.xml +++ b/remsfal-core/pom.xml @@ -99,4 +99,4 @@ - \ No newline at end of file + diff --git a/remsfal-core/src/main/java/de/remsfal/core/api/project/CommercialEndpoint.java b/remsfal-core/src/main/java/de/remsfal/core/api/project/CommercialEndpoint.java new file mode 100644 index 00000000..77ceb933 --- /dev/null +++ b/remsfal-core/src/main/java/de/remsfal/core/api/project/CommercialEndpoint.java @@ -0,0 +1,101 @@ +package de.remsfal.core.api.project; + +import de.remsfal.core.api.ProjectEndpoint; +import de.remsfal.core.validation.PatchValidation; +import de.remsfal.core.validation.PostValidation; +import de.remsfal.core.validation.UUID; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.groups.ConvertGroup; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.PATCH; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; + + +import org.eclipse.microprofile.openapi.annotations.Operation; +import org.eclipse.microprofile.openapi.annotations.headers.Header; +import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; + +import de.remsfal.core.json.project.CommercialJson; + +/** + * Endpoint for managing Commercial properties within buildings. + */ +@Path(ProjectEndpoint.CONTEXT + "/" + ProjectEndpoint.VERSION + "/" + + ProjectEndpoint.SERVICE + "/{projectId}/" + PropertyEndpoint.SERVICE + + "/{propertyId}/" + BuildingEndpoint.SERVICE + + "/{buildingId}/" + CommercialEndpoint.SERVICE) +public interface CommercialEndpoint { + + String SERVICE = "commercials"; + + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Operation(summary = "Create a new commercial unit.") + @APIResponse(responseCode = "201", description = "Commercial unit created successfully", + headers = @Header(name = "Location", description = "URL of the new commercial")) + Response createCommercial( + @Parameter(description = "ID of the project", required = true) + @PathParam("projectId") @NotNull @UUID String projectId, + @Parameter(description = "ID of the building", required = true) + @PathParam("buildingId") @NotNull @UUID String buildingId, + @Parameter(description = "Commercial unit information", required = true) + @Valid @ConvertGroup(to = PostValidation.class) CommercialJson commercial + ); + + @GET + @Path("/{commercialId}") + @Produces(MediaType.APPLICATION_JSON) + @Operation(summary = "Retrieve information about a commercial unit.") + @APIResponse(responseCode = "404", description = "The commercial unit does not exist") + @APIResponse(responseCode = "401", description = "No user authentication provided via session cookie") + CommercialJson getCommercial( + @Parameter(description = "ID of the project", required = true) + @PathParam("projectId") @NotNull @UUID String projectId, + @Parameter(description = "ID of the building", required = true) + @PathParam("buildingId") @NotNull @UUID String buildingId, + @Parameter(description = "ID of the commercial unit", required = true) + @PathParam("commercialId") @NotNull @UUID String commercialId + ); + + @PATCH + @Path("/{commercialId}") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Operation(summary = "Update information of a commercial unit") + @APIResponse(responseCode = "401", description = "No user authentication provided via session cookie") + @APIResponse(responseCode = "404", description = "The commercial unit does not exist") + CommercialJson updateCommercial( + @Parameter(description = "ID of the project", required = true) + @PathParam("projectId") @NotNull @UUID String projectId, + @Parameter(description = "ID of the building", required = true) + @PathParam("buildingId") @NotNull @UUID String buildingId, + @Parameter(description = "ID of the commercial unit", required = true) + @PathParam("commercialId") @NotNull @UUID String commercialId, + @Parameter(description = "Commercial unit object with information", required = true) + @Valid @ConvertGroup(to = PatchValidation.class) CommercialJson commercial + ); + + @DELETE + @Path("/{commercialId}") + @Operation(summary = "Delete an existing commercial unit") + @APIResponse(responseCode = "204", description = "The commercial unit was deleted successfully") + @APIResponse(responseCode = "401", description = "No user authentication provided via session cookie") + void deleteCommercial( + @Parameter(description = "ID of the project", required = true) + @PathParam("projectId") @NotNull @UUID String projectId, + @Parameter(description = "ID of the building", required = true) + @PathParam("buildingId") @NotNull @UUID String buildingId, + @Parameter(description = "ID of the commercial unit", required = true) + @PathParam("commercialId") @NotNull @UUID String commercialId + ); +} diff --git a/remsfal-core/src/main/java/de/remsfal/core/json/project/CommercialJson.java b/remsfal-core/src/main/java/de/remsfal/core/json/project/CommercialJson.java index da6e56a2..34b0e7fb 100644 --- a/remsfal-core/src/main/java/de/remsfal/core/json/project/CommercialJson.java +++ b/remsfal-core/src/main/java/de/remsfal/core/json/project/CommercialJson.java @@ -52,6 +52,25 @@ public abstract class CommercialJson implements CommercialModel { @Null @Nullable - public abstract Float getRent(); + public abstract TenancyJson getTenancy(); + + /** + * Converts a {@link CommercialModel} to a {@link CommercialJson}. + * + * @param model the {@link CommercialModel} instance to convert. + * @return an immutable {@link CommercialJson} instance. + */ + public static CommercialJson valueOf(final CommercialModel model) { + return ImmutableCommercialJson.builder() + .id(model.getId()) + .title(model.getTitle()) + .location(model.getLocation()) + .description(model.getDescription()) + .commercialSpace(model.getCommercialSpace()) + .usableSpace(model.getUsableSpace()) + .heatingSpace(model.getHeatingSpace()) + .tenancy(TenancyJson.valueOf(model.getTenancy())) + .build(); + } } diff --git a/remsfal-core/src/main/java/de/remsfal/core/model/project/CommercialModel.java b/remsfal-core/src/main/java/de/remsfal/core/model/project/CommercialModel.java index 5532f3d9..fcb8cd24 100644 --- a/remsfal-core/src/main/java/de/remsfal/core/model/project/CommercialModel.java +++ b/remsfal-core/src/main/java/de/remsfal/core/model/project/CommercialModel.java @@ -3,7 +3,7 @@ /** * @author Alexander Stanik [alexander.stanik@htw-berlin.de] */ -public interface CommercialModel { +public interface CommercialModel extends RentalUnitModel { String getId(); diff --git a/remsfal-service/src/main/java/de/remsfal/service/boundary/project/CommercialResource.java b/remsfal-service/src/main/java/de/remsfal/service/boundary/project/CommercialResource.java new file mode 100644 index 00000000..35a8b4e8 --- /dev/null +++ b/remsfal-service/src/main/java/de/remsfal/service/boundary/project/CommercialResource.java @@ -0,0 +1,55 @@ +package de.remsfal.service.boundary.project; + +import de.remsfal.core.api.project.CommercialEndpoint; +import de.remsfal.core.json.project.CommercialJson; +import de.remsfal.core.model.project.CommercialModel; +import de.remsfal.service.control.CommercialController; +import jakarta.enterprise.context.RequestScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +import java.net.URI; + +/** + * Resource for managing Commercial units via the API. + */ +@RequestScoped +public class CommercialResource extends ProjectSubResource implements CommercialEndpoint { + + @Inject + CommercialController controller; + + @Override + public Response createCommercial(final String projectId, final String buildingId, + final CommercialJson commercial) { + checkPrivileges(projectId); + final CommercialModel model = controller.createCommercial(projectId, buildingId, commercial); + final URI location = uri.getAbsolutePathBuilder().path(model.getId()).build(); + return Response.created(location) + .type(MediaType.APPLICATION_JSON) + .entity(CommercialJson.valueOf(model)) + .build(); + } + + @Override + public CommercialJson getCommercial(final String projectId, final String buildingId, final String commercialId) { + checkPrivileges(projectId); + return CommercialJson.valueOf(controller.getCommercial(projectId, buildingId, commercialId)); + } + + @Override + public CommercialJson updateCommercial(final String projectId, + final String buildingId, final String commercialId, + final CommercialJson commercial) { + checkPrivileges(projectId); + return CommercialJson.valueOf(controller.updateCommercial(projectId, buildingId, commercialId, commercial)); + } + + @Override + public void deleteCommercial(final String projectId, final String buildingId, + final String commercialId) { + checkPrivileges(projectId); + controller.deleteCommercial(projectId, buildingId, commercialId); + } +} diff --git a/remsfal-service/src/main/java/de/remsfal/service/control/CommercialController.java b/remsfal-service/src/main/java/de/remsfal/service/control/CommercialController.java new file mode 100644 index 00000000..5e81314c --- /dev/null +++ b/remsfal-service/src/main/java/de/remsfal/service/control/CommercialController.java @@ -0,0 +1,91 @@ +package de.remsfal.service.control; + +import de.remsfal.core.model.project.CommercialModel; +import de.remsfal.service.entity.dao.CommercialRepository; +import de.remsfal.service.entity.dto.CommercialEntity; +import jakarta.enterprise.context.RequestScoped; +import jakarta.inject.Inject; +import jakarta.persistence.NoResultException; +import jakarta.transaction.Transactional; +import jakarta.ws.rs.NotFoundException; +import org.jboss.logging.Logger; + +/** + * Controller for managing Commercial units. + */ +@RequestScoped +public class CommercialController { + + @Inject + Logger logger; + + @Inject + CommercialRepository commercialRepository; + + @Inject + TenancyController tenancyController; + + @Transactional + public CommercialModel createCommercial(final String projectId, final String buildingId, + final CommercialModel commercial) { + logger.infov("Creating a commercial (projectId={0}, buildingId={1}, commercial={2})", + projectId, buildingId, commercial); + CommercialEntity entity = CommercialEntity.fromModel(commercial); + entity.generateId(); + entity.setProjectId(projectId); + entity.setBuildingId(buildingId); + commercialRepository.persistAndFlush(entity); + commercialRepository.getEntityManager().refresh(entity); + return getCommercial(projectId, buildingId, entity.getId()); + } + + public CommercialModel getCommercial(final String projectId, + final String buildingId, final String commercialId) { + logger.infov("Retrieving a commercial (projectId={0}, buildingId={1}, commercialId={2})", + projectId, buildingId, commercialId); + CommercialEntity entity = commercialRepository.findCommercialById(projectId, buildingId,commercialId) + .orElseThrow(() -> new NotFoundException("Commercial not exist")); + + if (!entity.getProjectId().equals(projectId)) { + throw new NoResultException("Unable to find commercial, because the project ID is invalid"); + } + + return entity; + } + + @Transactional + public CommercialModel updateCommercial(final String projectId, final String buildingId, final String commercialId, + final CommercialModel commercial) { + logger.infov("Updating a commercial (commercialId={0})", commercialId); + CommercialEntity entity = commercialRepository.findCommercialById(projectId, buildingId, commercialId) + .orElseThrow(() -> new NotFoundException("Commercial not exist")); + + if (commercial.getTitle() != null) { + entity.setTitle(commercial.getTitle()); + } + if (commercial.getLocation() != null) { + entity.setLocation(commercial.getLocation()); + } + if (commercial.getCommercialSpace() != null) { + entity.setCommercialSpace(commercial.getCommercialSpace()); + } + if (commercial.getHeatingSpace() != null) { + entity.setHeatingSpace(commercial.getHeatingSpace()); + } + if (commercial.getTenancy() != null){ + entity.setTenancy(tenancyController.updateTenancy(projectId, entity.getTenancy(), commercial.getTenancy())); + } + return commercialRepository.merge(entity); + } + + @Transactional + public void deleteCommercial(final String projectId, final String buildingId, + final String commercialId) throws NotFoundException { + logger.infov("Delete a commercial (projectId{0} buildingId={1} commercialId{2})", + projectId, buildingId, commercialId); + if (commercialRepository.findCommercialById(projectId,buildingId,commercialId).isEmpty()) { + throw new NotFoundException("Commercial does not exist"); + } + commercialRepository.deleteCommercialById(projectId, buildingId, commercialId); + } +} diff --git a/remsfal-service/src/main/java/de/remsfal/service/entity/dao/CommercialRepository.java b/remsfal-service/src/main/java/de/remsfal/service/entity/dao/CommercialRepository.java index a5e7e86f..09a223b3 100644 --- a/remsfal-service/src/main/java/de/remsfal/service/entity/dao/CommercialRepository.java +++ b/remsfal-service/src/main/java/de/remsfal/service/entity/dao/CommercialRepository.java @@ -3,14 +3,17 @@ import jakarta.enterprise.context.ApplicationScoped; import de.remsfal.service.entity.dto.CommercialEntity; +import io.quarkus.panache.common.Parameters; import java.util.List; +import java.util.Optional; /** * @author Alexander Stanik [alexander.stanik@htw-berlin.de] */ @ApplicationScoped public class CommercialRepository extends AbstractRepository { + public List findCommercialByBuildingId(String buildingId) { return getEntityManager() .createQuery( @@ -20,4 +23,19 @@ public List findCommercialByBuildingId(String buildingId) { .setParameter("buildingId", buildingId) .getResultList(); } -} \ No newline at end of file + public Optional findCommercialById(final String projectId, + final String buildingId, + final String commercialId) { + return find("id = :id and projectId = :projectId and buildingId = :buildingId", + Parameters.with("id", commercialId) + .and("projectId", projectId) + .and("buildingId", buildingId)).singleResultOptional(); + } + + public long deleteCommercialById(final String projectId, final String buildingId, final String commercialId) { + return delete("id = :id and projectId = :projectId and buildingId = :buildingId", + Parameters.with("id", commercialId) + .and("projectId", projectId) + .and("buildingId", buildingId)); + } +} diff --git a/remsfal-service/src/test/java/de/remsfal/service/TestData.java b/remsfal-service/src/test/java/de/remsfal/service/TestData.java index 405788cd..3aea5646 100644 --- a/remsfal-service/src/test/java/de/remsfal/service/TestData.java +++ b/remsfal-service/src/test/java/de/remsfal/service/TestData.java @@ -237,8 +237,8 @@ public static final ImmutableBuildingJson.Builder buildingBuilder2() { .heatingSpace(TestData.BUILDING_HEATING_SPACE_2) .isDifferentHeatingSpace(false); } - - // Default test tenancy + + // Default test tenancy public static final String TENANCY_ID = TestData.TENANCY_ID_1; public static final String TENANCY_START = TestData.TENANCY_START_1; public static final String TENANCY_END = TestData.TENANCY_END_1; @@ -336,7 +336,6 @@ public static final ImmutableApartmentJson.Builder apartmentBuilder2() { public static final Float COMMERCIAL_COMMERCIAL_SPACE = TestData.COMMERCIAL_COMMERCIAL_SPACE_1; public static final Float COMMERCIAL_USABLE_SPACE = TestData.COMMERCIAL_USABLE_SPACE_1; public static final Float COMMERCIAL_HEATING_SPACE = TestData.COMMERCIAL_HEATING_SPACE_1; - public static final Float COMMERCIAL_RENT = TestData.COMMERCIAL_RENT_1; public static final ImmutableCommercialJson.Builder commercialBuilder() { return commercialBuilder1(); @@ -350,7 +349,6 @@ public static final ImmutableCommercialJson.Builder commercialBuilder() { public static final Float COMMERCIAL_COMMERCIAL_SPACE_1 = 423.92f; public static final Float COMMERCIAL_USABLE_SPACE_1 = 53.9f; public static final Float COMMERCIAL_HEATING_SPACE_1 = 204.27f; - public static final Float COMMERCIAL_RENT_1 = 3799.80f; public static final ImmutableCommercialJson.Builder commercialBuilder1() { return ImmutableCommercialJson @@ -361,8 +359,28 @@ public static final ImmutableCommercialJson.Builder commercialBuilder1() { .description(COMMERCIAL_DESCRIPTION_1) .commercialSpace(COMMERCIAL_COMMERCIAL_SPACE_1) .usableSpace(COMMERCIAL_USABLE_SPACE_1) - .heatingSpace(COMMERCIAL_HEATING_SPACE_1) - .rent(COMMERCIAL_RENT_1); + .heatingSpace(COMMERCIAL_HEATING_SPACE_1); + } + + // Test commercial 1 + public static final String COMMERCIAL_ID_2 = "b9440c43-b5c0-4951-9c23-000000000002"; + public static final String COMMERCIAL_TITLE_2 = "Bäckerei Ekpmel"; + public static final String COMMERCIAL_LOCATION_2 = "EG rechts"; + public static final String COMMERCIAL_DESCRIPTION_2 = "Bäckerei mit Tischen hinter dem Haus"; + public static final Float COMMERCIAL_COMMERCIAL_SPACE_2 = 450.92f; + public static final Float COMMERCIAL_USABLE_SPACE_2 = 100.9f; + public static final Float COMMERCIAL_HEATING_SPACE_2 = 134.27f; + + public static final ImmutableCommercialJson.Builder commercialBuilder2() { + return ImmutableCommercialJson + .builder() + .id(COMMERCIAL_ID_2) + .title(COMMERCIAL_TITLE_2) + .location(COMMERCIAL_LOCATION_2) + .description(COMMERCIAL_DESCRIPTION_2) + .commercialSpace(COMMERCIAL_COMMERCIAL_SPACE_2) + .usableSpace(COMMERCIAL_USABLE_SPACE_2) + .heatingSpace(COMMERCIAL_HEATING_SPACE_2); } // Default test garage diff --git a/remsfal-service/src/test/java/de/remsfal/service/boundary/project/CommercialResourceTest.java b/remsfal-service/src/test/java/de/remsfal/service/boundary/project/CommercialResourceTest.java new file mode 100644 index 00000000..2e396603 --- /dev/null +++ b/remsfal-service/src/test/java/de/remsfal/service/boundary/project/CommercialResourceTest.java @@ -0,0 +1,169 @@ +package de.remsfal.service.boundary.project; + +import de.remsfal.service.TestData; +import io.quarkus.test.junit.QuarkusTest; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.time.Duration; + +import static io.restassured.RestAssured.given; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@QuarkusTest +class CommercialResourceTest extends AbstractProjectResourceTest { + + static final String BASE_PATH = "/api/v1/projects/{projectId}/properties/{propertyId}/" + + "buildings/{buildingId}/commercials"; + + @Override + @BeforeEach + protected void setupTestProjects() { + super.setupTestUsers(); + super.setupTestProjects(); + super.setupTestProperties(); + } + + protected void setupTestCommercial() { + runInTransaction(() -> entityManager + .createNativeQuery("INSERT INTO BUILDING (ID, PROPERTY_ID, PROJECT_ID, ADDRESS_ID, TITLE)" + + " VALUES (?,?,?,?,?)") + .setParameter(1, TestData.BUILDING_ID) + .setParameter(2, TestData.PROPERTY_ID) + .setParameter(3, TestData.PROJECT_ID) + .setParameter(4, TestData.ADDRESS_ID) + .setParameter(5, TestData.COMMERCIAL_TITLE) + .executeUpdate()); + runInTransaction(() -> entityManager + .createNativeQuery("INSERT INTO COMMERCIAL (ID, BUILDING_ID, PROJECT_ID, " + + "LOCATION, COMMERCIAL_SPACE,HEATING_SPACE, TITLE, DESCRIPTION, USABLE_SPACE) VALUES (?,?,?,?,?,?,?,?,?)") + .setParameter(1, TestData.COMMERCIAL_ID) + .setParameter(2, TestData.BUILDING_ID) + .setParameter(3, TestData.PROJECT_ID) + .setParameter(4, TestData.COMMERCIAL_LOCATION) + .setParameter(5, TestData.COMMERCIAL_COMMERCIAL_SPACE) + .setParameter(6, TestData.COMMERCIAL_HEATING_SPACE) + .setParameter(7, TestData.COMMERCIAL_TITLE) + .setParameter(8, TestData.COMMERCIAL_DESCRIPTION) + .setParameter(9, TestData.COMMERCIAL_USABLE_SPACE) + .executeUpdate()); + } + + @Test + void getCommercial_FAILED_noAuthentication() { + given() + .when() + .get(BASE_PATH + "/{commercialId}", TestData.PROJECT_ID, TestData.PROPERTY_ID, + TestData.BUILDING_ID, TestData.COMMERCIAL_ID) + .then() + .statusCode(Response.Status.UNAUTHORIZED.getStatusCode()); + } + + @Test + void getCommercialSuccessfully() { + setupTestCommercial(); + given() + .when() + .cookie(buildCookie(TestData.USER_ID, TestData.USER_EMAIL, Duration.ofMinutes(10))) + .get(BASE_PATH + "/{commercialId}", TestData.PROJECT_ID, TestData.PROPERTY_ID, + TestData.BUILDING_ID, TestData.COMMERCIAL_ID) + .then() + .statusCode(Response.Status.OK.getStatusCode()) + .contentType(MediaType.APPLICATION_JSON) + .and().body("id", Matchers.equalTo(TestData.COMMERCIAL_ID)) + .and().body("title", Matchers.equalTo(TestData.COMMERCIAL_TITLE)) + .and().body("description", Matchers.equalTo(TestData.COMMERCIAL_DESCRIPTION)) + .and().body("commercialSpace", Matchers.equalTo(TestData.COMMERCIAL_COMMERCIAL_SPACE)) + .and().body("usableSpace", Matchers.equalTo(TestData.COMMERCIAL_USABLE_SPACE)) + .and().body("heatingSpace", Matchers.equalTo(TestData.COMMERCIAL_HEATING_SPACE)) + .and().body("location", Matchers.equalTo(TestData.COMMERCIAL_LOCATION)); + } + + @ParameterizedTest + @ValueSource(strings = "{ \"title\":\"" + TestData.COMMERCIAL_TITLE_2 + "\"}") + void createCommercialSuccessfully(String json) { + setupTestCommercial(); + given() + .when() + .cookie(buildCookie(TestData.USER_ID, TestData.USER_EMAIL, Duration.ofMinutes(10))) + .contentType(MediaType.APPLICATION_JSON) + .body(json) + .post(BASE_PATH, TestData.PROJECT_ID, TestData.PROPERTY_ID, TestData.BUILDING_ID) + .then() + .statusCode(Response.Status.CREATED.getStatusCode()) + .contentType(MediaType.APPLICATION_JSON) + .header("location", Matchers.containsString(BASE_PATH.replace("{projectId}", TestData.PROJECT_ID) + .replace("{propertyId}", TestData.PROPERTY_ID) + .replace("{buildingId}", TestData.BUILDING_ID) + "/")) + .and().body("id", Matchers.notNullValue()) + .and().body("title", Matchers.equalTo(TestData.COMMERCIAL_TITLE_2)); + + long entities = entityManager + .createQuery("SELECT count(commercial) FROM CommercialEntity commercial where commercial.title = :title", long.class) + .setParameter("title", TestData.COMMERCIAL_TITLE_2) + .getSingleResult(); + assertEquals(1, entities); + } + + @ParameterizedTest + @ValueSource(strings = "{ \"title\":\"" + TestData.COMMERCIAL_TITLE_2 + "\"}") + void updateCommercialSuccessfully(final String json) { + setupTestCommercial(); + + given() + .when() + .cookie(buildCookie(TestData.USER_ID, TestData.USER_EMAIL, Duration.ofMinutes(10))) + .contentType(MediaType.APPLICATION_JSON) + .body(json) + .patch(BASE_PATH + "/{commercialId}", TestData.PROJECT_ID, TestData.PROPERTY_ID, + TestData.BUILDING_ID, TestData.COMMERCIAL_ID) + .then() + .statusCode(Response.Status.OK.getStatusCode()) + .contentType(MediaType.APPLICATION_JSON) + .and().body("id", Matchers.equalTo(TestData.COMMERCIAL_ID)) + .and().body("title", Matchers.equalTo(TestData.COMMERCIAL_TITLE_2)) + .and().body("description", Matchers.equalTo(TestData.COMMERCIAL_DESCRIPTION)) + .and().body("commercialSpace", Matchers.equalTo(TestData.COMMERCIAL_COMMERCIAL_SPACE)) + .and().body("heatingSpace", Matchers.equalTo(TestData.COMMERCIAL_HEATING_SPACE)) + .and().body("location", Matchers.equalTo(TestData.COMMERCIAL_LOCATION)) + .and().body("usableSpace", Matchers.equalTo(TestData.COMMERCIAL_USABLE_SPACE)); + + given() + .when() + .cookie(buildCookie(TestData.USER_ID, TestData.USER_EMAIL, Duration.ofMinutes(10))) + .contentType(MediaType.APPLICATION_JSON) + .get(BASE_PATH + "/{commercialId}", TestData.PROJECT_ID, TestData.PROPERTY_ID, + TestData.BUILDING_ID, TestData.COMMERCIAL_ID) + .then() + .statusCode(Response.Status.OK.getStatusCode()) + .contentType(MediaType.APPLICATION_JSON) + .and().body("id", Matchers.equalTo(TestData.COMMERCIAL_ID)) + .and().body("title", Matchers.equalTo(TestData.COMMERCIAL_TITLE_2)); + } + + @Test + void deleteCommercialSuccessfully() { + setupTestCommercial(); + + given() + .when() + .cookie(buildCookie(TestData.USER_ID, TestData.USER_EMAIL, Duration.ofMinutes(10))) + .delete(BASE_PATH + "/{commercialId}", TestData.PROJECT_ID, TestData.PROPERTY_ID, + TestData.BUILDING_ID, TestData.COMMERCIAL_ID) + .then() + .statusCode(Response.Status.NO_CONTENT.getStatusCode()); + + given() + .when() + .cookie(buildCookie(TestData.USER_ID, TestData.USER_EMAIL, Duration.ofMinutes(10))) + .get(BASE_PATH + "/{commercialId}", TestData.PROJECT_ID, TestData.PROPERTY_ID, + TestData.BUILDING_ID, TestData.COMMERCIAL_ID) + .then() + .statusCode(Response.Status.NOT_FOUND.getStatusCode()); + } +} diff --git a/remsfal-service/src/test/java/de/remsfal/service/control/CommercialControllerTest.java b/remsfal-service/src/test/java/de/remsfal/service/control/CommercialControllerTest.java new file mode 100644 index 00000000..b67d549f --- /dev/null +++ b/remsfal-service/src/test/java/de/remsfal/service/control/CommercialControllerTest.java @@ -0,0 +1,141 @@ +package de.remsfal.service.control; + +import de.remsfal.core.model.project.CommercialModel; +import de.remsfal.service.AbstractTest; +import de.remsfal.service.TestData; +import io.quarkus.test.junit.QuarkusTest; +import jakarta.inject.Inject; +import jakarta.ws.rs.NotFoundException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +@QuarkusTest +class CommercialControllerTest extends AbstractTest { + + @Inject + PropertyController propertyController; + + @Inject + BuildingController buildingController; + + @Inject + CommercialController commercialController; + + @BeforeEach + void setupTestProjects() { + runInTransaction(() -> entityManager + .createNativeQuery("INSERT INTO PROJECT (ID, TITLE) VALUES (?,?)") + .setParameter(1, TestData.PROJECT_ID_1) + .setParameter(2, TestData.PROJECT_TITLE_1) + .executeUpdate()); + } + + @Test + void createCommercial_SUCCESS_getCommercial() { + final String propertyId = propertyController + .createProperty(TestData.PROJECT_ID, TestData.propertyBuilder().build()) + .getId(); + assertNotNull(propertyId); + + final String buildingId = buildingController + .createBuilding(TestData.PROJECT_ID, propertyId, + TestData.buildingBuilder() + .id(null) + .address(TestData.addressBuilder().build()) + .build()) + .getId(); + assertNotNull(buildingId); + + final CommercialModel commercial = TestData.commercialBuilder().build(); + final CommercialModel result = commercialController + .createCommercial(TestData.PROJECT_ID, buildingId, commercial); + + assertNotEquals(commercial.getId(), result.getId()); + assertEquals(commercial.getTitle(), result.getTitle()); + assertEquals(commercial.getLocation(), result.getLocation()); + assertEquals(commercial.getCommercialSpace(), result.getCommercialSpace()); + assertEquals(commercial.getHeatingSpace(), result.getHeatingSpace()); + + final String commercialId = entityManager + .createQuery("SELECT c.id FROM CommercialEntity c where c.title = :title", String.class) + .setParameter("title", TestData.COMMERCIAL_TITLE) + .getSingleResult(); + assertEquals(result.getId(), commercialId); + + final CommercialModel getResult = commercialController + .getCommercial(TestData.PROJECT_ID, buildingId, commercialId); + + assertEquals(result, getResult); + } + + @Test + void createCommercial_SUCCESS_deleteCommercial() { + final String propertyId = propertyController + .createProperty(TestData.PROJECT_ID, TestData.propertyBuilder().build()) + .getId(); + assertNotNull(propertyId); + + final String buildingId = buildingController + .createBuilding(TestData.PROJECT_ID, propertyId, + TestData.buildingBuilder() + .id(null) + .address(TestData.addressBuilder().build()) + .build()) + .getId(); + assertNotNull(buildingId); + + final CommercialModel commercial = TestData.commercialBuilder().build(); + final CommercialModel result = commercialController + .createCommercial(TestData.PROJECT_ID, buildingId, commercial); + + final String commercialId = entityManager + .createQuery("SELECT c.id FROM CommercialEntity c where c.title = :title", String.class) + .setParameter("title", TestData.COMMERCIAL_TITLE) + .getSingleResult(); + assertEquals(result.getId(), commercialId); + + commercialController.deleteCommercial(TestData.PROJECT_ID, buildingId, commercialId); + assertThrows(NotFoundException.class, () -> commercialController.getCommercial(TestData.PROJECT_ID, buildingId, commercialId)); + } + + @Test + void createCommercial_SUCCESS_updateCommercial() { + final String propertyId = propertyController + .createProperty(TestData.PROJECT_ID, TestData.propertyBuilder().build()) + .getId(); + assertNotNull(propertyId); + + final String buildingId = buildingController + .createBuilding(TestData.PROJECT_ID, propertyId, + TestData.buildingBuilder() + .id(null) + .address(TestData.addressBuilder().build()) + .build()) + .getId(); + assertNotNull(buildingId); + + final CommercialModel commercial = TestData.commercialBuilder().build(); + final CommercialModel result = commercialController + .createCommercial(TestData.PROJECT_ID, buildingId, commercial); + + final String commercialId = entityManager + .createQuery("SELECT c.id FROM CommercialEntity c where c.title = :title", String.class) + .setParameter("title", TestData.COMMERCIAL_TITLE) + .getSingleResult(); + assertEquals(result.getId(), commercialId); + + final CommercialModel updateTo = TestData.commercialBuilder2().build(); + + final CommercialModel updated = commercialController.updateCommercial(TestData.PROJECT_ID, buildingId, + commercialId, updateTo); + + assertNotEquals(updateTo.getId(), updated.getId()); + assertEquals(updateTo.getTitle(), updated.getTitle()); + assertEquals(updateTo.getLocation(), updated.getLocation()); + assertEquals(updateTo.getCommercialSpace(), updated.getCommercialSpace()); + assertEquals(updateTo.getHeatingSpace(), updated.getHeatingSpace()); + assertEquals(commercial.getTenancy(), updated.getTenancy()); + } +} diff --git a/remsfal-service/src/test/java/de/remsfal/service/entity/CommercialRepositoryTest.java b/remsfal-service/src/test/java/de/remsfal/service/entity/CommercialRepositoryTest.java new file mode 100644 index 00000000..fcf4c108 --- /dev/null +++ b/remsfal-service/src/test/java/de/remsfal/service/entity/CommercialRepositoryTest.java @@ -0,0 +1,81 @@ +package de.remsfal.service.entity; + +import de.remsfal.core.model.project.CommercialModel; +import de.remsfal.service.AbstractTest; +import de.remsfal.service.TestData; +import de.remsfal.service.control.BuildingController; +import de.remsfal.service.control.PropertyController; +import de.remsfal.service.entity.dao.CommercialRepository; +import de.remsfal.service.entity.dto.CommercialEntity; +import io.quarkus.test.junit.QuarkusTest; +import jakarta.inject.Inject; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Optional; + +import static org.hibernate.validator.internal.util.Contracts.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.wildfly.common.Assert.assertTrue; + +@QuarkusTest +class CommercialRepositoryTest extends AbstractTest { + + @Inject + CommercialRepository repository; + + @Inject + BuildingController buildingController; + + @Inject + PropertyController propertyController; + + String propertyId; + + String buildingId; + + @BeforeEach + public void setupCommercials() { + runInTransaction(() -> entityManager + .createNativeQuery("INSERT INTO PROJECT (ID, TITLE) VALUES (?, ?)") + .setParameter(1, TestData.PROJECT_ID) + .setParameter(2, TestData.PROJECT_TITLE) + .executeUpdate()); + propertyId = propertyController + .createProperty(TestData.PROJECT_ID, TestData.propertyBuilder().build()) + .getId(); + assertNotNull(propertyId); + buildingId = buildingController + .createBuilding(TestData.PROJECT_ID, propertyId, + TestData.buildingBuilder() + .id(null) + .address(TestData.addressBuilder().build()) + .build()) + .getId(); + assertNotNull(buildingId); + runInTransaction(() -> entityManager + .createNativeQuery("INSERT INTO COMMERCIAL (ID, PROJECT_ID, BUILDING_ID, TITLE) VALUES (?, ?, ?, ?)") + .setParameter(1, TestData.COMMERCIAL_ID) + .setParameter(2, TestData.PROJECT_ID) + .setParameter(3, buildingId) + .setParameter(4, TestData.COMMERCIAL_TITLE) + .executeUpdate()); + } + + @Test + void testCommercialById() { + final Optional found = repository.findCommercialById(TestData.PROJECT_ID, buildingId, TestData.COMMERCIAL_ID); + final CommercialModel commercial = TestData.commercialBuilder().build(); + assertTrue(found.isPresent()); + assertTrue(found.hashCode() != 0); + assertEquals(commercial.getId(), found.get().getId()); + assertEquals(commercial.getTitle(), found.get().getTitle()); + } + + @Test + void testDeleteCommercialById() { + final long deleteCommercialById = runInTransaction(() -> + repository.deleteCommercialById(TestData.PROJECT_ID, buildingId,TestData.COMMERCIAL_ID)); + assertEquals(1, deleteCommercialById); + } +} \ No newline at end of file