Compare commits

5 Commits

Author SHA1 Message Date
Florian THIERRY
fb13cfd74d Implementation of getting publication by its id. 2024-03-14 10:05:36 +01:00
Florian THIERRY
8d8a220fa0 Add picture fetch while retrieving a publication. 2024-03-14 09:59:46 +01:00
Florian THIERRY
adc3cdf9a3 Add dependency to pictures in publications. 2024-03-14 09:55:51 +01:00
Florian THIERRY
5c5304ff98 Reworking the exception handling. 2024-03-14 09:31:14 +01:00
Florian THIERRY
a872a9fe33 Add picture format control. 2024-03-14 09:24:19 +01:00
26 changed files with 241 additions and 102 deletions

View File

@@ -1,6 +1,7 @@
package org.codiki.application.picture;
import java.io.File;
import java.util.Optional;
import java.util.UUID;
import static org.codiki.domain.picture.model.builder.PictureBuilder.aPicture;
@@ -30,4 +31,8 @@ public class PictureUseCases {
public void deletePicture(UUID pictureId) {
picturePort.deleteById(pictureId);
}
public Optional<Picture> findById(UUID pictureId) {
return picturePort.findById(pictureId);
}
}

View File

@@ -19,8 +19,8 @@ public class PublicationCreationRequestValidator {
throw new PublicationEditionException("description cannot be null");
}
if (request.image() == null) {
throw new PublicationEditionException("image cannot be null");
if (request.pictureId() == null) {
throw new PublicationEditionException("pictureId cannot be null");
}
}
}

View File

