Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

fix(api-owner): add constraint validation on dpeDate for post property #938

Merged
merged 1 commit into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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,22 +1,19 @@
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 lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class PropertyForm {

private Long id;

@NotBlank
Rhmaric marked this conversation as resolved.
Show resolved Hide resolved
private String name;

private Double rentCost;
Expand All @@ -37,6 +34,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,110 @@
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 BadRequest when invalid date format (EEEE dd MMMM yyyy) is sent in body", new PropertyTestPayload("Test", "Lundi 15 mars 2021")), 400),
Arguments.of(Named.of("Should return BadRequest when invalid date format (MM/dd/yyyy) is sent in body", new PropertyTestPayload("Test", "02/10/2025")), 400),
Arguments.of(Named.of("Should return BadRequest when invalid date format (dd/MM/yyyy) is sent in body", new PropertyTestPayload("Test", "10/02/2025")), 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 valid body sent", new PropertyTestPayload("Test", "2021-08-01")), 200),
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>