Move backend files into a sub folder.

This commit is contained in:
Florian THIERRY
2024-03-27 10:28:22 +01:00
parent 39663e914d
commit 431d365d20
131 changed files with 3633 additions and 0 deletions

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.codiki</groupId>
<artifactId>codiki-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<artifactId>codiki-domain</artifactId>
<name>codiki-domain</name>
<description>Demo project for Spring Boot</description>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>16</source>
<target>16</target>
</configuration>
</plugin>
</plugins>
</build>
<packaging>jar</packaging>
</project>

View File

@@ -0,0 +1,11 @@
package org.codiki.domain.category.exception;
import java.util.UUID;
import org.codiki.domain.exception.FunctionnalException;
public class CategoryDeletionException extends FunctionnalException {
public CategoryDeletionException(UUID categoryId, String cause) {
super(String.format("Impossible to delete category with id %s. Cause: %s.", categoryId, cause));
}
}

View File

@@ -0,0 +1,13 @@
package org.codiki.domain.category.exception;
import org.codiki.domain.exception.FunctionnalException;
public class CategoryEditionException extends FunctionnalException {
public CategoryEditionException(String reason) {
super(String.format("Impossible to edit a category because : %s.", reason));
}
public CategoryEditionException(FunctionnalException cause) {
super("Impossible to edit a category due to a root cause.", cause);
}
}

View File

@@ -0,0 +1,11 @@
package org.codiki.domain.category.exception;
import java.util.UUID;
import org.codiki.domain.exception.FunctionnalException;
public class CategoryNotFoundException extends FunctionnalException {
public CategoryNotFoundException(UUID categoryId) {
super(String.format("No any category found for id %s.",categoryId));
}
}

View File

@@ -0,0 +1,11 @@
package org.codiki.domain.category.model;
import java.util.List;
import java.util.UUID;
public record Category(
UUID id,
String name,
List<Category> subCategories
) {
}

View File

@@ -0,0 +1,53 @@
package org.codiki.domain.category.model.builder;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.codiki.domain.category.model.Category;
public class CategoryBuilder {
private UUID id;
private String name;
private List<Category> subCategories;
public static CategoryBuilder aCategory() {
return new CategoryBuilder();
}
private CategoryBuilder() {}
public CategoryBuilder basedOn(Category category) {
this.id = category.id();
this.name = category.name();
this.subCategories = category.subCategories();
return this;
}
public CategoryBuilder withId(UUID id) {
this.id = id;
return this;
}
public CategoryBuilder withName(String name) {
this.name = name;
return this;
}
public CategoryBuilder withSubCategories(List<Category> subCategories) {
this.subCategories = subCategories;
return this;
}
public CategoryBuilder withSubCategory(Category subCategory) {
if (subCategories == null) {
subCategories = new ArrayList<>();
}
subCategories.add(subCategory);
return this;
}
public Category build() {
return new Category(id, name, subCategories);
}
}

View File

@@ -0,0 +1,23 @@
package org.codiki.domain.category.port;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.codiki.domain.category.model.Category;
public interface CategoryPort {
Optional<Category> findById(UUID uuid);
void save(Category category);
List<Category> findAllByIds(List<UUID> subCategoryIds);
boolean existsAnyAssociatedPublication(UUID categoryId);
void deleteById(UUID categoryId);
boolean existsById(UUID categoryId);
List<Category> findAll();
}

View File

@@ -0,0 +1,7 @@
package org.codiki.domain.exception;
public class AuthenticationRequiredException extends FunctionnalException {
public AuthenticationRequiredException() {
super("Authentication is required to perform this action.");
}
}

View File

