Skip to content

Commit

Permalink
fix(api-owner): add constraint validation on dpeDate for post property
Browse files Browse the repository at this point in the history
Now the propertyForm will have a validation constraint on the dpeDate to force the format yyyy-MM-dd and that the date is before the current date
  • Loading branch information
nicolasSagon committed Feb 10, 2025
1 parent 1e6f4f1 commit 5304cbc
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package fr.dossierfacile.api.dossierfacileapiowner.annotation;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Constraint(validatedBy = ValidDpeDateValidator.class)
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidDpeDate {

String message() default "Date must be in the format yyyy-MM-dd";

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package fr.dossierfacile.api.dossierfacileapiowner.annotation;

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;

public class ValidDpeDateValidator implements ConstraintValidator<ValidDpeDate, String> {

@Override
public boolean isValid(String dpeDate, ConstraintValidatorContext context) {
if (dpeDate == null) {
return true;
}
try {
var parsedDate = LocalDate.parse(dpeDate, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
var currentDate = LocalDate.now();
return !parsedDate.isAfter(currentDate);
} catch (DateTimeParseException e) {
return false;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import fr.dossierfacile.common.exceptions.NotFoundException;
import fr.dossierfacile.common.service.interfaces.LogService;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import lombok.AllArgsConstructor;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.utils.URIBuilder;
Expand Down Expand Up @@ -48,10 +49,10 @@ public class PropertyController {
private final PropertyMapper propertyMapper;

@PostMapping
public ResponseEntity<PropertyModel> createOrUpdate(HttpServletResponse response, @RequestBody PropertyForm Property) throws HttpResponseException, InterruptedException {
public ResponseEntity<PropertyModel> createOrUpdate(HttpServletResponse response, @Valid @RequestBody PropertyForm property) throws HttpResponseException, InterruptedException {
try {
PropertyModel propertyModel;
propertyModel = propertyService.createOrUpdate(Property);
propertyModel = propertyService.createOrUpdate(property);
logService.saveLog(LogType.ACCOUNT_EDITED, propertyModel.getId());
return ok(propertyModel);
} catch (DPENotFoundException e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package fr.dossierfacile.api.dossierfacileapiowner.property;

import fr.dossierfacile.api.dossierfacileapiowner.annotation.ValidDpeDate;
import fr.dossierfacile.common.enums.PropertyFurniture;
import fr.dossierfacile.common.enums.PropertyType;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
Expand All @@ -16,7 +18,6 @@ public class PropertyForm {

private Long id;

@NotBlank
private String name;

private Double rentCost;
Expand All @@ -37,6 +38,7 @@ public class PropertyForm {

private Integer energyConsumption;

@ValidDpeDate
private String dpeDate;

private String ademeNumber;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package fr.dossierfacile.api.dossierfacileapiowner;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.test.context.ActiveProfiles;

@SpringBootApplication
@ActiveProfiles(value = "test")
public class TestApplication {

public static void main(String[] args) {
SpringApplication.run(DossierfacileApiOwnerApplication.class, args);
}

@Bean
@Profile("test")
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().anyRequest();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package fr.dossierfacile.api.dossierfacileapiowner.property;

import com.fasterxml.jackson.databind.ObjectMapper;
import fr.dossierfacile.api.dossierfacileapiowner.TestApplication;
import fr.dossierfacile.api.dossierfacileapiowner.register.AuthenticationFacade;
import fr.dossierfacile.api.dossierfacileapiowner.user.OwnerMapper;
import fr.dossierfacile.common.service.interfaces.LogService;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Named;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.web.servlet.MockMvc;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.function.Supplier;
import java.util.stream.Stream;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(PropertyController.class)
@ContextConfiguration(classes = {TestApplication.class})
class PropertyControllerTest {

@Autowired
private ObjectMapper objectMapper;

@Autowired
private MockMvc mockMvc;

@MockBean
private LogService logService;

@MockBean
private PropertyService propertyService;

@MockBean
private PropertyApartmentSharingService propertyApartmentSharingService;

@MockBean
private AuthenticationFacade authenticationFacade;

@MockBean
private OwnerMapper ownerMapper;

@MockBean
private PropertyMapper propertyMapper;

record PropertyTestPayload(String name, String dpeDate) {
}


static Stream<Arguments> providePropertyData() {
var localDate = LocalDate.now();
var futureDate = localDate.plusDays(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
var currentDate = localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));

return Stream.of(
Arguments.of(Named.of("Should return BadRequest when empty body sent", null), 400),
Arguments.of(Named.of("Should return 200 when valid body sent", new PropertyTestPayload("Test", "2021-08-01")), 200),
Arguments.of(Named.of("Should return BadRequest when invalid date format is sent in body", new PropertyTestPayload("Test", "Lundi 15 mars 2021")), 400),
Arguments.of(Named.of("Should return BadRequest when a specific date is sent in body", new PropertyTestPayload("Test", "52021-04-06")), 400),
Arguments.of(Named.of("Should return BadRequest when a futur date is sent in body", new PropertyTestPayload("Test", futureDate)), 400),
Arguments.of(Named.of("Should return 200 when the current date is sent in body", new PropertyTestPayload("Test", currentDate)), 200)
);
}

@ParameterizedTest(name = "{0}")
@MethodSource("providePropertyData")
@DisplayName("Test Create or Update Property")
void testCreateOrUpdateProperty(PropertyTestPayload testPayload, int expectedStatus) throws Exception {
if (expectedStatus == 200) {
var propertyModel = new PropertyModel();
propertyModel.setId(1L);
propertyModel.setName("Test");
propertyModel.setDpeDate("2021-08-01");
when(propertyService.createOrUpdate(any())).thenReturn(propertyModel);
}

Supplier<PropertyForm> propertyForm = () -> {
if (testPayload == null) {
return null;
} else {
var tmpProperty = new PropertyForm();
tmpProperty.setName(testPayload.name());
tmpProperty.setDpeDate(testPayload.dpeDate());
return tmpProperty;
}
};

var mockMvcConfiguration = post("/api/property")
.contentType("application/json");

if (propertyForm.get() != null) {
mockMvcConfiguration.content(objectMapper.writeValueAsString(propertyForm.get()));
}

mockMvc.perform(mockMvcConfiguration).andExpect(status().is(expectedStatus));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
spring.profiles.active=test,mockOvh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>

<!-- CONSOLE -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<withJansi>true</withJansi>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %clr(%-5level) [%thread] %logger{35} - %msg %n</pattern>
</encoder>
</appender>

<root>
<appender-ref ref="STDOUT"/>
</root>

</configuration>

0 comments on commit 5304cbc

Please sign in to comment.