package de.juplo.kafka.chat.backend.api;
+import de.juplo.kafka.chat.backend.domain.InvalidUsernameException;
import de.juplo.kafka.chat.backend.domain.MessageMutationException;
import de.juplo.kafka.chat.backend.domain.UnknownChatroomException;
import org.springframework.beans.factory.annotation.Value;
return problem;
}
+
+ @ExceptionHandler(InvalidUsernameException.class)
+ public final ProblemDetail handleException(
+ InvalidUsernameException e,
+ ServerWebExchange exchange,
+ UriComponentsBuilder uriComponentsBuilder)
+ {
+ final HttpStatus status = HttpStatus.BAD_REQUEST;
+ ProblemDetail problem = ProblemDetail.forStatus(status);
+
+ problem.setProperty("timestamp", new Date());
+
+ problem.setProperty("requestId", exchange.getRequest().getId());
+
+ problem.setType(uriComponentsBuilder.replacePath(contextPath).path("/problem/invalid-username").build().toUri());
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append(status.getReasonPhrase());
+ stringBuilder.append(" - ");
+ stringBuilder.append(e.getMessage());
+ problem.setTitle(stringBuilder.toString());
+
+ stringBuilder.setLength(0);
+ stringBuilder.append("Invalid username: ");
+ stringBuilder.append(e.getUsername());
+ stringBuilder.append(
+ "! A valid username must consist of at at least two letters and " +
+ "must only contain lower case letters a-z, numbers and dashes");
+ problem.setDetail(stringBuilder.toString());
+
+ problem.setProperty("username", e.getUsername());
+
+ return problem;
+ }
}
import java.time.Clock;
import java.time.LocalDateTime;
import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
@Slf4j
@ToString(of = { "id", "name" })
public class ChatRoom
{
+ public final static Pattern VALID_USER = Pattern.compile("^[a-z0-9-]{2,}$");
@Getter
private final UUID id;
@Getter
String user,
String text)
{
+ Matcher matcher = VALID_USER.matcher(user);
+ if (!matcher.matches())
+ throw new InvalidUsernameException(user);
+
Message.MessageKey key = Message.MessageKey.of(user, id);
return service
.getMessage(key)
--- /dev/null
+package de.juplo.kafka.chat.backend.domain;
+
+import lombok.Getter;
+
+
+public class InvalidUsernameException extends RuntimeException
+{
+ @Getter
+ private final String username;
+
+ public InvalidUsernameException(String username)
+ {
+ super("Invalid username: " + username);
+ this.username = username;
+ }
+}
.jsonPath("$.mutatedText").isEqualTo(textMutatedMessage);
verify(chatRoomService, never()).persistMessage(eq(key), any(LocalDateTime.class), any(String.class));
}
+
+ @Test
+ @DisplayName("Assert expected problem-details for invalid username on PUT /put/{chatroomId}/{username}/{messageId}")
+ void testInvalidUsernameException(@Autowired WebTestClient client) throws Exception
+ {
+ // Given
+ UUID chatroomId = UUID.randomUUID();
+ String user = "Foo";
+ Long messageId = 66l;
+ Message.MessageKey key = Message.MessageKey.of(user, messageId);
+ String textMessage = "Hallo Welt";
+ ChatRoom chatRoom = new ChatRoom(
+ chatroomId,
+ "Test-ChatRoom",
+ Clock.systemDefaultZone(),
+ chatRoomService, 8);
+ when(chatHomeService.getChatRoom(any(UUID.class)))
+ .thenReturn(Mono.just(chatRoom));
+ when(chatRoomService.getMessage(any(Message.MessageKey.class)))
+ .thenReturn(Mono.empty());
+ // Needed for readable error-reports, in case of a bug that leads to according unwanted call
+ when(chatRoomService.persistMessage(any(Message.MessageKey.class), any(LocalDateTime.class), any(String.class)))
+ .thenReturn(mock(Message.class));
+
+ // When
+ client
+ .put()
+ .uri(
+ "/put/{chatroomId}/{username}/{messageId}",
+ chatroomId,
+ user,
+ messageId)
+ .bodyValue(textMessage)
+ .accept(MediaType.APPLICATION_JSON)
+ .exchange()
+ // Then
+ .expectStatus().is4xxClientError()
+ .expectBody()
+ .jsonPath("$.type").isEqualTo("/problem/invalid-username")
+ .jsonPath("$.username").isEqualTo(user);
+ verify(chatRoomService, never()).persistMessage(eq(key), any(LocalDateTime.class), any(String.class));
+ }
}