@@ -0,0 +1,15 @@
package org.codiki.domain.exception;
public abstract class FunctionnalException extends RuntimeException {
public FunctionnalException(String message) {
super(message);
}
public FunctionnalException(FunctionnalException cause) {
super(cause);
}
public FunctionnalException(String message, FunctionnalException cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,7 @@
package org.codiki.domain.exception;
public class LoginFailureException extends FunctionnalException {
public LoginFailureException() {
super("Login or password incorrect.");
}
}

View File

@@ -0,0 +1,9 @@
package org.codiki.domain.exception;
import java.util.UUID;
public class RefreshTokenDoesNotExistException extends FunctionnalException {
public RefreshTokenDoesNotExistException(UUID refreshTokenValue) {
super(String.format("Refresh token \"%s\" does not exist.", refreshTokenValue));
}
}

View File

@@ -0,0 +1,9 @@
package org.codiki.domain.exception;
import java.util.UUID;
public class RefreshTokenExpiredException extends FunctionnalException {
public RefreshTokenExpiredException(UUID refreshTokenValue) {
super(String.format("Refresh token \"%s\" is expired.", refreshTokenValue));
}
}

View File

@@ -0,0 +1,9 @@
package org.codiki.domain.exception;
import java.util.UUID;
public class UserDoesNotExistException extends FunctionnalException {
public UserDoesNotExistException(UUID userId) {
super(String.format("User \"%s\" does not exist.", userId));
}
}

View File

@@ -0,0 +1,11 @@
package org.codiki.domain.picture.exception;
import java.util.UUID;
import org.codiki.domain.exception.FunctionnalException;
public class PictureContentLoadingErrorException extends FunctionnalException {
public PictureContentLoadingErrorException(UUID pictureId) {
super(String.format("An error occurred while loading picture content (picture id=%s).", pictureId));
}
}

View File

@@ -0,0 +1,11 @@
package org.codiki.domain.picture.exception;
import java.util.UUID;
import org.codiki.domain.exception.FunctionnalException;
public class PictureNotFoundException extends FunctionnalException {
public PictureNotFoundException(UUID pictureId) {
super(String.format("Picture with id %s is not found.", pictureId));
}
}

View File

@@ -0,0 +1,9 @@
package org.codiki.domain.picture.exception;
import org.codiki.domain.exception.FunctionnalException;
public class PictureStorageErrorException extends FunctionnalException {
public PictureStorageErrorException() {
super("An error occurred while storing picture content file.");
}
}

View File

@@ -0,0 +1,9 @@
package org.codiki.domain.picture.exception;
import org.codiki.domain.exception.FunctionnalException;
public class PictureUploadException extends FunctionnalException {
public PictureUploadException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,10 @@
package org.codiki.domain.picture.model;
import java.io.File;
import java.util.UUID;
public record Picture(
UUID id,
UUID publisherId,
File contentFile
) {}

View File

@@ -0,0 +1,48 @@
package org.codiki.domain.picture.model.builder;
import java.io.File;
import java.util.UUID;
import org.codiki.domain.picture.model.Picture;
import org.codiki.domain.user.model.User;
public class PictureBuilder {
private UUID id;
private UUID publisherId;
private File contentFile;
private PictureBuilder() {}
public static PictureBuilder aPicture() {
return new PictureBuilder();
}
public PictureBuilder basedOn(Picture picture) {
id = picture.id();
contentFile = picture.contentFile();
return this;
}
public PictureBuilder withId(UUID id) {
this.id = id;
return this;
}
public PictureBuilder withPublisherId(UUID publisherId) {
this.publisherId = publisherId;
return this;
}
public PictureBuilder withPublisher(User publisher) {
return withPublisherId(publisher.id());
}
public PictureBuilder withContentFile(File contentFile) {
this.contentFile = contentFile;
return this;
}
public Picture build() {
return new Picture(id, publisherId, contentFile);
}
}

View File

@@ -0,0 +1,16 @@
package org.codiki.domain.picture.port;
import java.util.Optional;
import java.util.UUID;
import org.codiki.domain.picture.model.Picture;
public interface PicturePort {
boolean existsById(UUID pictureId);
Optional<Picture> findById(UUID pictureId);
void save(Picture picture);
void deleteById(UUID pictureId);
}

View File

@@ -0,0 +1,9 @@
package org.codiki.domain.publication.exception;
import org.codiki.domain.exception.FunctionnalException;
public class NoPublicationSearchResultException extends FunctionnalException {
public NoPublicationSearchResultException() {
super("No any publication was found for search criteria.");
}
}

View File

@@ -0,0 +1,13 @@
package org.codiki.domain.publication.exception;
import org.codiki.domain.exception.FunctionnalException;
public class PublicationEditionException extends FunctionnalException {
public PublicationEditionException(String reason) {
super(String.format("Impossible to edit a publication because : %s.", reason));
}
public PublicationEditionException(FunctionnalException cause) {
super(String.format("Impossible to edit a publication due to a root cause: %s.", cause.getMessage()));
}
}

View File

@@ -0,0 +1,11 @@
package org.codiki.domain.publication.exception;
import java.util.UUID;
import org.codiki.domain.exception.FunctionnalException;
public class PublicationNotFoundException extends FunctionnalException {
public PublicationNotFoundException(UUID publicationId) {
super(String.format("No any publication found for id %s.", publicationId));
}
}

View File

@@ -0,0 +1,9 @@
package org.codiki.domain.publication.exception;
import org.codiki.domain.exception.FunctionnalException;
public class PublicationUpdateForbiddenException extends FunctionnalException {
public PublicationUpdateForbiddenException() {
super("Publication update is not allowed because you are not its owner.");
}
}

View File

@@ -0,0 +1,11 @@
package org.codiki.domain.publication.model;
import java.util.UUID;
public record Author(
UUID id,
String name,
String image
) {
}

View File

@@ -0,0 +1,17 @@
package org.codiki.domain.publication.model;
import java.time.ZonedDateTime;
import java.util.UUID;
public record Publication(
UUID id,
String key,
String title,
String text,
String description,
ZonedDateTime creationDate,
UUID illustrationId,
UUID categoryId,
Author author
) {
}

View File

@@ -0,0 +1,11 @@
package org.codiki.domain.publication.model;
import java.util.UUID;
public record PublicationEditionRequest(
String title,
String text,
String description,
UUID illustrationId,
UUID categoryId
) {}

View File

@@ -0,0 +1,46 @@
package org.codiki.domain.publication.model.builder;
import java.util.UUID;
import org.codiki.domain.publication.model.Author;
import org.codiki.domain.user.model.User;
public class AuthorBuilder {
private UUID id;
private String name;
private String image;
private AuthorBuilder() {}
public static AuthorBuilder anAuthor() {
return new AuthorBuilder();
}
public AuthorBuilder basedOn(User user) {
return new AuthorBuilder()
.withId(user.id())
// .withName(user.name())
// .withImage(user.illustrationId())
;
}
public AuthorBuilder withId(UUID id) {
this.id = id;
return this;
}
public AuthorBuilder withName(String name) {
this.name = name;
return this;
}
public AuthorBuilder withImage(String image) {
this.image = image;
return this;
}
public Author build() {
//
return new Author(id, name, image);
}
}

View File

@@ -0,0 +1,98 @@
package org.codiki.domain.publication.model.builder;
import java.time.ZonedDateTime;
import java.util.UUID;
import org.codiki.domain.category.model.Category;
import org.codiki.domain.publication.model.Author;
import org.codiki.domain.publication.model.Publication;
public class PublicationBuilder {
private UUID id;
private String key;
private String title;
private String text;
private String description;
private ZonedDateTime creationDate;
private UUID illustrationId;
private UUID categoryId;
private Author author;
private PublicationBuilder() {}
public static PublicationBuilder aPublication() {
return new PublicationBuilder();
}
public PublicationBuilder basedOn(Publication publication) {
return new PublicationBuilder()
.withId(publication.id())
.withKey(publication.key())
.withTitle(publication.title())
.withText(publication.text())
.withDescription(publication.description())
.withCreationDate(publication.creationDate())
.withIllustrationId(publication.illustrationId())
.withCategoryId(publication.categoryId())
.withAuthor(publication.author());
}
public PublicationBuilder withId(UUID id) {
this.id = id;
return this;
}
public PublicationBuilder withKey(String key) {
this.key = key;
return this;
}
public PublicationBuilder withTitle(String title) {
this.title = title;
return this;
}
public PublicationBuilder withText(String text) {
this.text = text;
return this;
}
public PublicationBuilder withDescription(String description) {
this.description = description;
return this;
}
public PublicationBuilder withCreationDate(ZonedDateTime creationDate) {
this.creationDate = creationDate;
return this;
}
public PublicationBuilder withIllustrationId(UUID illustrationId) {
this.illustrationId = illustrationId;
return this;
}
public PublicationBuilder withCategoryId(UUID categoryId) {
this.categoryId = categoryId;
return this;
}
public PublicationBuilder withAuthor(Author author) {
this.author = author;
return this;
}
public Publication build() {
return new Publication(
id,
key,
title,
text,
description,
creationDate,
illustrationId,
categoryId,
author
);
}
}

View File

@@ -0,0 +1,8 @@
package org.codiki.domain.publication.model.search;
public enum ComparisonType {
EQUALS,
CONTAINS,
BEFORE,
AFTER
}

View File

@@ -0,0 +1,7 @@
package org.codiki.domain.publication.model.search;
public record PublicationSearchCriterion(
PublicationSearchField searchField,
ComparisonType searchType,
Object value
) { }

View File

@@ -0,0 +1,26 @@
package org.codiki.domain.publication.model.search;
import java.util.Optional;
import java.util.stream.Stream;
public enum PublicationSearchField {
ID,
KEY,
TITLE,
TEXT,
DESCRIPTION,
CREATION_DATE,
CATEGORY_ID,
AUTHOR_ID,
AUTHOR_PSEUDO;
public static Optional<PublicationSearchField> from(String fieldName) {
return Optional.ofNullable(fieldName)
.map(String::toUpperCase)
.flatMap(uppercaseFieldName ->
Stream.of(PublicationSearchField.values())
.filter(field -> field.name().equals(uppercaseFieldName))
.findFirst()
);
}
}

View File

@@ -0,0 +1,18 @@
package org.codiki.domain.publication.port;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.codiki.domain.publication.model.Publication;
import org.codiki.domain.publication.model.search.PublicationSearchCriterion;
public interface PublicationPort {
void save(Publication publication);
Optional<Publication> findById(UUID publicationId);
void delete(Publication publication);
List<Publication> search(List<PublicationSearchCriterion> criteria);
}

View File

@@ -0,0 +1,9 @@
package org.codiki.domain.user.exception;
import org.codiki.domain.exception.FunctionnalException;
public class UserAlreadyExistsException extends FunctionnalException {
public UserAlreadyExistsException() {
super("An user already exists with this email address.");
}
}

View File

@@ -0,0 +1,9 @@
package org.codiki.domain.user.exception;
import org.codiki.domain.exception.FunctionnalException;
public class UserCreationException extends FunctionnalException {
public UserCreationException() {
super("Pseudo, email address and password can not be empty.");
}
}

View File

@@ -0,0 +1,22 @@
package org.codiki.domain.user.model;
import java.time.ZonedDateTime;
import java.util.UUID;
public record RefreshToken(
UUID userId,
UUID value,
ZonedDateTime expirationDate
) {
public RefreshToken(UUID userId, ZonedDateTime exporationDate) {
this(userId, UUID.randomUUID(), exporationDate);
}
public boolean isExpired() {
return ZonedDateTime.now().isAfter(expirationDate);
}
public boolean isNotExpired() {
return !isExpired();
}
}

View File

@@ -0,0 +1,34 @@
package org.codiki.domain.user.model;
import static java.util.Objects.isNull;
import static java.util.stream.Collectors.joining;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public record User(
UUID id,
String pseudo,
String email,
String password,
UUID photoId,
List<UserRole> roles
) {
public Map<String, Object> toJwtPayload() {
Map<String, Object> result = new HashMap<>(4);
result.put("pseudo", pseudo);
result.put("email", email);
if (!isNull(photoId)) {
result.put("photoId", photoId.toString());
}
String rolesAsString = roles.stream()
.map(UserRole::name)
.collect(joining(","));
result.put("roles", rolesAsString);
return result;
}
}

View File

@@ -0,0 +1,8 @@
package org.codiki.domain.user.model;
public record UserAuthenticationData(
String tokenType,
String accessToken,
RefreshToken refreshToken
) {
}

View File

@@ -0,0 +1,15 @@
package org.codiki.domain.user.model;
import java.util.Optional;
import java.util.stream.Stream;
public enum UserRole {
STANDARD,
ADMIN;
public static Optional<UserRole> from(String roleAsString) {
return Stream.of(UserRole.values())
.filter(role -> role.name().equals(roleAsString))
.findFirst();
}
}

View File

@@ -0,0 +1,74 @@
package org.codiki.domain.user.model.builder;
import static java.util.Objects.isNull;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.codiki.domain.user.exception.UserCreationException;
import org.codiki.domain.user.model.User;
import org.codiki.domain.user.model.UserRole;
public class UserBuilder {
private UUID id;
private String pseudo;
private String email;
private String password;
private UUID photoId;
private Set<UserRole> roles = new HashSet<>();
private UserBuilder() {}
public static UserBuilder anUser() {
return new UserBuilder();
}
public UserBuilder withId(UUID id) {
this.id = id;
return this;
}
public UserBuilder withPseudo(String pseudo) {
this.pseudo = pseudo;
return this;
}
public UserBuilder withEmail(String email) {
this.email = email;
return this;
}
public UserBuilder withPassword(String password) {
this.password = password;
return this;
}
public UserBuilder withPhotoId(UUID photoId) {
this.photoId = photoId;
return this;
}
public UserBuilder withRole(UserRole role) {
this.roles.add(role);
return this;
}
public UserBuilder withRoles(List<UserRole> roles) {
this.roles = new HashSet<>(roles);
return this;
}
public User build() {
if (isNull(id) || isNull(pseudo) || isNull(email) || isNull(password) || isEmpty(roles)) {
throw new UserCreationException();
}
return new User(id, pseudo, email, password, photoId, new LinkedList<>(roles));
}
private static boolean isEmpty(Set<UserRole> roles) {
return isNull(roles) || roles.isEmpty();
}
}

View File

@@ -0,0 +1,28 @@
package org.codiki.domain.user.port;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.codiki.domain.user.model.RefreshToken;
import org.codiki.domain.user.model.User;
public interface UserPort {
Optional<User> findById(UUID userId);
Optional<User> findByEmail(String userEmail);
List<User> findAll();
void save(User user);
boolean existsById(UUID userId);
Optional<RefreshToken> findRefreshTokenByUserId(UUID userId);
Optional<RefreshToken> findRefreshTokenById(UUID refreshTokenId);
void save(RefreshToken refreshToken);
boolean existsByEmail(String email);
}