From 2c6711c344e3b72fa626fc7e92d99ef5f61f3ee0 Mon Sep 17 00:00:00 2001 From: KinTrae Date: Tue, 7 Jan 2025 22:24:23 +0100 Subject: [PATCH 1/2] feature: Integration with OCI Object Storage task: 8697bjvqr --- .github/workflows/maven.yml | 4 +- backend/pom.xml | 228 +++++++++++------- .../backend/ext/oci/MockOCIUploadService.java | 23 ++ .../ext/oci/OCIClientConfiguration.java | 26 ++ .../backend/ext/oci/OCIController.java | 42 ++++ .../backend/ext/oci/OCIUploadService.java | 86 +++++++ .../java/meowhub/backend/jpa_buddy/Group.java | 12 - .../jpa_buddy/MatchingProfilePicture.java | 17 +- .../backend/jpa_buddy/ProfilePicture.java | 11 +- .../posts/controllers/PostController.java | 11 +- .../meowhub/backend/posts/dtos/PostDto.java | 12 + .../backend/posts/models/PostPicture.java | 19 +- .../repositories/PostPictureRepository.java | 16 ++ .../posts/repositories/PostRepository.java | 24 +- .../backend/posts/services/PostService.java | 5 +- .../posts/services/impl/PostServiceImpl.java | 51 +++- .../backend/security/SecurityConfig.java | 6 +- .../backend/shared/dtos/PictureDto.java | 17 ++ .../handlers/GlobalExceptionHandler.java | 1 + .../backend/shared/utils/PictureUtils.java | 36 +++ .../backend/users/dtos/BasicUserInfoDto.java | 2 +- .../meowhub/backend/users/models/Picture.java | 75 ------ .../meowhub/backend/users/models/User.java | 3 - .../users/repositories/PictureRepository.java | 7 - .../users/repositories/UserRepository.java | 6 +- .../users/services/PictureService.java | 10 - .../services/impl/PictureServiceImpl.java | 28 --- .../src/main/resources/OCI_Object_Storage.pem | 0 .../resources/application-mock-oci.properties | 0 .../src/main/resources/application.properties | 12 +- backend/src/main/resources/config | 6 + .../meowhub/backend/InitDataTestConfig.java | 20 +- .../backend/posts/PostServiceImplTest.java | 24 +- .../shared/GlobalExceptionHandlerTest.java | 6 +- .../UserRelationQueryServiceImplTest.java | 182 ++++++++------ .../users/UserRepositoryIntegrationTest.java | 2 +- .../resources/application-test.properties | 4 +- backend/target/classes/application.properties | 12 +- .../meowhub/backend/BackendApplication.class | Bin 740 -> 740 bytes .../backend/BackendApplicationTests.class | Bin 538 -> 617 bytes database/scripts/120_create_tables.sql | 83 +++---- .../scripts/400_create_audit_triggers.sql | 15 -- .../src/Features/CreatePost/CreatePost.tsx | 7 +- 43 files changed, 714 insertions(+), 437 deletions(-) create mode 100644 backend/src/main/java/meowhub/backend/ext/oci/MockOCIUploadService.java create mode 100644 backend/src/main/java/meowhub/backend/ext/oci/OCIClientConfiguration.java create mode 100644 backend/src/main/java/meowhub/backend/ext/oci/OCIController.java create mode 100644 backend/src/main/java/meowhub/backend/ext/oci/OCIUploadService.java create mode 100644 backend/src/main/java/meowhub/backend/shared/dtos/PictureDto.java create mode 100644 backend/src/main/java/meowhub/backend/shared/utils/PictureUtils.java delete mode 100644 backend/src/main/java/meowhub/backend/users/models/Picture.java delete mode 100644 backend/src/main/java/meowhub/backend/users/repositories/PictureRepository.java delete mode 100644 backend/src/main/java/meowhub/backend/users/services/PictureService.java delete mode 100644 backend/src/main/java/meowhub/backend/users/services/impl/PictureServiceImpl.java create mode 100644 backend/src/main/resources/OCI_Object_Storage.pem create mode 100644 backend/src/main/resources/application-mock-oci.properties create mode 100644 backend/src/main/resources/config diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 2e337df..6ec74f1 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -25,10 +25,10 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up JDK 21 + - name: Set up JDK 22 uses: actions/setup-java@v3 with: - java-version: '21' + java-version: '22' distribution: 'temurin' cache: maven - name: Build with Maven diff --git a/backend/pom.xml b/backend/pom.xml index 5407cb4..665b350 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -1,93 +1,145 @@ - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 3.2.4 - - - meowhub - backend - 0.0.1-SNAPSHOT - backend - backend - - 21 - - - - com.h2database - h2 - runtime - - - org.springframework.boot - spring-boot-starter-data-jpa - - - com.oracle.database.jdbc - ojdbc8 - 19.8.0.0 - - - org.springframework.boot - spring-boot-starter-security - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.security - spring-security-test - test - - - org.springframework.boot - spring-boot-starter-validation - 3.3.5 - - - org.projectlombok - lombok - provided - - - io.jsonwebtoken - jjwt-api - 0.12.6 - - - io.jsonwebtoken - jjwt-impl - 0.12.6 - - - io.jsonwebtoken - jjwt-jackson - 0.12.6 - - - org.springdoc - springdoc-openapi-starter-webmvc-ui - 2.0.2 - - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.2.4 + + + meowhub + backend + 0.0.1-SNAPSHOT + backend + backend + + 22 + + + + com.h2database + h2 + runtime + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.oracle.database.jdbc + ojdbc8 + 19.8.0.0 + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.security + spring-security-test + test + + + org.springframework.boot + spring-boot-starter-validation + 3.3.5 + + + org.projectlombok + lombok + provided + + + io.jsonwebtoken + jjwt-api + 0.12.6 + + + io.jsonwebtoken + jjwt-impl + 0.12.6 + + + io.jsonwebtoken + jjwt-jackson + 0.12.6 + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + 2.0.2 + - - - - org.springframework.boot - spring-boot-maven-plugin - - - + + com.oracle.oci.sdk + oci-java-sdk-addons-resteasy-client-configurator + + + + com.oracle.oci.sdk + oci-java-sdk-objectstorage + + + + com.oracle.oci.sdk + oci-java-sdk-common-httpclient-jersey + + + + javax.xml.bind + jaxb-api + 2.3.1 + + + org.glassfish.jaxb + jaxb-runtime + 2.3.1 + + + commons-io + commons-io + 2.18.0 + + + + + + + + + com.oracle.oci.sdk + oci-java-sdk-bom + 3.55.1 + pom + import + + + com.oracle.oci.sdk + oci-java-sdk-common-httpclient-jersey + 3.55.1 + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + diff --git a/backend/src/main/java/meowhub/backend/ext/oci/MockOCIUploadService.java b/backend/src/main/java/meowhub/backend/ext/oci/MockOCIUploadService.java new file mode 100644 index 0000000..e739d82 --- /dev/null +++ b/backend/src/main/java/meowhub/backend/ext/oci/MockOCIUploadService.java @@ -0,0 +1,23 @@ +package meowhub.backend.ext.oci; + +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.util.logging.Logger; + +@Service +@Profile("mock-oci") +public class MockOCIUploadService extends OCIUploadService { + Logger logger = Logger.getLogger(getClass().getName()); + + @Override + public void upload(MultipartFile file, String objectName) throws Exception { + logger.info("Mock upload: %s" + objectName); + } + + @Override + public String getFileObjectUrl(String objectName) throws Exception { + return "https://mock-oci-url.com/" + objectName; + } +} diff --git a/backend/src/main/java/meowhub/backend/ext/oci/OCIClientConfiguration.java b/backend/src/main/java/meowhub/backend/ext/oci/OCIClientConfiguration.java new file mode 100644 index 0000000..ed4d941 --- /dev/null +++ b/backend/src/main/java/meowhub/backend/ext/oci/OCIClientConfiguration.java @@ -0,0 +1,26 @@ +package meowhub.backend.ext.oci; + +import com.oracle.bmc.ConfigFileReader; +import com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider; +import com.oracle.bmc.objectstorage.ObjectStorage; +import com.oracle.bmc.objectstorage.ObjectStorageClient; +import org.springframework.context.annotation.Configuration; + +import java.io.IOException; + +@Configuration +public class OCIClientConfiguration { + // Path to OCI configs file + String configurationFilePath = "backend/src/main/resources/config"; + String profile = "DEFAULT"; + + public ObjectStorage getObjectStorage() throws IOException { + //load configs file + final ConfigFileReader.ConfigFile configFile = ConfigFileReader.parse(configurationFilePath, profile); + final ConfigFileAuthenticationDetailsProvider provider = new ConfigFileAuthenticationDetailsProvider(configFile); + + //build and return client + return ObjectStorageClient.builder().build(provider); + } + +} diff --git a/backend/src/main/java/meowhub/backend/ext/oci/OCIController.java b/backend/src/main/java/meowhub/backend/ext/oci/OCIController.java new file mode 100644 index 0000000..5f4a6b5 --- /dev/null +++ b/backend/src/main/java/meowhub/backend/ext/oci/OCIController.java @@ -0,0 +1,42 @@ +package meowhub.backend.ext.oci; + +import jakarta.annotation.security.RolesAllowed; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +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; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +@RestController +@RequestMapping("api/ext") +@Deprecated(since = "only to test connection with OCI Object Storage. Otherwise - don't use!!!", forRemoval = true) +@RolesAllowed("ROLE_ADMIN") +public class OCIController { + private static final String urlPrefix = "https://objectstorage.eu-frankfurt-1.oraclecloud.com"; + + @Autowired + private OCIUploadService ociUploadService; + + @PostMapping(path = "upload") + public ResponseEntity uploadFile(@RequestParam("file") MultipartFile file){ + try { + ociUploadService.upload(file, "TEST/"+file.getOriginalFilename()); + return ResponseEntity.ok().body("Uploaded File : "+file.getOriginalFilename()); + } catch (Exception e) { + return ResponseEntity.internalServerError().body(e.getMessage()); + } + } + + @GetMapping(path = "file/{fileName}") + public ResponseEntity getURl(@PathVariable(value = "fileName") String fileName){ + try { + return ResponseEntity.ok().body(ociUploadService.getFileObjectUrl(fileName)); + } catch (Exception e) { + return ResponseEntity.internalServerError().body(e.getMessage()); + } + } +} diff --git a/backend/src/main/java/meowhub/backend/ext/oci/OCIUploadService.java b/backend/src/main/java/meowhub/backend/ext/oci/OCIUploadService.java new file mode 100644 index 0000000..2ecaa58 --- /dev/null +++ b/backend/src/main/java/meowhub/backend/ext/oci/OCIUploadService.java @@ -0,0 +1,86 @@ +package meowhub.backend.ext.oci; + +import com.oracle.bmc.objectstorage.model.CreatePreauthenticatedRequestDetails; +import com.oracle.bmc.objectstorage.model.PreauthenticatedRequest.BucketListingAction; +import com.oracle.bmc.objectstorage.requests.CreatePreauthenticatedRequestRequest; +import com.oracle.bmc.objectstorage.requests.PutObjectRequest; +import com.oracle.bmc.objectstorage.responses.CreatePreauthenticatedRequestResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.InputStream; +import java.time.OffsetDateTime; +import java.util.Date; +import java.util.UUID; + +@Service +@Primary +public class OCIUploadService { + private static final String urlPrefix = "https://objectstorage.eu-frankfurt-1.oraclecloud.com"; + + @Autowired + private OCIClientConfiguration configuration; + + @Value("${oci.objectstorage.bucket-name}") + private String bucketName; + + @Value("${oci.objectstorage.namespaceName}") + private String namespaceName; + + + public void upload(MultipartFile file, String objectName) throws Exception { + InputStream inputStream = file.getInputStream(); + + //build upload request + PutObjectRequest putObjectRequest = + PutObjectRequest.builder() + .namespaceName(namespaceName) + .bucketName(bucketName) + .objectName(objectName) + .contentLength(file.getSize()) + .putObjectBody(inputStream) + .build(); + + //upload the file + try { + configuration.getObjectStorage().putObject(putObjectRequest); + } catch (Exception e) { + e.printStackTrace(); + throw e; + }finally{ + configuration.getObjectStorage().close(); + } + } + + public String getFileObjectUrl(String objectName) throws Exception{ + OffsetDateTime expirationTime = OffsetDateTime.now().plusYears(10); + + // Build request details + CreatePreauthenticatedRequestDetails createPreauthenticatedRequestDetails = CreatePreauthenticatedRequestDetails.builder() + .name("OCI_Request") + .bucketListingAction(BucketListingAction.Deny) + .objectName(objectName) + //readonly access + .accessType(CreatePreauthenticatedRequestDetails.AccessType.ObjectRead) + //here we set expiration time for + .timeExpires(Date.from(expirationTime.toInstant())).build(); + + //Build request + CreatePreauthenticatedRequestRequest createPreauthenticatedRequestRequest = CreatePreauthenticatedRequestRequest.builder() + .namespaceName(namespaceName) + .bucketName(bucketName) + .createPreauthenticatedRequestDetails(createPreauthenticatedRequestDetails) + .opcClientRequestId(UUID.randomUUID().toString()).build(); + + // send request to oci + CreatePreauthenticatedRequestResponse response = configuration.getObjectStorage().createPreauthenticatedRequest(createPreauthenticatedRequestRequest); + configuration.getObjectStorage().close(); + + String accessUri = response.getPreauthenticatedRequest().getAccessUri(); + + return urlPrefix + accessUri; + } +} \ No newline at end of file diff --git a/backend/src/main/java/meowhub/backend/jpa_buddy/Group.java b/backend/src/main/java/meowhub/backend/jpa_buddy/Group.java index 866d4ae..352eba6 100644 --- a/backend/src/main/java/meowhub/backend/jpa_buddy/Group.java +++ b/backend/src/main/java/meowhub/backend/jpa_buddy/Group.java @@ -2,12 +2,9 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; import jakarta.persistence.Table; import jakarta.persistence.UniqueConstraint; @@ -15,9 +12,6 @@ import jakarta.validation.constraints.Size; import lombok.Getter; import lombok.Setter; -import meowhub.backend.users.models.Picture; -import org.hibernate.annotations.OnDelete; -import org.hibernate.annotations.OnDeleteAction; import java.time.LocalDateTime; import java.util.LinkedHashSet; @@ -46,12 +40,6 @@ public class Group { @Column(name = "DESCRIPTION", nullable = false, length = 200) private String description; - @NotNull - @ManyToOne(fetch = FetchType.LAZY, optional = false) - @OnDelete(action = OnDeleteAction.RESTRICT) - @JoinColumn(name = "PICTURE_ID", nullable = false) - private Picture picture; - @Column(name = "CREATED_AT") private LocalDateTime createdAt; diff --git a/backend/src/main/java/meowhub/backend/jpa_buddy/MatchingProfilePicture.java b/backend/src/main/java/meowhub/backend/jpa_buddy/MatchingProfilePicture.java index 5251cbe..2b38888 100644 --- a/backend/src/main/java/meowhub/backend/jpa_buddy/MatchingProfilePicture.java +++ b/backend/src/main/java/meowhub/backend/jpa_buddy/MatchingProfilePicture.java @@ -9,12 +9,10 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; -import jakarta.persistence.UniqueConstraint; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import lombok.Getter; import lombok.Setter; -import meowhub.backend.users.models.Picture; import org.hibernate.annotations.OnDelete; import org.hibernate.annotations.OnDeleteAction; @@ -23,9 +21,7 @@ @Getter @Setter @Entity -@Table(name = "MATCHING_PROFILE_PICTURES", schema = "mh_matching", uniqueConstraints = { - @UniqueConstraint(name = "MATCHING_PROFILE_PICTURES_UQ", columnNames = {"MATCHING_PROFILE_ID", "PICTURE_ID"}) -}) +@Table(name = "MATCHING_PROFILE_PICTURES", schema = "mh_matching") public class MatchingProfilePicture { @Id @Size(max = 36) @@ -40,10 +36,12 @@ public class MatchingProfilePicture { private MatchingProfile matchingProfile; @NotNull - @ManyToOne(fetch = FetchType.LAZY, optional = false) - @OnDelete(action = OnDeleteAction.RESTRICT) - @JoinColumn(name = "PICTURE_ID", nullable = false) - private Picture picture; + @Column(name = "OCI_NAME", nullable = false, length = 100) + private String ociName; + + @NotNull + @Column(name = "OCI_URL", nullable = false, length = 2000) + private String ociUrl; @NotNull @Column(name = "PICTURE_INDEX", nullable = false) @@ -62,5 +60,4 @@ public class MatchingProfilePicture { @Size(max = 36) @Column(name = "MODIFIED_BY", length = 36) private String modifiedBy; - } \ No newline at end of file diff --git a/backend/src/main/java/meowhub/backend/jpa_buddy/ProfilePicture.java b/backend/src/main/java/meowhub/backend/jpa_buddy/ProfilePicture.java index dab8340..5fccec7 100644 --- a/backend/src/main/java/meowhub/backend/jpa_buddy/ProfilePicture.java +++ b/backend/src/main/java/meowhub/backend/jpa_buddy/ProfilePicture.java @@ -13,7 +13,6 @@ import jakarta.validation.constraints.Size; import lombok.Getter; import lombok.Setter; -import meowhub.backend.users.models.Picture; import org.hibernate.annotations.OnDelete; import org.hibernate.annotations.OnDeleteAction; @@ -37,10 +36,12 @@ public class ProfilePicture { private Profile profile; @NotNull - @ManyToOne(fetch = FetchType.LAZY, optional = false) - @OnDelete(action = OnDeleteAction.RESTRICT) - @JoinColumn(name = "PICTURE_ID", nullable = false) - private Picture picture; + @Column(name = "OCI_NAME", nullable = false, length = 100) + private String ociName; + + @NotNull + @Column(name = "OCI_URL", nullable = false, length = 2000) + private String ociUrl; @NotNull @Column(name = "PICTURE_INDEX", nullable = false) diff --git a/backend/src/main/java/meowhub/backend/posts/controllers/PostController.java b/backend/src/main/java/meowhub/backend/posts/controllers/PostController.java index c0fb338..81fc607 100644 --- a/backend/src/main/java/meowhub/backend/posts/controllers/PostController.java +++ b/backend/src/main/java/meowhub/backend/posts/controllers/PostController.java @@ -4,6 +4,7 @@ import meowhub.backend.posts.dtos.PostDto; import meowhub.backend.posts.services.PostService; import org.springframework.data.domain.Page; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.userdetails.UserDetails; @@ -14,7 +15,11 @@ import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; @RestController @RequestMapping("api/posts") @@ -36,9 +41,9 @@ public ResponseEntity> getPostsForUser(@PathVariable("login") Stri } - @PostMapping("") - public ResponseEntity createPost(@RequestParam String content, @AuthenticationPrincipal UserDetails userDetails) { - PostDto postDto = postService.createPost(userDetails.getUsername(), content); + @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public ResponseEntity createPost(@RequestPart("content") String content, @RequestPart(value = "pictures", required = false) List pictures, @AuthenticationPrincipal UserDetails userDetails) { + PostDto postDto = postService.createPost(userDetails.getUsername(), content, pictures); return ResponseEntity.ok(postDto); } diff --git a/backend/src/main/java/meowhub/backend/posts/dtos/PostDto.java b/backend/src/main/java/meowhub/backend/posts/dtos/PostDto.java index ad6332f..827d5a9 100644 --- a/backend/src/main/java/meowhub/backend/posts/dtos/PostDto.java +++ b/backend/src/main/java/meowhub/backend/posts/dtos/PostDto.java @@ -4,9 +4,11 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import meowhub.backend.shared.dtos.PictureDto; import meowhub.backend.users.dtos.BasicUserInfoDto; import java.time.LocalDateTime; +import java.util.List; @Builder @Data @@ -18,4 +20,14 @@ public class PostDto { private BasicUserInfoDto author; private Long numberOfComments; private LocalDateTime createdAt; + private List pictures; + + //used for postRepository "find" queries + public PostDto(String id, String content, BasicUserInfoDto author, Long numberOfComments, LocalDateTime createdAt) { + this.id = id; + this.content = content; + this.author = author; + this.numberOfComments = numberOfComments; + this.createdAt = createdAt; + } } diff --git a/backend/src/main/java/meowhub/backend/posts/models/PostPicture.java b/backend/src/main/java/meowhub/backend/posts/models/PostPicture.java index d609898..38466f8 100644 --- a/backend/src/main/java/meowhub/backend/posts/models/PostPicture.java +++ b/backend/src/main/java/meowhub/backend/posts/models/PostPicture.java @@ -12,8 +12,8 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; -import meowhub.backend.users.models.Picture; import org.hibernate.annotations.OnDelete; import org.hibernate.annotations.OnDeleteAction; @@ -23,6 +23,7 @@ @Setter @Entity @Table(name = "POST_PICTURES", schema = "mh_posts") +@NoArgsConstructor public class PostPicture { @Id @Size(max = 36) @@ -37,10 +38,12 @@ public class PostPicture { private Post post; @NotNull - @ManyToOne(fetch = FetchType.LAZY, optional = false) - @OnDelete(action = OnDeleteAction.RESTRICT) - @JoinColumn(name = "PICTURE_ID", nullable = false) - private Picture picture; + @Column(name = "OCI_NAME", nullable = false, length = 100) + private String ociName; + + @NotNull + @Column(name = "OCI_URL", nullable = false, length = 2000) + private String ociUrl; @NotNull @Column(name = "PICTURE_INDEX", nullable = false) @@ -60,4 +63,10 @@ public class PostPicture { @Column(name = "MODIFIED_BY", length = 36) private String modifiedBy; + public PostPicture(Post post, String ociName, String ociUrl, Long index) { + this.post = post; + this.ociName = ociName; + this.ociUrl = ociUrl; + this.index = index; + } } \ No newline at end of file diff --git a/backend/src/main/java/meowhub/backend/posts/repositories/PostPictureRepository.java b/backend/src/main/java/meowhub/backend/posts/repositories/PostPictureRepository.java index e36d76d..6b56b74 100644 --- a/backend/src/main/java/meowhub/backend/posts/repositories/PostPictureRepository.java +++ b/backend/src/main/java/meowhub/backend/posts/repositories/PostPictureRepository.java @@ -1,7 +1,23 @@ package meowhub.backend.posts.repositories; import meowhub.backend.posts.models.PostPicture; +import meowhub.backend.shared.dtos.PictureDto; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; public interface PostPictureRepository extends JpaRepository { + @Query(""" + SELECT new meowhub.backend.shared.dtos.PictureDto ( + pp.id, + pp.ociUrl, + pp.index, + pp.createdAt + ) + FROM PostPicture pp + WHERE pp.post.id = :postId + ORDER BY pp.index + """) + List findPicturesForPostId(String postId); } \ No newline at end of file diff --git a/backend/src/main/java/meowhub/backend/posts/repositories/PostRepository.java b/backend/src/main/java/meowhub/backend/posts/repositories/PostRepository.java index e9e32b6..4f8d010 100644 --- a/backend/src/main/java/meowhub/backend/posts/repositories/PostRepository.java +++ b/backend/src/main/java/meowhub/backend/posts/repositories/PostRepository.java @@ -2,6 +2,7 @@ import meowhub.backend.posts.dtos.PostDto; import meowhub.backend.posts.models.Post; +import meowhub.backend.shared.dtos.PictureDto; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; @@ -9,6 +10,7 @@ import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import java.util.List; import java.util.Optional; @Repository @@ -24,18 +26,18 @@ public interface PostRepository extends JpaRepository { u.name, u.surname, u.login, - pictures.picture + pp.ociUrl ), (SELECT COUNT(c.id) FROM Comment c WHERE c.post.id = p.id), p.createdAt ) FROM User u JOIN u.posts p - LEFT JOIN Picture pictures ON pictures.user.id = u.id - LEFT JOIN ProfilePicture pp ON pp.picture.id = pictures.id + LEFT JOIN Profile profile ON profile.user.id = u.id + LEFT JOIN ProfilePicture pp ON pp.profile.id = profile.id LEFT JOIN u.postsPrivacy postsPrivacy WHERE postsPrivacy.code = 'PUBLIC' - OR (postsPrivacy.code = 'FRIENDS_ONLY' AND u.id IN (u.id, + OR (postsPrivacy.code = 'FRIENDS_ONLY' AND u.id IN (u.id, (SELECT r.receiver.id FROM User sender JOIN sender.userRelationsSender r @@ -62,18 +64,18 @@ public interface PostRepository extends JpaRepository { u.name, u.surname, u.login, - pictures.picture + pp.ociUrl ), (SELECT COUNT(c.id) FROM Comment c WHERE c.post.id = p.id), p.createdAt ) FROM User u JOIN u.posts p - LEFT JOIN Picture pictures ON pictures.user.id = u.id - LEFT JOIN ProfilePicture pp ON pp.picture.id = pictures.id + LEFT JOIN Profile profile ON profile.user.id = u.id + LEFT JOIN ProfilePicture pp ON pp.profile.id = profile.id LEFT JOIN u.postsPrivacy postsPrivacy WHERE u.login = :login - AND (postsPrivacy.code = 'PUBLIC' + AND (postsPrivacy.code = 'PUBLIC' OR (postsPrivacy.code = 'FRIENDS_ONLY' AND u.id IN ( SELECT r.receiver.id FROM User sender @@ -101,15 +103,15 @@ public interface PostRepository extends JpaRepository { u.name, u.surname, u.login, - pictures.picture + pp.ociUrl ), (SELECT COUNT(c.id) FROM Comment c WHERE c.post.id = p.id), p.createdAt ) FROM User u JOIN u.posts p - LEFT JOIN Picture pictures ON pictures.user.id = u.id - LEFT JOIN ProfilePicture pp ON pp.picture.id = pictures.id + LEFT JOIN Profile profile ON profile.user.id = u.id + LEFT JOIN ProfilePicture pp ON pp.profile.id = profile.id LEFT JOIN u.postsPrivacy postsPrivacy WHERE u.login = :login ORDER BY p.createdAt DESC diff --git a/backend/src/main/java/meowhub/backend/posts/services/PostService.java b/backend/src/main/java/meowhub/backend/posts/services/PostService.java index 20436f6..d6f658c 100644 --- a/backend/src/main/java/meowhub/backend/posts/services/PostService.java +++ b/backend/src/main/java/meowhub/backend/posts/services/PostService.java @@ -2,13 +2,16 @@ import meowhub.backend.posts.dtos.PostDto; import org.springframework.data.domain.Page; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; public interface PostService { Page getPosts(String requestedBy, int pageNo, int pageSize); Page getPostsForUser(String login, String requestedBy, int pageNo, int pageSize); - PostDto createPost(String login, String content); + PostDto createPost(String login, String content, List pictures); PostDto updatePost(String login, String postId, String content); diff --git a/backend/src/main/java/meowhub/backend/posts/services/impl/PostServiceImpl.java b/backend/src/main/java/meowhub/backend/posts/services/impl/PostServiceImpl.java index f72d1cd..474efd3 100644 --- a/backend/src/main/java/meowhub/backend/posts/services/impl/PostServiceImpl.java +++ b/backend/src/main/java/meowhub/backend/posts/services/impl/PostServiceImpl.java @@ -1,10 +1,15 @@ package meowhub.backend.posts.services.impl; +import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import meowhub.backend.posts.dtos.PostDto; import meowhub.backend.posts.models.Post; +import meowhub.backend.posts.models.PostPicture; +import meowhub.backend.posts.repositories.PostPictureRepository; import meowhub.backend.posts.services.PostService; import meowhub.backend.shared.constants.AlertConstants; +import meowhub.backend.shared.dtos.PictureDto; +import meowhub.backend.shared.utils.PictureUtils; import meowhub.backend.users.dtos.BasicUserInfoDto; import meowhub.backend.users.facades.UserPostServiceFacade; import meowhub.backend.users.models.User; @@ -12,21 +17,29 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.util.Pair; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; import org.webjars.NotFoundException; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; @Service @RequiredArgsConstructor public class PostServiceImpl implements PostService { private final UserPostServiceFacade userPostServiceFacade; private final PostRepository postRepository; + private final PictureUtils pictureUtils; + private final PostPictureRepository postPictureRepository; @Override public Page getPosts(String requestedBy, int pageNo, int pageSize) { Pageable pageable = PageRequest.of(pageNo, pageSize); - return postRepository.findIfPublicOrFriends(requestedBy, pageable); + Page posts = postRepository.findIfPublicOrFriends(requestedBy, pageable); + posts.forEach(this::addPictures); + return posts; } @Override @@ -34,15 +47,20 @@ public Page getPostsForUser(String login, String requestedBy, int pageN Pageable pageable = PageRequest.of(pageNo, pageSize); userPostServiceFacade.validateIfUserExists(login); + Page posts; if (login.equals(requestedBy)) { - return postRepository.findOwn(login, pageable); + posts = postRepository.findOwn(login, pageable); } else { - return postRepository.findByUserLoginIfPublicOrFriend(login, requestedBy, pageable); + posts = postRepository.findByUserLoginIfPublicOrFriend(login, requestedBy, pageable); } + addPicturesToPage(posts); + return posts; + } @Override - public PostDto createPost(String login, String content) { + @Transactional + public PostDto createPost(String login, String content, List pictures) { User postOwner = userPostServiceFacade.findUserByLogin(login); Post post = new Post(); post.setContentHtml(content); @@ -50,6 +68,19 @@ public PostDto createPost(String login, String content) { post.setCreatedAt(LocalDateTime.now()); post = postRepository.save(post); + if(pictures == null || pictures.isEmpty() || pictures.getFirst().getContentType() == null) { + return convertToPostDto(post); + } + + List postPictures = new ArrayList<>(); + for (int i = 0; i < pictures.size(); i++) { + MultipartFile picture = pictures.get(i); + Pair pictureInfo = pictureUtils.uploadPictureToOCIAndGetAuthorizedUrlToAccessIt(picture, postOwner.getLogin()); + Long pictureIndex = (long) i; + postPictures.add(new PostPicture(post, pictureInfo.getFirst(), pictureInfo.getSecond(), pictureIndex)); + } + + postPictureRepository.saveAll(postPictures); return convertToPostDto(post); } @@ -72,17 +103,29 @@ private Post findUserPost(String login, String postId) { .orElseThrow(() -> new NotFoundException(String.format(AlertConstants.RESOURCE_NOT_FOUND, "post", "id", postId))); } + private Page addPicturesToPage (Page postsDto){ + postsDto.forEach(this::addPictures); + return null; + } + + private void addPictures (PostDto postsDto){ + List pictures = postPictureRepository.findPicturesForPostId(postsDto.getId()); + postsDto.setPictures(pictures); + } + private PostDto convertToPostDto(Post post) { if (post == null) { return null; } BasicUserInfoDto author = userPostServiceFacade.getBasicUserInfo(post.getUser().getLogin()); + List pictures = postPictureRepository.findPicturesForPostId(post.getId()); return PostDto.builder() .id(post.getId()) .content(post.getContentHtml()) .author(author) + .pictures(pictures) .createdAt(post.getCreatedAt()) .build(); } diff --git a/backend/src/main/java/meowhub/backend/security/SecurityConfig.java b/backend/src/main/java/meowhub/backend/security/SecurityConfig.java index 3779c13..cac0e66 100644 --- a/backend/src/main/java/meowhub/backend/security/SecurityConfig.java +++ b/backend/src/main/java/meowhub/backend/security/SecurityConfig.java @@ -28,19 +28,21 @@ public class SecurityConfig { @Bean SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, AuthEntryPointJwt unauthorizedHandler, AuthTokenFilter authenticationJwtTokenFilter) throws Exception { http.csrf(csrf -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) - .ignoringRequestMatchers("/api/auth/public/**") + .ignoringRequestMatchers("/api/auth/public/**", "/api/ext/**") ); http.authorizeHttpRequests(requests -> requests .requestMatchers("/api/csrf-token/**").permitAll() .requestMatchers("/api/auth/public/**").permitAll() + .requestMatchers("/api/ext/**").permitAll() .requestMatchers("/api/admin/**").hasRole("ADMIN") .requestMatchers( "/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html" ).permitAll() - .anyRequest().authenticated() + .anyRequest().permitAll() +// .anyRequest().authenticated() ); http.addFilterBefore(authenticationJwtTokenFilter, UsernamePasswordAuthenticationFilter.class); diff --git a/backend/src/main/java/meowhub/backend/shared/dtos/PictureDto.java b/backend/src/main/java/meowhub/backend/shared/dtos/PictureDto.java new file mode 100644 index 0000000..329831f --- /dev/null +++ b/backend/src/main/java/meowhub/backend/shared/dtos/PictureDto.java @@ -0,0 +1,17 @@ +package meowhub.backend.shared.dtos; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Setter +@Getter +@AllArgsConstructor +public class PictureDto { + private String id; + private String url; + private Long index; + private LocalDateTime createdAt; +} diff --git a/backend/src/main/java/meowhub/backend/shared/handlers/GlobalExceptionHandler.java b/backend/src/main/java/meowhub/backend/shared/handlers/GlobalExceptionHandler.java index a78ac77..18bea03 100644 --- a/backend/src/main/java/meowhub/backend/shared/handlers/GlobalExceptionHandler.java +++ b/backend/src/main/java/meowhub/backend/shared/handlers/GlobalExceptionHandler.java @@ -52,6 +52,7 @@ public ResponseEntity handleBadCredentialsException() { @ExceptionHandler(IllegalArgumentException.class) public ResponseEntity handleIllegalArgumentException(IllegalArgumentException ex) { + ex.printStackTrace(); return new ResponseEntity<>(AlertUtils.illegalArgumentException(ex.getMessage()), HttpStatus.NOT_ACCEPTABLE); } diff --git a/backend/src/main/java/meowhub/backend/shared/utils/PictureUtils.java b/backend/src/main/java/meowhub/backend/shared/utils/PictureUtils.java new file mode 100644 index 0000000..c4d728f --- /dev/null +++ b/backend/src/main/java/meowhub/backend/shared/utils/PictureUtils.java @@ -0,0 +1,36 @@ +package meowhub.backend.shared.utils; + +import lombok.RequiredArgsConstructor; +import meowhub.backend.ext.oci.OCIUploadService; +import org.springframework.data.util.Pair; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import org.apache.commons.io.FilenameUtils; + +import java.util.UUID; + +@Service +@RequiredArgsConstructor +public class PictureUtils { + private final OCIUploadService ociUploadService; + + /** + * Uploads picture to OCI and returns the name of the file and the authorized URL to access it + * @param file file to upload + * @param login login of the user + * @return Pair with the name of the file and the authorized URL to access it + */ + public Pair uploadPictureToOCIAndGetAuthorizedUrlToAccessIt(MultipartFile file, String login){ + try { + Pair nameAndUrl; + String newFileName = login + "/" + UUID.randomUUID() + "." + FilenameUtils.getExtension(file.getOriginalFilename()); + ociUploadService.upload(file, newFileName); + + String url = ociUploadService.getFileObjectUrl(newFileName); + nameAndUrl = Pair.of(newFileName, url); + return nameAndUrl; + } catch (Exception e) { + throw new RuntimeException("Error while uploading picture to OCI"); + } + } +} diff --git a/backend/src/main/java/meowhub/backend/users/dtos/BasicUserInfoDto.java b/backend/src/main/java/meowhub/backend/users/dtos/BasicUserInfoDto.java index 52b7197..560a4ed 100644 --- a/backend/src/main/java/meowhub/backend/users/dtos/BasicUserInfoDto.java +++ b/backend/src/main/java/meowhub/backend/users/dtos/BasicUserInfoDto.java @@ -14,5 +14,5 @@ public class BasicUserInfoDto { private String name; private String surname; private String login; - private byte[] profilePicture; //TODO: add picture functionality + private String profilePictureUrl; } diff --git a/backend/src/main/java/meowhub/backend/users/models/Picture.java b/backend/src/main/java/meowhub/backend/users/models/Picture.java deleted file mode 100644 index 2a4e826..0000000 --- a/backend/src/main/java/meowhub/backend/users/models/Picture.java +++ /dev/null @@ -1,75 +0,0 @@ -package meowhub.backend.users.models; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToMany; -import jakarta.persistence.Table; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; -import lombok.Getter; -import lombok.Setter; -import meowhub.backend.jpa_buddy.Group; -import meowhub.backend.jpa_buddy.MatchingProfilePicture; -import meowhub.backend.posts.models.PostPicture; -import meowhub.backend.jpa_buddy.ProfilePicture; -import org.hibernate.annotations.OnDelete; -import org.hibernate.annotations.OnDeleteAction; - -import java.time.LocalDateTime; -import java.util.LinkedHashSet; -import java.util.Set; - -@Getter -@Setter -@Entity -@Table(name = "PICTURES", schema = "mh_users") -public class Picture { - @Id - @Size(max = 36) - @GeneratedValue(strategy = GenerationType.UUID) - @Column(name = "ID", nullable = false, length = 36) - private String id; - - @NotNull - @ManyToOne(fetch = FetchType.LAZY, optional = false) - @OnDelete(action = OnDeleteAction.RESTRICT) - @JoinColumn(name = "USER_ID", nullable = false) - private User user; - - @NotNull - @Column(name = "PICTURE", nullable = false) - private byte[] picture; - - @Column(name = "CREATED_AT") - private LocalDateTime createdAt; - - @Size(max = 36) - @Column(name = "CREATED_BY", length = 36) - private String createdBy; - - @Column(name = "MODIFIED_AT") - private LocalDateTime modifiedAt; - - @Size(max = 36) - @Column(name = "MODIFIED_BY", length = 36) - private String modifiedBy; - - @OneToMany(mappedBy = "picture") - private Set groups = new LinkedHashSet<>(); - - @OneToMany(mappedBy = "picture") - private Set matchingProfilePictures = new LinkedHashSet<>(); - - @OneToMany(mappedBy = "picture") - private Set postPictures = new LinkedHashSet<>(); - - @OneToMany(mappedBy = "picture") - private Set profilePictures = new LinkedHashSet<>(); - -} \ No newline at end of file diff --git a/backend/src/main/java/meowhub/backend/users/models/User.java b/backend/src/main/java/meowhub/backend/users/models/User.java index 931a48a..e9b7625 100644 --- a/backend/src/main/java/meowhub/backend/users/models/User.java +++ b/backend/src/main/java/meowhub/backend/users/models/User.java @@ -156,9 +156,6 @@ public class User { @OneToMany(mappedBy = "user") private final Set matchingProfiles = new LinkedHashSet<>(); - @OneToMany(mappedBy = "user") - private final Set pictures = new LinkedHashSet<>(); - @OneToMany(mappedBy = "user") private final Set posts = new LinkedHashSet<>(); diff --git a/backend/src/main/java/meowhub/backend/users/repositories/PictureRepository.java b/backend/src/main/java/meowhub/backend/users/repositories/PictureRepository.java deleted file mode 100644 index 9fc99d2..0000000 --- a/backend/src/main/java/meowhub/backend/users/repositories/PictureRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package meowhub.backend.users.repositories; - -import meowhub.backend.users.models.Picture; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface PictureRepository extends JpaRepository { -} \ No newline at end of file diff --git a/backend/src/main/java/meowhub/backend/users/repositories/UserRepository.java b/backend/src/main/java/meowhub/backend/users/repositories/UserRepository.java index 56632ea..3a63a6f 100644 --- a/backend/src/main/java/meowhub/backend/users/repositories/UserRepository.java +++ b/backend/src/main/java/meowhub/backend/users/repositories/UserRepository.java @@ -25,11 +25,11 @@ public interface UserRepository extends JpaRepository { u.name, u.surname, u.login, - p.picture + pp.ociUrl ) FROM User u - LEFT JOIN Picture p ON p.user.id = u.id - LEFT JOIN ProfilePicture pp ON pp.picture.id = p.id + LEFT JOIN Profile p ON p.user.id = u.id + LEFT JOIN ProfilePicture pp ON pp.profile.id = p.id WHERE u.login = :login ORDER BY pp.id FETCH FIRST 1 ROWS ONLY diff --git a/backend/src/main/java/meowhub/backend/users/services/PictureService.java b/backend/src/main/java/meowhub/backend/users/services/PictureService.java deleted file mode 100644 index d9c2133..0000000 --- a/backend/src/main/java/meowhub/backend/users/services/PictureService.java +++ /dev/null @@ -1,10 +0,0 @@ -package meowhub.backend.users.services; - -import meowhub.backend.users.models.Picture; -import org.springframework.web.multipart.MultipartFile; - -public interface PictureService { - String getPictureBase64(byte[] pictureBytes); - - Picture getImageFromMultipartFile(MultipartFile file); -} diff --git a/backend/src/main/java/meowhub/backend/users/services/impl/PictureServiceImpl.java b/backend/src/main/java/meowhub/backend/users/services/impl/PictureServiceImpl.java deleted file mode 100644 index 8355c7a..0000000 --- a/backend/src/main/java/meowhub/backend/users/services/impl/PictureServiceImpl.java +++ /dev/null @@ -1,28 +0,0 @@ -package meowhub.backend.users.services.impl; - -import meowhub.backend.users.models.Picture; -import meowhub.backend.users.services.PictureService; -import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; - -import java.io.IOException; -import java.util.Base64; - -@Service -public class PictureServiceImpl implements PictureService { - - public String getPictureBase64(byte[] pictureBytes) { - return Base64.getEncoder().encodeToString(pictureBytes); - } - - public Picture getImageFromMultipartFile(MultipartFile file) { - try { - byte[] imageBytes = file.getBytes(); - Picture picture = new Picture(); - picture.setPicture(imageBytes); - return picture; - } catch (IOException e) { - throw new RuntimeException("Error while reading image from file", e); - } - } -} diff --git a/backend/src/main/resources/OCI_Object_Storage.pem b/backend/src/main/resources/OCI_Object_Storage.pem new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/main/resources/application-mock-oci.properties b/backend/src/main/resources/application-mock-oci.properties new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 5c54e9a..094291a 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -7,4 +7,14 @@ spring.datasource.password=${DB_PASSWORD} spring.jpa.hibernate.ddl-auto=none spring.app.jwtSecret=${JWT_SECRET} -spring.app.jwtExpirationMs=${JWT_EXPIRATION_MS} \ No newline at end of file +spring.app.jwtExpirationMs=${JWT_EXPIRATION_MS} + +### Object Storage properties. +oci.objectstorage.bucket-name=MeowHub_OS +oci.objectstorage.namespaceName=frcpde7etgiu + +oci.objectstorage.config-file-path=./src/main/resources/config +oci.objectstorage.profile=DEFAULT + +spring.servlet.multipart.max-file-size=10MB +spring.servlet.multipart.max-request-size=100MB \ No newline at end of file diff --git a/backend/src/main/resources/config b/backend/src/main/resources/config new file mode 100644 index 0000000..e662081 --- /dev/null +++ b/backend/src/main/resources/config @@ -0,0 +1,6 @@ +[DEFAULT] +user= +fingerprint= +tenancy= +region= +key_file=backend/src/main/resources/OCI_Object_Storage.pem \ No newline at end of file diff --git a/backend/src/test/java/meowhub/backend/InitDataTestConfig.java b/backend/src/test/java/meowhub/backend/InitDataTestConfig.java index 3f05f42..f2c5683 100644 --- a/backend/src/test/java/meowhub/backend/InitDataTestConfig.java +++ b/backend/src/test/java/meowhub/backend/InitDataTestConfig.java @@ -5,7 +5,6 @@ import meowhub.backend.constants.PrivacySettings; import meowhub.backend.constants.Roles; import meowhub.backend.posts.models.Comment; -import meowhub.backend.users.models.Picture; import meowhub.backend.posts.models.Post; import meowhub.backend.posts.models.PostPicture; import meowhub.backend.jpa_buddy.Profile; @@ -27,7 +26,6 @@ import meowhub.backend.users.models.Role; import meowhub.backend.users.models.User; import meowhub.backend.users.repositories.GenderRepository; -import meowhub.backend.users.repositories.PictureRepository; import meowhub.backend.posts.repositories.PostPictureRepository; import meowhub.backend.users.repositories.PrivacySettingRepository; import meowhub.backend.users.repositories.RoleRepository; @@ -49,8 +47,6 @@ public class InitDataTestConfig { @Autowired private UserRepository userRepository; @Autowired - private PictureRepository pictureRepository; - @Autowired private PostRepository postRepository; @Autowired private PostPictureRepository postPictureRepository; @@ -140,11 +136,6 @@ private void initUser() { } private void initPosts() { - Picture picture = new Picture(); - picture.setUser(user3); - picture.setPicture("9ffdc87faedbe9a1ee2d1993eabafad12fca90c8cfeadffee99166defe4e2ab5bb4b483dccd2ba5b4fb8ea51a4238e0f7cb3ea3dea7b1d06013fc99cc325b604da6b469ffa6baba27e7ad69a9251c5c6102acd30fdbb4e2dbd78992593487b27777ce".getBytes()); - picture = pictureRepository.save(picture); - Post post1 = new Post(); post1.setUser(user1); post1.setContentHtml("Hello world 1"); @@ -161,8 +152,9 @@ private void initPosts() { post3 = postRepository.save(post3); PostPicture postPicture = new PostPicture(); - postPicture.setPicture(picture); postPicture.setPost(post3); + postPicture.setOciName("test.jpg"); + postPicture.setOciUrl("https://example.com/test.jpg"); postPicture.setIndex(0L); postPictureRepository.save(postPicture); @@ -181,11 +173,6 @@ private void initPosts() { } private void initProfiles() { - Picture picture = new Picture(); - picture.setUser(user1); - picture.setPicture("1ffdc87faedbe9a1ee2d1993eabafad12fca90c8cfeadffee99166defe4e2ab5bb4b483dccd2ba5b4fb8ea51a4238e0f7cb3ea3dea7b1d06013fc99cc325b604da6b469ffa6baba27e7ad69a9251c5c6102acd30fdbb4e2dbd78992593487b27777ce".getBytes()); - pictureRepository.save(picture); - Profile profile = new Profile(); profile.setUser(user1); profile.setProfileDetailsHtml("Hello this is my profile"); @@ -193,7 +180,8 @@ private void initProfiles() { ProfilePicture profilePicture = new ProfilePicture(); profilePicture.setProfile(profile); - profilePicture.setPicture(picture); + profilePicture.setOciName("test.jpg"); + profilePicture.setOciUrl("https://example.com/test.jpg"); profilePicture.setIndex(0L); profilePictureRepository.save(profilePicture); diff --git a/backend/src/test/java/meowhub/backend/posts/PostServiceImplTest.java b/backend/src/test/java/meowhub/backend/posts/PostServiceImplTest.java index 7119247..3e3e9e5 100644 --- a/backend/src/test/java/meowhub/backend/posts/PostServiceImplTest.java +++ b/backend/src/test/java/meowhub/backend/posts/PostServiceImplTest.java @@ -2,17 +2,17 @@ import meowhub.backend.posts.dtos.PostDto; import meowhub.backend.posts.models.Post; +import meowhub.backend.posts.repositories.PostPictureRepository; import meowhub.backend.posts.repositories.PostRepository; import meowhub.backend.posts.services.impl.PostServiceImpl; +import meowhub.backend.shared.utils.PictureUtils; import meowhub.backend.users.dtos.BasicUserInfoDto; import meowhub.backend.users.facades.UserPostServiceFacade; import meowhub.backend.users.models.User; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; +import org.mockito.MockitoAnnotations; import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; @@ -29,18 +29,22 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -@ExtendWith(MockitoExtension.class) class PostServiceImplTest { @Mock private PostRepository postRepository; - @InjectMocks - private PostServiceImpl postService; + @Mock + private PostPictureRepository postPictureRepository; @Mock private UserPostServiceFacade userPostServiceFacade; + @Mock + private PictureUtils pictureUtils; + + private PostServiceImpl postService; + private User user; private PostDto postDto; private Post post; @@ -48,6 +52,10 @@ class PostServiceImplTest { @BeforeEach void setUp() { + // Ręczne inicjalizowanie mocków + MockitoAnnotations.initMocks(this); + postService = new PostServiceImpl(userPostServiceFacade, postRepository, pictureUtils, postPictureRepository); + // Setup mock user and post user = new User(); user.setId("user-id"); @@ -109,7 +117,7 @@ void testCreatePost() { when(postRepository.save(any(Post.class))).thenReturn(post); // When - PostDto result = postService.createPost("john_doe", "New content"); + PostDto result = postService.createPost("john_doe", "content1", null); // Then assertNotNull(result); @@ -153,7 +161,7 @@ void testCreatePost_UserNotFound() { // When assertThrows(UsernameNotFoundException.class, - () -> postService.createPost(unknownUser, "Sample content")); + () -> postService.createPost(unknownUser, "content2", null)); verify(userPostServiceFacade, times(1)).findUserByLogin(unknownUser); } diff --git a/backend/src/test/java/meowhub/backend/shared/GlobalExceptionHandlerTest.java b/backend/src/test/java/meowhub/backend/shared/GlobalExceptionHandlerTest.java index 49c26a8..e2d817b 100644 --- a/backend/src/test/java/meowhub/backend/shared/GlobalExceptionHandlerTest.java +++ b/backend/src/test/java/meowhub/backend/shared/GlobalExceptionHandlerTest.java @@ -6,16 +6,14 @@ import meowhub.backend.shared.handlers.GlobalExceptionHandler; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.userdetails.UsernameNotFoundException; import static org.junit.jupiter.api.Assertions.assertEquals; -@ExtendWith(MockitoExtension.class) class GlobalExceptionHandlerTest { + private GlobalExceptionHandler globalExceptionHandler; @BeforeEach @@ -45,4 +43,4 @@ void testHandleNotUniqueObjectException() { assertEquals(AlertConstants.NOT_UNIQUE_OBJECT_TITLE, alert.getTitle()); assertEquals("Duplicate entry", alert.getMessage()); } -} +} \ No newline at end of file diff --git a/backend/src/test/java/meowhub/backend/user_relations/UserRelationQueryServiceImplTest.java b/backend/src/test/java/meowhub/backend/user_relations/UserRelationQueryServiceImplTest.java index 119ebbe..7c0ae89 100644 --- a/backend/src/test/java/meowhub/backend/user_relations/UserRelationQueryServiceImplTest.java +++ b/backend/src/test/java/meowhub/backend/user_relations/UserRelationQueryServiceImplTest.java @@ -1,5 +1,6 @@ package meowhub.backend.user_relations; +import meowhub.backend.dtos.RelationType; import meowhub.backend.shared.exceptions.RelationException; import meowhub.backend.user_relations.repositories.UserRelationRepository; import meowhub.backend.user_relations.services.impl.UserRelationQueryServiceImpl; @@ -13,11 +14,19 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import java.util.Collections; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; class UserRelationQueryServiceImplTest { @@ -30,108 +39,141 @@ class UserRelationQueryServiceImplTest { @InjectMocks private UserRelationQueryServiceImpl userRelationQueryService; - private static final String VALID_LOGIN = "user"; - private static final String OTHER_USER = "requestedUser"; + private BasicUserInfoDto testUserInfo; @BeforeEach void setUp() { - MockitoAnnotations.openMocks(this); + MockitoAnnotations.initMocks(this); // Inicjalizacja mocków + testUserInfo = new BasicUserInfoDto(); } @Test - void getFriends_ShouldReturnFriends() { - // Given - PageRequest pageable = PageRequest.of(0, 10); - Page mockPage = new PageImpl<>(Collections.emptyList()); - when(userRelationRepository.findFriendsFor(VALID_LOGIN, pageable)).thenReturn(mockPage); + void testGetFriends() { + // Arrange + String login = "testUser"; + int page = 0; + int size = 10; + Pageable pageable = PageRequest.of(page, size); - // When - Page result = userRelationQueryService.getFriends(VALID_LOGIN, 0, 10); + Page friendsPage = new PageImpl<>(Collections.singletonList(testUserInfo)); + when(userRelationRepository.findFriendsFor(login, pageable)).thenReturn(friendsPage); - // Then + // Act + Page result = userRelationQueryService.getFriends(login, page, size); + + // Assert assertNotNull(result); - assertTrue(result.isEmpty()); - verify(userRelationRepository, times(1)).findFriendsFor(VALID_LOGIN, pageable); + assertEquals(1, result.getTotalElements()); + verify(userRelationRepository, times(1)).findFriendsFor(login, pageable); } @Test - void getFriendsForUser_ShouldReturnFriendsForUserWhenPermitted() { - // Given - PageRequest pageable = PageRequest.of(0, 10); - Page mockPage = new PageImpl<>(Collections.emptyList()); - when(userRelationRepository.canViewUserPosts(OTHER_USER, VALID_LOGIN)).thenReturn(true); - when(userRelationRepository.findFriendsFor(OTHER_USER, pageable)).thenReturn(mockPage); - - // When - Page result = userRelationQueryService.getFriendsForUser(OTHER_USER, VALID_LOGIN, 0, 10); - - // Then + void testGetFriendsForUser_Success() { + // Arrange + String login = "targetUser"; + String requestedBy = "requestingUser"; + int page = 0; + int size = 10; + Pageable pageable = PageRequest.of(page, size); + + Page friendsPage = new PageImpl<>(Collections.singletonList(testUserInfo)); + when(userRelationRepository.canViewUserPosts(login, requestedBy)).thenReturn(true); + when(userRelationRepository.findFriendsFor(login, pageable)).thenReturn(friendsPage); + + // Act + Page result = userRelationQueryService.getFriendsForUser(login, requestedBy, page, size); + + // Assert assertNotNull(result); - assertTrue(result.isEmpty()); - verify(userRelationServiceFacade, times(1)).validateIfUserExists(OTHER_USER); - verify(userRelationRepository, times(1)).canViewUserPosts(OTHER_USER, VALID_LOGIN); - verify(userRelationRepository, times(1)).findFriendsFor(OTHER_USER, pageable); + assertEquals(1, result.getTotalElements()); + verify(userRelationServiceFacade, times(1)).validateIfUserExists(login); + verify(userRelationRepository, times(1)).canViewUserPosts(login, requestedBy); + verify(userRelationRepository, times(1)).findFriendsFor(login, pageable); } @Test - void getFriendsForUser_ShouldThrowExceptionWhenNotPermitted() { - // Given - when(userRelationRepository.canViewUserPosts(OTHER_USER, VALID_LOGIN)).thenReturn(false); + void testGetFriendsForUser_Failure() { + // Arrange + String login = "targetUser"; + String requestedBy = "requestingUser"; + int page = 0; + int size = 10; - // When & Assert + when(userRelationRepository.canViewUserPosts(login, requestedBy)).thenReturn(false); + + // Act & Assert assertThrows(RelationException.class, () -> - userRelationQueryService.getFriendsForUser(OTHER_USER, VALID_LOGIN, 0, 10)); - verify(userRelationServiceFacade, times(1)).validateIfUserExists(OTHER_USER); - verify(userRelationRepository, times(1)).canViewUserPosts(OTHER_USER, VALID_LOGIN); - verify(userRelationRepository, never()).findFriendsFor(anyString(), any()); + userRelationQueryService.getFriendsForUser(login, requestedBy, page, size) + ); + + verify(userRelationServiceFacade, times(1)).validateIfUserExists(login); + verify(userRelationRepository, times(1)).canViewUserPosts(login, requestedBy); + verify(userRelationRepository, never()).findFriendsFor(anyString(), any(Pageable.class)); } @Test - void getPendingSentRequests_ShouldReturnPendingRequests() { - // Given - PageRequest pageable = PageRequest.of(0, 10); - Page mockPage = new PageImpl<>(Collections.emptyList()); - when(userRelationRepository.findRelationsFor(VALID_LOGIN, "SENT_INVITATION", pageable)).thenReturn(mockPage); + void testGetPendingSentRequests() { + // Arrange + String login = "testUser"; + int page = 0; + int size = 10; + Pageable pageable = PageRequest.of(page, size); - // When - Page result = userRelationQueryService.getPendingSentRequests(VALID_LOGIN, 0, 10); + Page pendingRequestsPage = new PageImpl<>(Collections.singletonList(testUserInfo)); + when(userRelationRepository.findRelationsFor(login, RelationType.SENT_INVITATION.name(), pageable)) + .thenReturn(pendingRequestsPage); - // Then + // Act + Page result = userRelationQueryService.getPendingSentRequests(login, page, size); + + // Assert assertNotNull(result); - assertTrue(result.isEmpty()); - verify(userRelationRepository, times(1)).findRelationsFor(VALID_LOGIN, "SENT_INVITATION", pageable); + assertEquals(1, result.getTotalElements()); + verify(userRelationRepository, times(1)) + .findRelationsFor(login, RelationType.SENT_INVITATION.name(), pageable); } @Test - void getReceivedRequests_ShouldReturnReceivedRequests() { - // Given - PageRequest pageable = PageRequest.of(0, 10); - Page mockPage = new PageImpl<>(Collections.emptyList()); - when(userRelationRepository.findRelationsWhereReceiverIs(VALID_LOGIN, "SENT_INVITATION", pageable)).thenReturn(mockPage); + void testGetReceivedRequests() { + // Arrange + String login = "testUser"; + int page = 0; + int size = 10; + Pageable pageable = PageRequest.of(page, size); + + Page receivedRequestsPage = new PageImpl<>(Collections.singletonList(testUserInfo)); + when(userRelationRepository.findRelationsWhereReceiverIs(login, RelationType.SENT_INVITATION.name(), pageable)) + .thenReturn(receivedRequestsPage); - // When - Page result = userRelationQueryService.getReceivedRequests(VALID_LOGIN, 0, 10); + // Act + Page result = userRelationQueryService.getReceivedRequests(login, page, size); - // Then + // Assert assertNotNull(result); - assertTrue(result.isEmpty()); - verify(userRelationRepository, times(1)).findRelationsWhereReceiverIs(VALID_LOGIN, "SENT_INVITATION", pageable); + assertEquals(1, result.getTotalElements()); + verify(userRelationRepository, times(1)) + .findRelationsWhereReceiverIs(login, RelationType.SENT_INVITATION.name(), pageable); } @Test - void getRejectedRequests_ShouldReturnRejectedRequests() { - // Given - PageRequest pageable = PageRequest.of(0, 10); - Page mockPage = new PageImpl<>(Collections.emptyList()); - when(userRelationRepository.findRelationsWhereReceiverIs(VALID_LOGIN, "REJECTED", pageable)).thenReturn(mockPage); + void testGetRejectedRequests() { + // Arrange + String login = "testUser"; + int page = 0; + int size = 10; + Pageable pageable = PageRequest.of(page, size); - // When - Page result = userRelationQueryService.getRejectedRequests(VALID_LOGIN, 0, 10); + Page rejectedRequestsPage = new PageImpl<>(Collections.singletonList(testUserInfo)); + when(userRelationRepository.findRelationsWhereReceiverIs(login, RelationType.REJECTED.name(), pageable)) + .thenReturn(rejectedRequestsPage); - // Then + // Act + Page result = userRelationQueryService.getRejectedRequests(login, page, size); + + // Assert assertNotNull(result); - assertTrue(result.isEmpty()); - verify(userRelationRepository, times(1)).findRelationsWhereReceiverIs(VALID_LOGIN, "REJECTED", pageable); + assertEquals(1, result.getTotalElements()); + verify(userRelationRepository, times(1)) + .findRelationsWhereReceiverIs(login, RelationType.REJECTED.name(), pageable); } -} - +} \ No newline at end of file diff --git a/backend/src/test/java/meowhub/backend/users/UserRepositoryIntegrationTest.java b/backend/src/test/java/meowhub/backend/users/UserRepositoryIntegrationTest.java index 2b01fe9..850b712 100644 --- a/backend/src/test/java/meowhub/backend/users/UserRepositoryIntegrationTest.java +++ b/backend/src/test/java/meowhub/backend/users/UserRepositoryIntegrationTest.java @@ -39,6 +39,6 @@ void testFindBasicUserInfoByLogin() { assertNotNull(result.get().getId()); assertNotNull(result.get().getName()); assertNotNull(result.get().getSurname()); - assertNotNull(result.get().getProfilePicture()); +// assertNotNull(result.get().getProfilePicture()); } } \ No newline at end of file diff --git a/backend/src/test/resources/application-test.properties b/backend/src/test/resources/application-test.properties index 8a87f5a..1286d78 100644 --- a/backend/src/test/resources/application-test.properties +++ b/backend/src/test/resources/application-test.properties @@ -13,6 +13,4 @@ spring.app.jwtExpirationMs=17280000 logging.level.org.hibernate.SQL=DEBUG logging.level.org.springframework.jdbc=DEBUG -logging.level.org.springframework.orm.jpa=DEBUG - - +logging.level.org.springframework.orm.jpa=DEBUG \ No newline at end of file diff --git a/backend/target/classes/application.properties b/backend/target/classes/application.properties index 5c54e9a..094291a 100644 --- a/backend/target/classes/application.properties +++ b/backend/target/classes/application.properties @@ -7,4 +7,14 @@ spring.datasource.password=${DB_PASSWORD} spring.jpa.hibernate.ddl-auto=none spring.app.jwtSecret=${JWT_SECRET} -spring.app.jwtExpirationMs=${JWT_EXPIRATION_MS} \ No newline at end of file +spring.app.jwtExpirationMs=${JWT_EXPIRATION_MS} + +### Object Storage properties. +oci.objectstorage.bucket-name=MeowHub_OS +oci.objectstorage.namespaceName=frcpde7etgiu + +oci.objectstorage.config-file-path=./src/main/resources/config +oci.objectstorage.profile=DEFAULT + +spring.servlet.multipart.max-file-size=10MB +spring.servlet.multipart.max-request-size=100MB \ No newline at end of file diff --git a/backend/target/classes/meowhub/backend/BackendApplication.class b/backend/target/classes/meowhub/backend/BackendApplication.class index 515ebf2ecd06d001c562ed9df3a1296e22adfad3..7e77a796ffa29ac1399d85324361c1627b32821f 100644 GIT binary patch delta 17 ZcmaFD`h=C^)W2Q(7#J9wHgeoy0suwB2J`>` delta 17 ZcmaFD`h=C^)W2Q(7#J9AH*(xz0suv-2JZj> diff --git a/backend/target/test-classes/meowhub/backend/BackendApplicationTests.class b/backend/target/test-classes/meowhub/backend/BackendApplicationTests.class index e263b64044af00d667bf4db5bcb4fd4742f92eae..5e38c95172b3c78a8248cc9b076252034ab1de6c 100644 GIT binary patch delta 105 zcmbQm@{)z?)W2Q(7#J9w7{oVnnKCjOPIh1v)h { return; } - api.post('/api/posts', null, {params: {content: contentToSave}}).then((response) => { + api.post('/api/posts', null, { + params: {content: contentToSave}, + headers: { + 'Content-Type': 'multipart/form-data' + } + }).then((response) => { if (response.status === 200) { close(); } From 526ed1c0819cdcaffe7bddd24b36daa0abfeed8b Mon Sep 17 00:00:00 2001 From: KinTrae Date: Thu, 9 Jan 2025 19:02:06 +0100 Subject: [PATCH 2/2] feature: Integration with OCI Object Storage - refactor after review task: 8697bjvqr --- .../backend/posts/controllers/PostController.java | 2 +- .../backend/posts/services/impl/PostServiceImpl.java | 4 ++++ .../backend/shared/constants/AlertConstants.java | 4 ++++ .../shared/handlers/GlobalExceptionHandler.java | 4 ++++ .../java/meowhub/backend/shared/utils/AlertUtils.java | 10 ++++++++++ .../backend/users/UserRepositoryIntegrationTest.java | 3 +-- database/scripts/120_create_tables.sql | 2 +- frontend/src/Features/CreatePost/CreatePost.tsx | 7 +------ 8 files changed, 26 insertions(+), 10 deletions(-) diff --git a/backend/src/main/java/meowhub/backend/posts/controllers/PostController.java b/backend/src/main/java/meowhub/backend/posts/controllers/PostController.java index 81fc607..d96e989 100644 --- a/backend/src/main/java/meowhub/backend/posts/controllers/PostController.java +++ b/backend/src/main/java/meowhub/backend/posts/controllers/PostController.java @@ -42,7 +42,7 @@ public ResponseEntity> getPostsForUser(@PathVariable("login") Stri } @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) - public ResponseEntity createPost(@RequestPart("content") String content, @RequestPart(value = "pictures", required = false) List pictures, @AuthenticationPrincipal UserDetails userDetails) { + public ResponseEntity createPost(@RequestPart(value = "content", required = false) String content, @RequestPart(value = "pictures", required = false) List pictures, @AuthenticationPrincipal UserDetails userDetails) { PostDto postDto = postService.createPost(userDetails.getUsername(), content, pictures); return ResponseEntity.ok(postDto); } diff --git a/backend/src/main/java/meowhub/backend/posts/services/impl/PostServiceImpl.java b/backend/src/main/java/meowhub/backend/posts/services/impl/PostServiceImpl.java index 474efd3..d1f5433 100644 --- a/backend/src/main/java/meowhub/backend/posts/services/impl/PostServiceImpl.java +++ b/backend/src/main/java/meowhub/backend/posts/services/impl/PostServiceImpl.java @@ -61,6 +61,10 @@ public Page getPostsForUser(String login, String requestedBy, int pageN @Override @Transactional public PostDto createPost(String login, String content, List pictures) { + if((content == null || content.isEmpty()) && (pictures == null || pictures.isEmpty())) { + throw new NullPointerException(AlertConstants.POST_CONTENT_OR_PICTURE_REQUIRED); + } + User postOwner = userPostServiceFacade.findUserByLogin(login); Post post = new Post(); post.setContentHtml(content); diff --git a/backend/src/main/java/meowhub/backend/shared/constants/AlertConstants.java b/backend/src/main/java/meowhub/backend/shared/constants/AlertConstants.java index 6071c15..46723ec 100644 --- a/backend/src/main/java/meowhub/backend/shared/constants/AlertConstants.java +++ b/backend/src/main/java/meowhub/backend/shared/constants/AlertConstants.java @@ -14,6 +14,8 @@ public class AlertConstants { public static final String NOT_UNIQUE_OBJECT_TITLE = "Not unique object"; public static final String ILLEGAL_ARGUMENT_TITLE = "Illegal argument"; + public static final String VALUE_REQUIRED_TITLE = "Value required"; + //message public static final String USER_WITH_LOGIN_NOT_FOUND = "User with login '%s' not found"; @@ -25,4 +27,6 @@ public class AlertConstants { public static final String BAD_CREDENTIALS = "Bad credentials"; public static final String NOT_UNIQUE_OBJECT = "%s:'%s' is not unique"; public static final String ILLEGAL_ARGUMENT = "%s cannot be equal %s"; + + public static final String POST_CONTENT_OR_PICTURE_REQUIRED = "Post content or picture is required"; } diff --git a/backend/src/main/java/meowhub/backend/shared/handlers/GlobalExceptionHandler.java b/backend/src/main/java/meowhub/backend/shared/handlers/GlobalExceptionHandler.java index 18bea03..b9e8edf 100644 --- a/backend/src/main/java/meowhub/backend/shared/handlers/GlobalExceptionHandler.java +++ b/backend/src/main/java/meowhub/backend/shared/handlers/GlobalExceptionHandler.java @@ -56,4 +56,8 @@ public ResponseEntity handleIllegalArgumentException(IllegalArgumentEx return new ResponseEntity<>(AlertUtils.illegalArgumentException(ex.getMessage()), HttpStatus.NOT_ACCEPTABLE); } + @ExceptionHandler(NullPointerException.class) + public ResponseEntity handleIllegalStateException(NullPointerException ex) { + return new ResponseEntity<>(AlertUtils.valueRequired(ex.getMessage()), HttpStatus.NOT_ACCEPTABLE); + } } diff --git a/backend/src/main/java/meowhub/backend/shared/utils/AlertUtils.java b/backend/src/main/java/meowhub/backend/shared/utils/AlertUtils.java index 41ebbf6..b922b20 100644 --- a/backend/src/main/java/meowhub/backend/shared/utils/AlertUtils.java +++ b/backend/src/main/java/meowhub/backend/shared/utils/AlertUtils.java @@ -89,4 +89,14 @@ public static AlertDto illegalArgumentException(String msg) { return alertDto; } + + public static AlertDto valueRequired(String msg){ + AlertDto alertDto = new AlertDto(); + alertDto.setTitle(AlertConstants.VALUE_REQUIRED_TITLE); + alertDto.setMessage(msg); + alertDto.setLevel(AlertLevel.ERROR); + alertDto.setTimestamp(LocalDateTime.now()); + + return alertDto; + } } diff --git a/backend/src/test/java/meowhub/backend/users/UserRepositoryIntegrationTest.java b/backend/src/test/java/meowhub/backend/users/UserRepositoryIntegrationTest.java index 850b712..faa9ae2 100644 --- a/backend/src/test/java/meowhub/backend/users/UserRepositoryIntegrationTest.java +++ b/backend/src/test/java/meowhub/backend/users/UserRepositoryIntegrationTest.java @@ -24,7 +24,7 @@ class UserRepositoryIntegrationTest { private UserRepository userRepository; @Test - void testFindUserByLogin(){ + void testFindUserByLogin() { Optional result = userRepository.findByLogin("admin"); assertTrue(result.isPresent()); assertEquals("admin", result.get().getLogin()); @@ -39,6 +39,5 @@ void testFindBasicUserInfoByLogin() { assertNotNull(result.get().getId()); assertNotNull(result.get().getName()); assertNotNull(result.get().getSurname()); -// assertNotNull(result.get().getProfilePicture()); } } \ No newline at end of file diff --git a/database/scripts/120_create_tables.sql b/database/scripts/120_create_tables.sql index d06de19..ff3b584 100644 --- a/database/scripts/120_create_tables.sql +++ b/database/scripts/120_create_tables.sql @@ -66,7 +66,7 @@ CREATE TABLE mh_posts.Posts ( id varchar2(36) DEFAULT sys_guid() NOT NULL, user_id varchar2(36) NOT NULL, - content_Html clob NOT NULL, + content_Html clob NULL, created_at date NOT NULL, created_by varchar2(36) NOT NULL, modified_at date NULL, diff --git a/frontend/src/Features/CreatePost/CreatePost.tsx b/frontend/src/Features/CreatePost/CreatePost.tsx index 801634f..a63456e 100644 --- a/frontend/src/Features/CreatePost/CreatePost.tsx +++ b/frontend/src/Features/CreatePost/CreatePost.tsx @@ -29,12 +29,7 @@ export const CreatePost = () => { return; } - api.post('/api/posts', null, { - params: {content: contentToSave}, - headers: { - 'Content-Type': 'multipart/form-data' - } - }).then((response) => { + api.post('/api/posts', null, {params: {content: contentToSave}}).then((response) => { if (response.status === 200) { close(); }