@@ -13,7 +13,7 @@ public class PublicationUpdateRequestValidator {
isNull(request.title()) &&
isNull(request.text()) &&
isNull(request.description()) &&
isNull(request.image()) &&
isNull(request.pictureId()) &&
isNull(request.categoryId())
) {
throw new PublicationEditionException("no any field is filled");

View File

@@ -3,15 +3,19 @@ package org.codiki.application.publication;
import static java.util.Objects.isNull;
import java.time.Clock;
import java.time.ZonedDateTime;
import java.util.Optional;
import java.util.UUID;
import static org.codiki.domain.publication.model.builder.AuthorBuilder.anAuthor;
import static org.codiki.domain.publication.model.builder.PublicationBuilder.aPublication;
import org.codiki.application.category.CategoryUseCases;
import org.codiki.application.picture.PictureUseCases;
import org.codiki.application.user.UserUseCases;
import org.codiki.domain.category.exception.CategoryNotFoundException;
import org.codiki.domain.category.model.Category;
import org.codiki.domain.exception.AuthenticationRequiredException;
import org.codiki.domain.picture.exception.PictureNotFoundException;
import org.codiki.domain.picture.model.Picture;
import org.codiki.domain.publication.exception.PublicationEditionException;
import org.codiki.domain.publication.exception.PublicationNotFoundException;
import org.codiki.domain.publication.exception.PublicationUpdateForbiddenException;
@@ -25,19 +29,22 @@ import org.springframework.stereotype.Service;
@Service
public class PublicationUseCases {
private final CategoryUseCases categoryUseCases;
private final Clock clock;
private final KeyGenerator keyGenerator;
private final PictureUseCases pictureUseCases;
private final PublicationPort publicationPort;
private final PublicationCreationRequestValidator publicationCreationRequestValidator;
private final PublicationUpdateRequestValidator publicationUpdateRequestValidator;
private final UserUseCases userUseCases;
private final Clock clock;
public PublicationUseCases(
CategoryUseCases categoryUseCases,
Clock clock,
KeyGenerator keyGenerator,
PictureUseCases pictureUseCases,
PublicationCreationRequestValidator publicationCreationRequestValidator,
PublicationPort publicationPort, PublicationUpdateRequestValidator publicationUpdateRequestValidator,
PublicationPort publicationPort,
PublicationUpdateRequestValidator publicationUpdateRequestValidator,
UserUseCases userUseCases
) {
this.categoryUseCases = categoryUseCases;
@@ -47,6 +54,7 @@ public class PublicationUseCases {
this.publicationPort = publicationPort;
this.publicationUpdateRequestValidator = publicationUpdateRequestValidator;
this.userUseCases = userUseCases;
this.pictureUseCases = pictureUseCases;
}
public Publication createPublication(PublicationEditionRequest request) {
@@ -60,13 +68,18 @@ public class PublicationUseCases {
new CategoryNotFoundException(request.categoryId())
));
Picture picture = pictureUseCases.findById(request.pictureId())
.orElseThrow(() -> new PublicationEditionException(
new PictureNotFoundException(request.pictureId())
));
Publication newPublication = aPublication()
.withId(UUID.randomUUID())
.withKey(keyGenerator.generateKey())
.withTitle(request.title())
.withText(request.text())
.withDescription(request.description())
.withImage(request.image())
.withPicture(picture)
.withCreationDate(ZonedDateTime.now(clock))
.withAuthor(anAuthor().basedOn(authenticatedUser).build())
.withCategory(category)
@@ -104,8 +117,12 @@ public class PublicationUseCases {
publicationBuilder.withDescription(request.description());
}
if (!isNull(request.image())) {
publicationBuilder.withImage(request.image());
if (!isNull(request.pictureId())) {
Picture picture = pictureUseCases.findById(request.pictureId())
.orElseThrow(() -> new PublicationEditionException(
new PictureNotFoundException(request.pictureId())
));
publicationBuilder.withPicture(picture);
}
if (!isNull(request.categoryId())) {
@@ -137,4 +154,8 @@ public class PublicationUseCases {
publicationPort.delete(publicationToDelete);
}
public Optional<Publication> findById(UUID publicationId) {
return publicationPort.findById(publicationId);
}
}

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 PictureUploadException extends FunctionnalException {
public PictureUploadException(String message) {
super(message);
}
}

View File

@@ -8,6 +8,6 @@ public class PublicationEditionException extends FunctionnalException {
}
public PublicationEditionException(FunctionnalException cause) {
super("Impossible to edit a publication due to a root cause.", cause);
super(String.format("Impossible to edit a publication due to a root cause: %s.", cause.getMessage()));
}
}

View File

@@ -4,6 +4,7 @@ import java.time.ZonedDateTime;
import java.util.UUID;
import org.codiki.domain.category.model.Category;
import org.codiki.domain.picture.model.Picture;
public record Publication(
UUID id,
@@ -11,8 +12,8 @@ public record Publication(
String title,
String text,
String description,
String image,
ZonedDateTime creationDate,
Picture picture,
Author author,
Category category
) {

View File

@@ -6,6 +6,6 @@ public record PublicationEditionRequest(
String title,
String text,
String description,
String image,
UUID pictureId,
UUID categoryId
) {}

View File

@@ -20,7 +20,7 @@ public class AuthorBuilder {
return new AuthorBuilder()
.withId(user.id())
// .withName(user.name())
// .withImage(user.image())
// .withImage(user.pictureId())
;
}

View File

@@ -3,6 +3,7 @@ package org.codiki.domain.publication.model.builder;
import java.time.ZonedDateTime;
import java.util.UUID;
import org.codiki.domain.picture.model.Picture;
import org.codiki.domain.publication.model.Author;
import org.codiki.domain.category.model.Category;
import org.codiki.domain.publication.model.Publication;
@@ -13,7 +14,7 @@ public class PublicationBuilder {
private String title;
private String text;
private String description;
private String image;
private Picture picture;
private ZonedDateTime creationDate;
private Author author;
private Category category;
@@ -49,8 +50,8 @@ public class PublicationBuilder {
return this;
}
public PublicationBuilder withImage(String image) {
this.image = image;
public PublicationBuilder withPicture(Picture picture) {
this.picture = picture;
return this;
}
@@ -76,8 +77,7 @@ public class PublicationBuilder {
title,
text,
description,
image,
creationDate,
creationDate, picture,
author,
category
);
@@ -90,7 +90,7 @@ public class PublicationBuilder {
.withTitle(publication.title())
.withText(publication.text())
.withDescription(publication.description())
.withImage(publication.image())
.withPicture(publication.picture())
.withCreationDate(publication.creationDate())
.withAuthor(publication.author())
.withCategory(publication.category());

View File

@@ -29,6 +29,10 @@
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-data-jpa</artifactId>-->

View File

@@ -11,73 +11,57 @@ import org.codiki.domain.exception.LoginFailureException;
import org.codiki.domain.exception.RefreshTokenDoesNotExistException;
import org.codiki.domain.exception.RefreshTokenExpiredException;
import org.codiki.domain.exception.UserDoesNotExistException;
import org.codiki.domain.picture.exception.PictureNotFoundException;
import org.codiki.domain.picture.exception.PictureUploadException;
import org.codiki.domain.publication.exception.PublicationEditionException;
import org.codiki.domain.publication.exception.PublicationNotFoundException;
import org.codiki.domain.publication.exception.PublicationUpdateForbiddenException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.http.HttpStatus;
import org.springframework.http.ProblemDetail;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
@ControllerAdvice
public class GlobalControllerExceptionHandler {
@ResponseStatus(BAD_REQUEST)
@ExceptionHandler(LoginFailureException.class)
public void handleLoginFailureException() {
// Do nothing.
@RestControllerAdvice
public class GlobalControllerExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler({
CategoryDeletionException.class,
CategoryEditionException.class,
CategoryNotFoundException.class,
LoginFailureException.class,
PublicationEditionException.class,
PictureUploadException.class
})
public ProblemDetail handleBadRequestExceptions(Exception exception) {
return buildProblemDetail(BAD_REQUEST, exception);
}
@ResponseStatus(NOT_FOUND)
@ExceptionHandler(UserDoesNotExistException.class)
public void handleUserDoesNotExistException() {
// Do nothing.
@ExceptionHandler({
UserDoesNotExistException.class,
RefreshTokenDoesNotExistException.class,
PublicationNotFoundException.class,
PictureNotFoundException.class
})
public ProblemDetail handleNotFoundExceptions(Exception exception) {
return buildProblemDetail(NOT_FOUND, exception);
}
@ResponseStatus(NOT_FOUND)
@ExceptionHandler(RefreshTokenDoesNotExistException.class)
public void handleRefreshTokenDoesNotExistException() {
// Do nothing.
@ExceptionHandler({
RefreshTokenExpiredException.class
})
public ProblemDetail handleUnauthorizedExceptions(Exception exception) {
return buildProblemDetail(UNAUTHORIZED, exception);
}
@ResponseStatus(UNAUTHORIZED)
@ExceptionHandler(RefreshTokenExpiredException.class)
public void handleRefreshTokenExpiredException() {
// Do nothing.
@ExceptionHandler({
PublicationUpdateForbiddenException.class
})
public ProblemDetail handleForbiddenExceptions(Exception exception) {
return buildProblemDetail(FORBIDDEN, exception);
}
@ResponseStatus(BAD_REQUEST)
@ExceptionHandler(CategoryNotFoundException.class)
public void handleCategoryNotFoundException() {
// Do nothing.
private static ProblemDetail buildProblemDetail(HttpStatus forbidden, Exception exception) {
return ProblemDetail.forStatusAndDetail(forbidden, exception.getMessage());
}
@ResponseStatus(BAD_REQUEST)
@ExceptionHandler(PublicationEditionException.class)
public void handlePublicationEditionException() {
// Do nothing.
}
@ResponseStatus(NOT_FOUND)
@ExceptionHandler(PublicationNotFoundException.class)
public void handlePublicationNotFoundException() {
// Do nothing.
}
@ResponseStatus(FORBIDDEN)
@ExceptionHandler(PublicationUpdateForbiddenException.class)
public void handlePublicationUpdateForbiddenException() {
// Do nothing.
}
@ResponseStatus(BAD_REQUEST)
@ExceptionHandler(CategoryEditionException.class)
public void handleCategoryEditionException() {
// Do nothing.
}
@ResponseStatus(BAD_REQUEST)
@ExceptionHandler(CategoryDeletionException.class)
public void handleCategoryDeletionException() {
// Do nothing.
}
}

View File

@@ -6,6 +6,7 @@ import static org.springframework.http.HttpMethod.OPTIONS;
import static org.springframework.http.HttpMethod.POST;
import static org.springframework.http.HttpMethod.PUT;
import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS;
import org.codiki.domain.user.model.UserRole;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
@@ -44,6 +45,8 @@ public class SecurityConfiguration {
GET,
"/api/health/check",
"/api/categories",
"/api/pictures/{pictureId}",
"/api/publications/{publicationId}",
"/error"
).permitAll()
.requestMatchers(
@@ -54,15 +57,15 @@ public class SecurityConfiguration {
.requestMatchers(
POST,
"/api/categories"
).hasRole("ADMIN")
).hasRole(UserRole.ADMIN.name())
.requestMatchers(
PUT,
"/api/categories/{categoryId}"
).hasRole("ADMIN")
).hasRole(UserRole.ADMIN.name())
.requestMatchers(
DELETE,
"/api/categories/{categoryId}"
).hasRole("ADMIN")
).hasRole(UserRole.ADMIN.name())
.requestMatchers(OPTIONS).permitAll()
.anyRequest().authenticated()
);

View File

@@ -1,23 +1,44 @@
package org.codiki.exposition.picture;
import static java.util.Objects.isNull;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.UUID;
import org.apache.tika.mime.MimeType;
import org.apache.tika.mime.MimeTypeException;
import org.apache.tika.mime.MimeTypes;
import org.codiki.domain.picture.exception.PictureUploadException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
@Component
public class MultipartFileConverter {
private final String tempPicturesForlderPath;
private static final List<MimeType> ALLOWED_MIME_TYPES;
public MultipartFileConverter(@Value("${application.pictures.temp-path}") String tempPicturesForlderPath) {
this.tempPicturesForlderPath = tempPicturesForlderPath;
static {
MimeTypes mimeTypes = MimeTypes.getDefaultMimeTypes();
try {
ALLOWED_MIME_TYPES = List.of(
mimeTypes.forName("image/png"),
mimeTypes.forName("image/jpeg")
);
} catch (MimeTypeException exception) {
throw new RuntimeException("An error occurred while loading allowed mime types.", exception);
}
}
private final String tempPicturesFolderPath;
public MultipartFileConverter(@Value("${application.pictures.temp-path}") String tempPicturesFolderPath) {
this.tempPicturesFolderPath = tempPicturesFolderPath;
}
public File transformToFile(MultipartFile fileContent) {
File pictureFile = new File(String.format("%s/%s", tempPicturesForlderPath, UUID.randomUUID()));
File pictureFile = new File(buildPicturePath(fileContent));
try {
fileContent.transferTo(pictureFile);
} catch (IOException e) {
@@ -25,4 +46,34 @@ public class MultipartFileConverter {
}
return pictureFile;
}
private String buildPicturePath(MultipartFile fileContent) {
MimeType fileContentType = extractMimeType(fileContent);
return String.format(
"%s/%s%s",
tempPicturesFolderPath,
UUID.randomUUID(),
fileContentType.getExtension()
);
}
private MimeType extractMimeType(MultipartFile fileContent) {
MimeType result = null;
try {
result = MimeTypes.getDefaultMimeTypes()
.forName(fileContent.getContentType());
} catch (MimeTypeException exception) {
// Do nothing
}
if (isNull(result) || !isAllowedMimeType(result)) {
throw new PictureUploadException("Unable to upload the picture because its format is incorrect.");
}
return result;
}
private boolean isAllowedMimeType(MimeType mimeType) {
return ALLOWED_MIME_TYPES.contains(mimeType);
}
}

View File

@@ -3,9 +3,14 @@ package org.codiki.exposition.picture;
import java.io.File;
import java.util.UUID;
import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE;
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
import org.codiki.application.picture.PictureUseCases;
import org.codiki.domain.picture.exception.PictureNotFoundException;
import org.codiki.domain.picture.model.Picture;
import org.springframework.core.io.FileSystemResource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@@ -32,4 +37,11 @@ public class PictureController {
Picture newPicture = pictureUseCases.createPicture(pictureFile);
return newPicture.id();
}
@GetMapping(value = "/{pictureId}", produces = APPLICATION_OCTET_STREAM_VALUE)
public FileSystemResource loadPicture(@PathVariable("pictureId") UUID pictureId) {
Picture picture = pictureUseCases.findById(pictureId)
.orElseThrow(() -> new PictureNotFoundException(pictureId));
return new FileSystemResource(picture.contentFile());
}
}

View File

@@ -5,11 +5,13 @@ import java.util.UUID;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.NO_CONTENT;
import org.codiki.application.publication.PublicationUseCases;
import org.codiki.domain.publication.exception.PublicationNotFoundException;
import org.codiki.domain.publication.model.Publication;
import org.codiki.domain.publication.model.PublicationEditionRequest;
import org.codiki.exposition.publication.model.PublicationDto;
import org.codiki.exposition.publication.model.PublicationEditionRequestDto;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
@@ -27,6 +29,13 @@ public class PublicationController {
this.publicationUseCases = publicationUseCases;
}
@GetMapping("/{publicationId}")
public PublicationDto getById(@PathVariable("publicationId") UUID publicationId) {
return publicationUseCases.findById(publicationId)
.map(PublicationDto::new)
.orElseThrow(() -> new PublicationNotFoundException(publicationId));
}
@PostMapping
@ResponseStatus(CREATED)
public PublicationDto createPublication(@RequestBody PublicationEditionRequestDto requestDto) {

View File

@@ -1,11 +1,10 @@
package org.codiki.exposition.publication;
package org.codiki.exposition.publication.model;
import java.time.ZonedDateTime;
import java.util.UUID;
import org.codiki.domain.publication.model.Publication;
import org.codiki.exposition.category.model.CategoryDto;
import org.codiki.exposition.publication.model.AuthorDto;
public record PublicationDto(
UUID id,
@@ -13,8 +12,8 @@ public record PublicationDto(
String title,
String text,
String description,
String image,
ZonedDateTime creationDate,
UUID picture,
AuthorDto author,
CategoryDto category
) {
@@ -25,8 +24,8 @@ public record PublicationDto(
publication.title(),
publication.text(),
publication.description(),
publication.image(),
publication.creationDate(),
publication.picture().id(),
new AuthorDto(publication.author()),
new CategoryDto(publication.category())
);

View File

@@ -8,10 +8,10 @@ public record PublicationEditionRequestDto(
String title,
String text,
String description,
String image,
UUID pictureId,
UUID categoryId
) {
public PublicationEditionRequest toDomain() {
return new PublicationEditionRequest(title, text, description, image, categoryId);
return new PublicationEditionRequest(title, text, description, pictureId, categoryId);
}
}

View File

@@ -4,6 +4,8 @@ import java.io.File;
import java.util.Optional;
import java.util.UUID;
import static org.codiki.domain.picture.model.builder.PictureBuilder.aPicture;
import org.codiki.domain.picture.exception.PictureNotFoundException;
import org.codiki.domain.picture.exception.PictureStorageErrorException;
import org.codiki.domain.picture.model.Picture;
import org.codiki.domain.picture.port.PicturePort;
@@ -35,7 +37,17 @@ public class PictureJpaAdapter implements PicturePort {
@Override
public Optional<Picture> findById(UUID pictureId) {
return repository.findById(pictureId)
.map(PictureEntity::toDomain);
.map(PictureEntity::toDomain)
.map(picture -> {
File pictureFile = new File(String.format("%s/%s", pictureFolderPath, pictureId));
if (!pictureFile.exists()) {
throw new PictureNotFoundException(pictureId);
}
return aPicture()
.basedOn(picture)
.withContentFile(pictureFile)
.build();
});
}
@Override

View File

@@ -24,13 +24,13 @@ public class AuthorEntity {
private UUID id;
@Column(nullable = false)
private String name;
// private String image;
// private String pictureId;
public AuthorEntity(Author author) {
this(
author.id(),
author.name()
// author.image()
// author.pictureId()
);
}

View File

@@ -5,6 +5,7 @@ import java.util.UUID;
import org.codiki.domain.publication.model.Publication;
import org.codiki.infrastructure.category.model.CategoryEntity;
import org.codiki.infrastructure.picture.model.PictureEntity;
import static jakarta.persistence.FetchType.LAZY;
import jakarta.persistence.Column;
@@ -36,10 +37,11 @@ public class PublicationEntity {
@Column(nullable = false)
private String description;
@Column(nullable = false)
private String image;
@Column(nullable = false)
private ZonedDateTime creationDate;
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "picture_id")
private PictureEntity picture;
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "author_id")
private AuthorEntity author;
@ManyToOne(fetch = LAZY)
@@ -53,8 +55,8 @@ public class PublicationEntity {
publication.title(),
publication.text(),
publication.description(),
publication.image(),
publication.creationDate(),
new PictureEntity(publication.picture()),
new AuthorEntity(publication.author()),
new CategoryEntity(publication.category())
);
@@ -67,8 +69,7 @@ public class PublicationEntity {
title,
text,
description,
image,
creationDate,
creationDate, picture.toDomain(),
author.toDomain(),
category.toDomain()
);

View File

@@ -9,6 +9,13 @@ import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface PublicationRepository extends JpaRepository<PublicationEntity, UUID> {
@Query("SELECT p FROM PublicationEntity p JOIN FETCH p.author a JOIN FETCH p.category C WHERE p.id = :publicationId")
@Query("""
SELECT p
FROM PublicationEntity p
JOIN FETCH p.picture pi
JOIN FETCH p.author a
JOIN FETCH p.category c
WHERE p.id = :publicationId
""")
Optional<PublicationEntity> findById(@Param("publicationId") UUID publicationId);
}

View File

@@ -31,24 +31,27 @@ CREATE TABLE IF NOT EXISTS category (
);
CREATE INDEX category_parent_category_id_idx ON category (parent_category_id);
CREATE TABLE IF NOT EXISTS picture (
id UUID NOT NULL,
CONSTRAINT picture_pk PRIMARY KEY (id)
);
CREATE TABLE IF NOT EXISTS publication (
id UUID NOT NULL,
key VARCHAR(10) NOT NULL,
title VARCHAR NOT NULL,
text VARCHAR NOT NULL,
description VARCHAR NOT NULL,
image VARCHAR NOT NULL,
creation_date TIMESTAMP NOT NULL,
picture_id UUID NOT NULL,
author_id UUID NOT NULL,
category_id UUID NOT NULL,
CONSTRAINT publication_pk PRIMARY KEY (id),
CONSTRAINT publication_picture_id_fk FOREIGN KEY (picture_id) REFERENCES picture (id),
CONSTRAINT publication_author_id_fk FOREIGN KEY (author_id) REFERENCES "user" (id),
CONSTRAINT publication_category_id_fk FOREIGN KEY (category_id) REFERENCES category (id)
);
CREATE INDEX publication_picture_id_idx ON publication (picture_id);
CREATE INDEX publication_author_id_idx ON publication (author_id);
CREATE INDEX publication_category_id_idx ON publication (category_id);
CREATE TABLE IF NOT EXISTS picture (
id UUID NOT NULL,
CONSTRAINT picture_pk PRIMARY KEY (id)
);

View File

@@ -18,6 +18,7 @@
<jakarta.servlet-api.version>6.0.0</jakarta.servlet-api.version>
<java-jwt.version>4.4.0</java-jwt.version>
<postgresql.version>42.7.0</postgresql.version>
<tika-core.version>2.9.0</tika-core.version>
</properties>
<modules>
@@ -72,6 +73,11 @@
<artifactId>postgresql</artifactId>
<version>${postgresql.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
<version>${tika-core.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

View File

@@ -1,6 +1,7 @@
vars {
url: http://localhost:8080
publicationId: fce1de27-11c6-4deb-a248-b63288c00037
bearerToken: eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI1YWQ0NjJiOC04ZjllLTRhMjYtYmI4Ni1jNzRmZWY1ZDExYjYiLCJleHAiOjE3MTAzMzc2Njd9.ExV8xDeqqKk5WjIVb16NBqF1gPoRqx7uL4jQIhWjjY0QVhB5EAGdHMIbLr4s9Ck2f6z8U4sRlpPAQquDOr_9NA
publicationId: e23831a6-9cc0-4f3d-9efa-7a1cae191cb1
bearerToken: eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI1YWQ0NjJiOC04ZjllLTRhMjYtYmI4Ni1jNzRmZWY1ZDExYjYiLCJleHAiOjE3MTA0MDc5Mjd9.QdanZXjfLztIVJU-pRS3gZxnC4GIycyhmlCkhYSPchiXpeNzruw_GY3fH1_qshC_AjOCSqGmCq3X1S_zVFdodQ
categoryId: 172fa901-3f4b-4540-92f3-1c15820e8ec9
pictureId: 65b660b7-66bb-4e4a-a62c-fd0ca101f972
}