diff --git a/.gitignore b/.gitignore index a2a3040..173b6aa 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,11 @@ build/ ### VS Code ### .vscode/ + +### TexRendering ### +*.png +*.tex +*.pdf +*.log +*.aux +*.svg \ No newline at end of file diff --git a/pom.xml b/pom.xml index 0f72c5a..7c96e8e 100644 --- a/pom.xml +++ b/pom.xml @@ -134,6 +134,15 @@ 1.10.7 test + + org.springframework + spring-test + + + com.fasterxml.jackson.core + jackson-databind + 2.10.0 + diff --git a/src/main/java/com/patternpedia/api/entities/DiscussionComment.java b/src/main/java/com/patternpedia/api/entities/DiscussionComment.java new file mode 100644 index 0000000..ab7c945 --- /dev/null +++ b/src/main/java/com/patternpedia/api/entities/DiscussionComment.java @@ -0,0 +1,34 @@ +package com.patternpedia.api.entities; + + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.*; +import org.hibernate.annotations.GenericGenerator; + +import javax.persistence.*; +import java.util.Date; +import java.util.UUID; + + + +@Entity +@Data +@NoArgsConstructor +@Getter +@Setter +public class DiscussionComment { + @Id + @GeneratedValue(generator = "uuid") + @GenericGenerator(name = "uuid", strategy = "uuid2") + private UUID id; + private String text; + private UUID replyTo; + private Date date; + + @JsonIgnore + @ToString.Exclude + @ManyToOne + private DiscussionTopic discussionTopic; + + +} diff --git a/src/main/java/com/patternpedia/api/entities/DiscussionTopic.java b/src/main/java/com/patternpedia/api/entities/DiscussionTopic.java new file mode 100644 index 0000000..1896931 --- /dev/null +++ b/src/main/java/com/patternpedia/api/entities/DiscussionTopic.java @@ -0,0 +1,41 @@ +package com.patternpedia.api.entities; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.patternpedia.api.rest.model.Status; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.GenericGenerator; + +import javax.persistence.*; +import java.util.Date; +import java.util.List; +import java.util.UUID; + +@Entity +@Data +@Getter +@Setter +@NoArgsConstructor +public class DiscussionTopic { + @Id + @GeneratedValue(generator = "uuid") + @GenericGenerator(name = "uuid", strategy = "uuid2") + private UUID id; + private String title; + private String description; + private Status status; + private Date date; + private Double x; + private Double y; + private Double width; + private Double height; + private String fill; + private UUID imageId; + + @JsonIgnore + @OneToMany(mappedBy = "discussionTopic", cascade = CascadeType.ALL, fetch = FetchType.LAZY) + private List discussionComments; +} + diff --git a/src/main/java/com/patternpedia/api/entities/Image.java b/src/main/java/com/patternpedia/api/entities/Image.java new file mode 100644 index 0000000..f0fc2cd --- /dev/null +++ b/src/main/java/com/patternpedia/api/entities/Image.java @@ -0,0 +1,29 @@ +package com.patternpedia.api.entities; + +import lombok.*; + +import org.hibernate.annotations.GenericGenerator; + + +import javax.persistence.*; +import java.util.UUID; + +@Entity +@Data +@NoArgsConstructor +@Getter +@Setter +public class Image { + + @Id + @GeneratedValue(generator = "uuid") + @GenericGenerator(name = "uuid", strategy = "uuid2") + private UUID id; + + private String fileName; + + private String fileType; + + @Lob + private byte[] data; +} diff --git a/src/main/java/com/patternpedia/api/entities/Pattern.java b/src/main/java/com/patternpedia/api/entities/Pattern.java index fc00d6c..8fc8cce 100644 --- a/src/main/java/com/patternpedia/api/entities/Pattern.java +++ b/src/main/java/com/patternpedia/api/entities/Pattern.java @@ -38,4 +38,8 @@ public class Pattern extends EntityWithURI { @Column(columnDefinition = "jsonb") @NotNull private Object content; + + @Type(type = "jsonb") + @Column(columnDefinition = "jsonb") + private Object renderedContent; } diff --git a/src/main/java/com/patternpedia/api/repositories/DiscussionCommentRepository.java b/src/main/java/com/patternpedia/api/repositories/DiscussionCommentRepository.java new file mode 100644 index 0000000..2b1671d --- /dev/null +++ b/src/main/java/com/patternpedia/api/repositories/DiscussionCommentRepository.java @@ -0,0 +1,14 @@ +package com.patternpedia.api.repositories; + +import com.patternpedia.api.entities.DiscussionComment; +import com.patternpedia.api.entities.DiscussionTopic; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.rest.core.annotation.RepositoryRestResource; + +import java.util.List; +import java.util.UUID; + +@RepositoryRestResource(exported = false) +public interface DiscussionCommentRepository extends CrudRepository { + List findDiscussionCommentByDiscussionTopic(DiscussionTopic discussionTopic); +} diff --git a/src/main/java/com/patternpedia/api/repositories/DiscussionTopicRepository.java b/src/main/java/com/patternpedia/api/repositories/DiscussionTopicRepository.java new file mode 100644 index 0000000..89161a7 --- /dev/null +++ b/src/main/java/com/patternpedia/api/repositories/DiscussionTopicRepository.java @@ -0,0 +1,14 @@ +package com.patternpedia.api.repositories; + +import com.patternpedia.api.entities.DiscussionTopic; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.rest.core.annotation.RepositoryRestResource; + +import java.util.List; +import java.util.UUID; + +@RepositoryRestResource(exported = false) +public interface DiscussionTopicRepository extends CrudRepository { + List findDiscussionTopicsByImageId(UUID imageId); +} + diff --git a/src/main/java/com/patternpedia/api/repositories/ImageRepository.java b/src/main/java/com/patternpedia/api/repositories/ImageRepository.java new file mode 100644 index 0000000..b6c9cbe --- /dev/null +++ b/src/main/java/com/patternpedia/api/repositories/ImageRepository.java @@ -0,0 +1,10 @@ +package com.patternpedia.api.repositories; + +import com.patternpedia.api.entities.Image; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.rest.core.annotation.RepositoryRestResource; +import java.util.UUID; + +@RepositoryRestResource(exported = false) +public interface ImageRepository extends CrudRepository { +} diff --git a/src/main/java/com/patternpedia/api/rest/controller/DiscussionController.java b/src/main/java/com/patternpedia/api/rest/controller/DiscussionController.java new file mode 100644 index 0000000..c321190 --- /dev/null +++ b/src/main/java/com/patternpedia/api/rest/controller/DiscussionController.java @@ -0,0 +1,83 @@ +package com.patternpedia.api.rest.controller; + +import com.patternpedia.api.entities.DiscussionComment; +import com.patternpedia.api.entities.DiscussionTopic; +import com.patternpedia.api.rest.model.DiscussionTopicModel; +import com.patternpedia.api.service.DiscussionService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.UUID; + + +@RestController +@CrossOrigin(allowedHeaders = "*", origins = "*") +public class DiscussionController { + + private DiscussionService discussionService; + + @Autowired + public DiscussionController(DiscussionService discussionService) { + this.discussionService = discussionService; + } + + @ResponseStatus(HttpStatus.CREATED) + @PostMapping( + value = "/add-topic" + ) + public @ResponseBody + DiscussionTopic addDiscussionTopic(@RequestBody DiscussionTopic topic){ + // deepcode ignore XSS: + return this.discussionService.createTopic(topic); + } + + @DeleteMapping( + value = "/delete-topic/{topicId}" + ) + public @ResponseBody + ResponseEntity deleteDiscussionTopic(@PathVariable UUID topicId){ + this.discussionService.deleteTopicById(topicId); + return ResponseEntity.noContent().build(); + } + + @ResponseStatus(HttpStatus.CREATED) + @PostMapping( + value = "/add-comment/{topicId}" + ) + public @ResponseBody + DiscussionComment addDiscussionComment(@PathVariable UUID topicId, @RequestBody DiscussionComment comment){ + comment.setDiscussionTopic(this.discussionService.getTopicById(topicId)); + // deepcode ignore XSS: + return this.discussionService.createComment(comment); + } + + @GetMapping( + value = "/get-comments-by-topic/{topicId}" + ) + public @ResponseBody + List getCommentsByTopic(@PathVariable UUID topicId){ + // deepcode ignore XSS: + return this.discussionService.getCommentsByTopicId(topicId); + } + + @GetMapping( + value = "/get-topic-by-image/{imageId}" + ) + public @ResponseBody + List getTopicsByImageId(@PathVariable UUID imageId){ + // deepcode ignore XSS: + return this.discussionService.getTopicsByImageId(imageId); + } + + @GetMapping( + value = "/get-topics-and-comments-by-image/{imageId}" + ) + public @ResponseBody + List getTopicsAndCommentsByImageId(@PathVariable UUID imageId){ + // deepcode ignore XSS: + return this.discussionService.getTopicsAndCommentsByImageId(imageId); + } +} \ No newline at end of file diff --git a/src/main/java/com/patternpedia/api/rest/controller/ImageController.java b/src/main/java/com/patternpedia/api/rest/controller/ImageController.java new file mode 100644 index 0000000..b78aae6 --- /dev/null +++ b/src/main/java/com/patternpedia/api/rest/controller/ImageController.java @@ -0,0 +1,75 @@ +package com.patternpedia.api.rest.controller; + +import com.patternpedia.api.entities.Image; +import com.patternpedia.api.rest.model.ImageModel; +import com.patternpedia.api.service.DiscussionService; +import com.patternpedia.api.service.ImageService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + + +import java.util.UUID; + + + +@RestController +@CrossOrigin(allowedHeaders = "*", origins = "*") +public class ImageController { + + private ImageService imageService; + private DiscussionService discussionService; + + @Autowired + public ImageController(ImageService imageService, DiscussionService discussionService) { + this.imageService = imageService; + this.discussionService = discussionService; + } + + + @GetMapping( + value = "/get-image-by-id/{imageId}", + produces = "image/svg+xml" + ) + public @ResponseBody + byte[] getImageById(@PathVariable UUID imageId){ + // deepcode ignore XSS: Returning by service created content via uuid + return this.imageService.getImageById(imageId).getData(); + } + + @PostMapping( + value = "/update-image/{imageId}", + produces = "image/svg+xml" + ) + public @ResponseBody + byte[] updateImage(@PathVariable UUID imageId, @RequestBody byte[] data){ + Image image = new Image(); + image.setId(imageId); + image.setData(data); + image.setFileName(imageId.toString()); + image.setFileType("image/svg+xml"); + return this.imageService.updateImage(image).getData(); + } + + + @ResponseStatus(HttpStatus.CREATED) + @PostMapping( + value = "/add-image", + produces = "application/json" + ) + public @ResponseBody + Image addImage(@RequestBody Image image){ + // deepcode ignore XSS: + return this.imageService.createImage(image); + + } + + @GetMapping( + value = "/get-image-and-comments-by-id/{imageId}" + ) + public @ResponseBody + ImageModel getImageAndCommentsById(@PathVariable UUID imageId){ + // deepcode ignore XSS: Returning by service created content via uuid + return new ImageModel(this.imageService.getImageById(imageId).getData(), this.discussionService.getTopicsAndCommentsByImageId(imageId)); + } +} \ No newline at end of file diff --git a/src/main/java/com/patternpedia/api/rest/controller/PatternController.java b/src/main/java/com/patternpedia/api/rest/controller/PatternController.java index 08124c1..95fa9a7 100644 --- a/src/main/java/com/patternpedia/api/rest/controller/PatternController.java +++ b/src/main/java/com/patternpedia/api/rest/controller/PatternController.java @@ -10,12 +10,9 @@ import com.patternpedia.api.exception.DirectedEdgeNotFoundException; import com.patternpedia.api.exception.UndirectedEdgeNotFoundException; import com.patternpedia.api.rest.model.PatternContentModel; +import com.patternpedia.api.rest.model.PatternRenderedContentModel; import com.patternpedia.api.rest.model.PatternModel; -import com.patternpedia.api.service.PatternLanguageService; -import com.patternpedia.api.service.PatternRelationDescriptorService; -import com.patternpedia.api.service.PatternService; -import com.patternpedia.api.service.PatternViewService; - +import com.patternpedia.api.service.*; import com.fasterxml.jackson.databind.ObjectMapper; import javax.validation.Valid; @@ -50,17 +47,20 @@ public class PatternController { private PatternViewService patternViewService; private PatternRelationDescriptorService patternRelationDescriptorService; private ObjectMapper objectMapper; + private PatternRenderService patternRenderService; public PatternController(PatternService patternService, PatternLanguageService patternLanguageService, PatternViewService patternViewService, PatternRelationDescriptorService patternRelationDescriptorService, + PatternRenderService patternRenderService, ObjectMapper objectMapper) { this.patternService = patternService; this.patternLanguageService = patternLanguageService; this.patternViewService = patternViewService; this.patternRelationDescriptorService = patternRelationDescriptorService; this.objectMapper = objectMapper; + this.patternRenderService = patternRenderService; } static List getPatternLanguagePatternCollectionLinks(UUID patternLanguageId) { @@ -90,6 +90,7 @@ List getPatternLinks(Pattern pattern) { .andAffordance(afford(methodOn(PatternController.class).updatePatternViaPut(pattern.getPatternLanguage().getId(), pattern.getId(), null))) .andAffordance(afford(methodOn(PatternController.class).deletePatternOfPatternLanguage(pattern.getPatternLanguage().getId(), pattern.getId())))); links.add(linkTo(methodOn(PatternController.class).getPatternContentOfPattern(pattern.getPatternLanguage().getId(), pattern.getId())).withRel("content")); + links.add(linkTo(methodOn(PatternController.class).getPatternRenderedContentOfPattern(pattern.getPatternLanguage().getId(), pattern.getId())).withRel("renderedContent")); links.add(linkTo(methodOn(PatternLanguageController.class).getPatternLanguageById(pattern.getPatternLanguage().getId())).withRel("patternLanguage")); if (null != pattern.getPatternViews()) { @@ -261,6 +262,7 @@ CollectionModel> getPatternsOfPatternLanguage(@PathVar .map(pattern -> new EntityModel<>(PatternModel.from(pattern), linkTo(methodOn(PatternController.class).getPatternOfPatternLanguageById(patternLanguageId, pattern.getId())).withSelfRel(), linkTo(methodOn(PatternController.class).getPatternContentOfPattern(patternLanguageId, pattern.getId())).withRel("content"), + linkTo(methodOn(PatternController.class).getPatternRenderedContentOfPattern(patternLanguageId, pattern.getId())).withRel("renderedContent"), linkTo(methodOn(PatternLanguageController.class).getPatternLanguageById(patternLanguageId)).withRel("patternLanguage"))) .collect(Collectors.toList()); return new CollectionModel<>(patterns, getPatternLanguagePatternCollectionLinks(patternLanguageId)); @@ -314,7 +316,13 @@ CollectionModel> getPatternsOfPatternView(@PathVariabl @PostMapping(value = "/patternViews/{patternViewId}/patterns") @CrossOrigin(exposedHeaders = "Location") @ResponseStatus(HttpStatus.CREATED) - public ResponseEntity addPatternToPatternView(@PathVariable UUID patternViewId, @RequestBody Pattern pattern) { + public ResponseEntity addPatternToPatternView(@PathVariable UUID patternViewId, @RequestBody Pattern pattern) { + Object renderedContent = patternRenderService.renderContent(pattern, null); + if (renderedContent != null){ + pattern.setRenderedContent(renderedContent); + } else { + pattern.setRenderedContent(pattern.getContent()); + } this.patternViewService.addPatternToPatternView(patternViewId, pattern.getId()); return ResponseEntity.created(linkTo(methodOn(PatternController.class) .getPatternOfPatternViewById(patternViewId, pattern.getId())).toUri()).build(); @@ -345,7 +353,12 @@ ResponseEntity addPatternToPatternLanguage(@PathVariable UUID patternLanguage if (null == pattern.getUri()) { pattern.setUri(patternLanguage.getUri() + '/' + CaseUtils.toCamelCase(pattern.getName(), false)); } - + Object renderedContent = patternRenderService.renderContent(pattern, null); + if (renderedContent != null){ + pattern.setRenderedContent(renderedContent); + } else { + pattern.setRenderedContent(pattern.getContent()); + } pattern = this.patternLanguageService.createPatternAndAddToPatternLanguage(patternLanguageId, pattern); return ResponseEntity.created(linkTo(methodOn(PatternController.class) @@ -367,6 +380,13 @@ EntityModel updatePatternViaPut(@PathVariable UUID patternLanguageId, @ PatternLanguage patternLanguage = this.patternLanguageService.getPatternLanguageById(patternLanguageId); Pattern persistedVersion = this.patternService.getPatternById(patternId); // Remark: At the moment we do not support changing name, uri of a pattern + + Object renderedContent = patternRenderService.renderContent(pattern, persistedVersion); + if (renderedContent != null){ + persistedVersion.setRenderedContent(renderedContent); + } else { + persistedVersion.setRenderedContent(pattern.getContent()); + } persistedVersion.setIconUrl(pattern.getIconUrl()); persistedVersion.setContent(pattern.getContent()); @@ -374,6 +394,7 @@ EntityModel updatePatternViaPut(@PathVariable UUID patternLanguageId, @ return new EntityModel<>(pattern, linkTo(methodOn(PatternController.class).getPatternOfPatternLanguageById(patternLanguageId, patternId)).withSelfRel(), linkTo(methodOn(PatternController.class).getPatternContentOfPattern(patternLanguageId, patternId)).withRel("content"), + linkTo(methodOn(PatternController.class).getPatternRenderedContentOfPattern(patternLanguageId, patternId)).withRel("renderedContent"), linkTo(methodOn(PatternLanguageController.class).getPatternLanguageById(patternLanguageId)).withRel("patternLanguage")); } @@ -400,6 +421,27 @@ EntityModel getPatternContentOfPattern(@PathVariable UUID patternLanguag return new EntityModel<>(model, linkTo(methodOn(PatternController.class).getPatternContentOfPattern(patternLanguageId, patternId)).withSelfRel(), linkTo(methodOn(PatternController.class).getPatternOfPatternLanguageById(patternLanguageId, patternId)).withRel("pattern"), + linkTo(methodOn(PatternController.class).getPatternRenderedContentOfPattern(patternLanguageId, patternId)).withRel("renderedContent"), + linkTo(methodOn(PatternLanguageController.class).getPatternLanguageById(patternLanguageId)).withRel("patternLanguage")); + } + + + @GetMapping(value = "/patternLanguages/{patternLanguageId}/patterns/{patternId}/renderedContent") + EntityModel getPatternRenderedContentOfPattern(@PathVariable UUID patternLanguageId, @PathVariable UUID patternId) { + + Pattern pattern = this.patternLanguageService.getPatternOfPatternLanguageById(patternLanguageId, patternId); + PatternRenderedContentModel model = new PatternRenderedContentModel(); + + if (null == pattern.getRenderedContent()) { + model.setRenderedContent(this.objectMapper.createObjectNode()); + } else { + model.setRenderedContent(pattern.getRenderedContent()); + } + + return new EntityModel<>(model, + linkTo(methodOn(PatternController.class).getPatternContentOfPattern(patternLanguageId, patternId)).withSelfRel(), + linkTo(methodOn(PatternController.class).getPatternOfPatternLanguageById(patternLanguageId, patternId)).withRel("pattern"), + linkTo(methodOn(PatternController.class).getPatternRenderedContentOfPattern(patternLanguageId, patternId)).withRel("content"), linkTo(methodOn(PatternLanguageController.class).getPatternLanguageById(patternLanguageId)).withRel("patternLanguage")); } } diff --git a/src/main/java/com/patternpedia/api/rest/model/DiscussionTopicModel.java b/src/main/java/com/patternpedia/api/rest/model/DiscussionTopicModel.java new file mode 100644 index 0000000..69e4ac7 --- /dev/null +++ b/src/main/java/com/patternpedia/api/rest/model/DiscussionTopicModel.java @@ -0,0 +1,22 @@ +package com.patternpedia.api.rest.model; + +import com.patternpedia.api.entities.DiscussionComment; +import com.patternpedia.api.entities.DiscussionTopic; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@NoArgsConstructor +public class DiscussionTopicModel { + private DiscussionTopic discussionTopic; + private List discussionComments; + + public DiscussionTopicModel (DiscussionTopic discussionTopic, List discussionComments){ + this.discussionTopic = discussionTopic; + this.discussionComments = discussionComments; + } +} diff --git a/src/main/java/com/patternpedia/api/rest/model/ImageModel.java b/src/main/java/com/patternpedia/api/rest/model/ImageModel.java new file mode 100644 index 0000000..614ec15 --- /dev/null +++ b/src/main/java/com/patternpedia/api/rest/model/ImageModel.java @@ -0,0 +1,20 @@ +package com.patternpedia.api.rest.model; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@NoArgsConstructor +public class ImageModel { + private byte[] image; + private List topicModels; + + public ImageModel(byte[] image, List topicModels){ + this.image = image; + this.topicModels = topicModels; + } +} diff --git a/src/main/java/com/patternpedia/api/rest/model/LatexContent.java b/src/main/java/com/patternpedia/api/rest/model/LatexContent.java new file mode 100644 index 0000000..6427a82 --- /dev/null +++ b/src/main/java/com/patternpedia/api/rest/model/LatexContent.java @@ -0,0 +1,25 @@ +package com.patternpedia.api.rest.model; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + + +@Getter +@Setter +@NoArgsConstructor +public class LatexContent { + private String content; + private List latexPackages; + private String output; + + public LatexContent(String content, List latexPackages, String output){ + this.content = content; + this.latexPackages = latexPackages; + this.output = output; + } + + +} diff --git a/src/main/java/com/patternpedia/api/rest/model/PatternRenderedContentModel.java b/src/main/java/com/patternpedia/api/rest/model/PatternRenderedContentModel.java new file mode 100644 index 0000000..3172fb4 --- /dev/null +++ b/src/main/java/com/patternpedia/api/rest/model/PatternRenderedContentModel.java @@ -0,0 +1,10 @@ +package com.patternpedia.api.rest.model; + +import lombok.Data; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +@Data +public class PatternRenderedContentModel { + private Object renderedContent; +} diff --git a/src/main/java/com/patternpedia/api/rest/model/Status.java b/src/main/java/com/patternpedia/api/rest/model/Status.java new file mode 100644 index 0000000..a341a30 --- /dev/null +++ b/src/main/java/com/patternpedia/api/rest/model/Status.java @@ -0,0 +1,6 @@ +package com.patternpedia.api.rest.model; + +public enum Status { + OPEN, + CLOSED; +} diff --git a/src/main/java/com/patternpedia/api/service/DiscussionService.java b/src/main/java/com/patternpedia/api/service/DiscussionService.java new file mode 100644 index 0000000..2928eb2 --- /dev/null +++ b/src/main/java/com/patternpedia/api/service/DiscussionService.java @@ -0,0 +1,42 @@ +package com.patternpedia.api.service; + +import com.patternpedia.api.entities.DiscussionComment; +import com.patternpedia.api.entities.DiscussionTopic; +import com.patternpedia.api.rest.model.DiscussionTopicModel; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.UUID; + +public interface DiscussionService { + + @Transactional + DiscussionTopic createTopic(DiscussionTopic topic); + + @Transactional(readOnly = true) + DiscussionTopic getTopicById(UUID topicId); + + @Transactional + void deleteTopicById(UUID id); + + @Transactional + DiscussionComment createComment(DiscussionComment topic); + + @Transactional(readOnly = true) + DiscussionComment getCommentById(UUID commentId); + + @Transactional + void deleteCommentById(UUID id); + + @Transactional + List getCommentsByTopicId(UUID topicId); + + @Transactional + List getTopicsByImageId(UUID imageId); + + @Transactional + List getTopicsAndCommentsByImageId(UUID imageId); + + @Transactional + List updateTopicsByImageId(UUID oldImageId, UUID newImageId); +} diff --git a/src/main/java/com/patternpedia/api/service/DiscussionServiceImpl.java b/src/main/java/com/patternpedia/api/service/DiscussionServiceImpl.java new file mode 100644 index 0000000..c3f07a6 --- /dev/null +++ b/src/main/java/com/patternpedia/api/service/DiscussionServiceImpl.java @@ -0,0 +1,94 @@ +package com.patternpedia.api.service; + +import com.patternpedia.api.repositories.DiscussionCommentRepository; +import com.patternpedia.api.repositories.DiscussionTopicRepository; +import com.patternpedia.api.entities.DiscussionComment; +import com.patternpedia.api.entities.DiscussionTopic; +import com.patternpedia.api.rest.model.DiscussionTopicModel; +import org.springframework.data.rest.webmvc.ResourceNotFoundException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + + +@Service +@Validated +@Transactional +public class DiscussionServiceImpl implements DiscussionService{ + + private DiscussionTopicRepository discussionTopicRepository; + private DiscussionCommentRepository discussionCommentRepository; + + public DiscussionServiceImpl(DiscussionTopicRepository discussionTopicRepository, DiscussionCommentRepository discussionCommentRepository){ + this.discussionCommentRepository = discussionCommentRepository; + this.discussionTopicRepository = discussionTopicRepository; + } + + @Override + public DiscussionTopic createTopic(DiscussionTopic topic) { + return this.discussionTopicRepository.save(topic); + } + + @Override + public void deleteTopicById(UUID id) { + for (DiscussionComment discussionComment: this.getTopicById(id).getDiscussionComments()){ + this.discussionCommentRepository.deleteById(discussionComment.getId()); + } + this.discussionTopicRepository.deleteById(id); + } + + @Override + public DiscussionTopic getTopicById(UUID topicId) { + return this.discussionTopicRepository.findById(topicId).orElseThrow(() -> new ResourceNotFoundException("not found")); + } + + @Override + public DiscussionComment createComment(DiscussionComment topic) { + return this.discussionCommentRepository.save(topic); + } + + @Override + public DiscussionComment getCommentById(UUID commentId) { + return this.discussionCommentRepository.findById(commentId).orElseThrow(() -> new ResourceNotFoundException("not found")); + } + + @Override + public void deleteCommentById(UUID id) { + this.discussionCommentRepository.deleteById(id); + } + + @Override + public List getCommentsByTopicId(UUID topicId) { + return this.discussionCommentRepository.findDiscussionCommentByDiscussionTopic(this.getTopicById(topicId)); + } + + @Override + public List getTopicsByImageId(UUID imageId) { + return this.discussionTopicRepository.findDiscussionTopicsByImageId(imageId); + } + + @Override + public List getTopicsAndCommentsByImageId(UUID imageId) { + List topicModelList = new ArrayList<>(); + this.discussionTopicRepository.findDiscussionTopicsByImageId(imageId).forEach( topic -> { + DiscussionTopicModel topicModel = new DiscussionTopicModel(topic, this.discussionCommentRepository.findDiscussionCommentByDiscussionTopic(this.getTopicById(topic.getId()))); + topicModelList.add(topicModel); + }); + return topicModelList; + } + + @Override + public List updateTopicsByImageId(UUID oldImageId, UUID newImageId) { + this.discussionTopicRepository.findDiscussionTopicsByImageId(oldImageId).forEach(topic -> { + topic.setImageId(newImageId); + this.discussionTopicRepository.save(topic); + }); + return this.discussionTopicRepository.findDiscussionTopicsByImageId(newImageId); + } + + +} diff --git a/src/main/java/com/patternpedia/api/service/ImageService.java b/src/main/java/com/patternpedia/api/service/ImageService.java new file mode 100644 index 0000000..f3850b1 --- /dev/null +++ b/src/main/java/com/patternpedia/api/service/ImageService.java @@ -0,0 +1,21 @@ +package com.patternpedia.api.service; +import com.patternpedia.api.entities.Image; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Optional; +import java.util.UUID; + +public interface ImageService { + + @Transactional + Image createImage(Image image); + + @Transactional(readOnly = true) + Image getImageById(UUID imageId); + + @Transactional + Image updateImage(Image image); + + @Transactional + void deleteImage(Image image); +} diff --git a/src/main/java/com/patternpedia/api/service/ImageServiceImpl.java b/src/main/java/com/patternpedia/api/service/ImageServiceImpl.java new file mode 100644 index 0000000..17cbeab --- /dev/null +++ b/src/main/java/com/patternpedia/api/service/ImageServiceImpl.java @@ -0,0 +1,58 @@ +package com.patternpedia.api.service; + +import com.patternpedia.api.entities.Image; +import com.patternpedia.api.repositories.ImageRepository; +import org.springframework.data.rest.webmvc.ResourceNotFoundException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + + +import java.util.UUID; + +@Service +@Validated +@Transactional +public class ImageServiceImpl implements ImageService{ + + private ImageRepository imageRepository; + + public ImageServiceImpl(ImageRepository imageRepository){ + this.imageRepository = imageRepository; + } + + @Override + public Image createImage(Image image) { + if (null == image) { + throw new NullPointerException(); + } + + return this.imageRepository.save(image); + } + + @Override + @Transactional(readOnly = true) + public Image getImageById(UUID imageId) { + return this.imageRepository.findById(imageId).orElseThrow(() -> new ResourceNotFoundException("not found")); + } + + @Override + @Transactional + public Image updateImage(Image image) { + if (null == image) { + throw new NullPointerException(); + } + + return this.imageRepository.save(image); + } + + @Override + @Transactional + public void deleteImage(Image image) { + if (null == image) { + throw new NullPointerException(); + } + this.imageRepository.save(image); + this.imageRepository.deleteById(image.getId()); + } +} diff --git a/src/main/java/com/patternpedia/api/service/PatternRenderService.java b/src/main/java/com/patternpedia/api/service/PatternRenderService.java new file mode 100644 index 0000000..aa0379c --- /dev/null +++ b/src/main/java/com/patternpedia/api/service/PatternRenderService.java @@ -0,0 +1,21 @@ +package com.patternpedia.api.service; + +import com.patternpedia.api.entities.Pattern; +import org.springframework.transaction.annotation.Transactional; +import java.util.List; + + +public interface PatternRenderService { + + @Transactional (readOnly = false) + Object renderContent (Pattern pattern, Pattern oldVersion); + + @Transactional (readOnly = false) + Integer[] getNextOccurance(String content, String begin, String end); + + @Transactional (readOnly = false) + byte [] renderContentViaAPI(String content, List packages, String output); + + @Transactional (readOnly = false) + String saveAndUploadFile(byte[] file, String output); +} diff --git a/src/main/java/com/patternpedia/api/service/PatternRenderServiceImpl.java b/src/main/java/com/patternpedia/api/service/PatternRenderServiceImpl.java new file mode 100644 index 0000000..ab51bb3 --- /dev/null +++ b/src/main/java/com/patternpedia/api/service/PatternRenderServiceImpl.java @@ -0,0 +1,223 @@ +package com.patternpedia.api.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.patternpedia.api.entities.DiscussionTopic; +import com.patternpedia.api.entities.Image; +import com.patternpedia.api.entities.Pattern; +import com.patternpedia.api.rest.model.LatexContent; +import org.apache.commons.text.similarity.JaccardSimilarity; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +import java.net.URI; +import java.time.Instant; +import java.util.*; + +@Component +public class PatternRenderServiceImpl implements PatternRenderService { + @Autowired + private ImageService imageService; + + @Autowired + private DiscussionService discussionService; + + /** + * + * @param pattern Pattern received from Frontend (not saved in DB yet) + * @param oldVersion Database version of the received Pattern + * @return content JSON that includes ids of the rendered LaTeX code instead of the LaTeX code + */ + @Override + public Object renderContent(Pattern pattern, Pattern oldVersion) { + String jsonString = null; + String contentOld = ""; + String renderedContentOld = ""; + ObjectMapper mapper = new ObjectMapper(); + try { + jsonString = mapper.writeValueAsString(pattern.getContent()); + if(oldVersion == null){ + contentOld = "null"; + renderedContentOld = "null"; + } else { + contentOld = mapper.writeValueAsString(oldVersion.getContent()); + renderedContentOld = mapper.writeValueAsString(oldVersion.getRenderedContent()); + } + } catch (Exception e) { + return null; + } + + + if (jsonString == null) + return null; + + + //get old quantikz and qcircuit occurences and match them with the related svg tags and imageids + ArrayList oldContentOccurances = new ArrayList<>(); + ArrayList oldSVGOccurances = new ArrayList<>(); + while(true){ + Integer[] occuranceStartEndOldQuantikz = getNextOccurance(contentOld, "\\\\begin{quantikz}", "\\end{quantikz}"); + Integer[] occuranceStartEndOldQcircuit = getNextOccurance(contentOld, "\\\\Qcircuit", "end}"); + Integer[] svgOccurencesOld = getNextOccurance(renderedContentOld, "", ""); + if (((occuranceStartEndOldQcircuit[0] != -1 && occuranceStartEndOldQcircuit[1] != -1) || + (occuranceStartEndOldQuantikz[0] != -1 && occuranceStartEndOldQuantikz[1] != -1)) && svgOccurencesOld[0] != -1 && svgOccurencesOld[1] != -1) { + if(occuranceStartEndOldQuantikz[0] != -1 && ((occuranceStartEndOldQuantikz[0] < occuranceStartEndOldQcircuit[0]) || occuranceStartEndOldQcircuit[0] == -1 )){ + //HAS OLD QUANTIKZ CONTENT + oldContentOccurances.add(contentOld.substring(occuranceStartEndOldQuantikz[0], occuranceStartEndOldQuantikz[1] + 14) + .replaceAll("\\\\n", " ").replaceAll("(\\\\t)(?!a)"," ") + .replaceAll("\\\\\\\\","\\\\")); + contentOld = contentOld.replace(contentOld.substring(occuranceStartEndOldQuantikz[0], occuranceStartEndOldQuantikz[1] + 14), " "); + } else if(occuranceStartEndOldQcircuit[0] != -1 && ((occuranceStartEndOldQcircuit[0] < occuranceStartEndOldQuantikz[0]) || occuranceStartEndOldQuantikz[0] == -1 )){ + //HAS OLD QCircuit CONTENT + oldContentOccurances.add(contentOld.substring(occuranceStartEndOldQcircuit[0], occuranceStartEndOldQcircuit[1] + 4) + .replaceAll("\\\\n", " ").replaceAll("(\\\\t)(?!a)"," ") + .replaceAll("\\\\\\\\","\\\\")); + contentOld = contentOld.replace(contentOld.substring(occuranceStartEndOldQcircuit[0], occuranceStartEndOldQcircuit[1] + 4), " ");} + + oldSVGOccurances.add(renderedContentOld.substring(svgOccurencesOld[0], svgOccurencesOld[1] + 6)); + renderedContentOld = renderedContentOld.replace(renderedContentOld.substring(svgOccurencesOld[0], svgOccurencesOld[1] + 6), " "); + } + else { + break; + } + } + + + int countQuantikz = 0; + //JaccardSimilarity is ued to check if the Quantikz Occurance is similar to the previous one. If that is the case all comments of the old graphic get copied to the new one + JaccardSimilarity jaccardSimilarity = new JaccardSimilarity(); + while (true) { + Integer[] occuranceStartEnd = getNextOccurance(jsonString, "\\\\begin{quantikz}", "\\end{quantikz}"); + if (occuranceStartEnd[0] != -1 && occuranceStartEnd[1] != -1) { + String renderContent = jsonString.substring(occuranceStartEnd[0], occuranceStartEnd[1] + 14) + .replaceAll("\\\\n", " ").replaceAll("(\\\\t)(?!a)"," ") + .replaceAll("\\\\\\\\","\\\\"); + boolean occured = false; + + for (int i = 0; i < oldContentOccurances.size(); i++){ + if(oldContentOccurances.get(i).equals(renderContent)){ + occured = true; + jsonString = jsonString.replace(jsonString.substring(occuranceStartEnd[0], occuranceStartEnd[1] + 14), " " + oldSVGOccurances.get(i) + " "); + } + } + + if(!occured){ + List settings = new ArrayList<>(); + settings.add("\\usepackage{tikz} \n"); + settings.add("\\usetikzlibrary{quantikz} \n"); + byte []renderedFile = renderContentViaAPI(renderContent, settings, "svg"); + String id = saveAndUploadFile(renderedFile, "svg"); + jsonString = jsonString.replace(jsonString.substring(occuranceStartEnd[0], occuranceStartEnd[1] + 14), " " + id + " "); + if(countQuantikz < oldContentOccurances.size()){ + if(jaccardSimilarity.apply(oldContentOccurances.get(countQuantikz), renderContent) > 0.8) { + this.discussionService.updateTopicsByImageId(UUID.fromString(oldSVGOccurances.get(countQuantikz).substring(5, oldSVGOccurances.get(countQuantikz).length() - 6)), UUID.fromString(id.substring(5, id.length() - 6))); + } + } + } + countQuantikz++ ; + }else { + break; + } + } + + + int countQcircuit = 0; + while (true) { + Integer[] occuranceStartEnd = getNextOccurance(jsonString, "\\\\Qcircuit", "end}"); + if (occuranceStartEnd[0] != -1 && occuranceStartEnd[1] != -1) { + String renderContent = jsonString.substring(occuranceStartEnd[0], occuranceStartEnd[1] + 4) + .replaceAll("\\\\n", " ").replaceAll("(\\\\t)(?!a)"," ") + .replaceAll("\\\\\\\\","\\\\"); + boolean occured = false; + + for (int i = 0; i < oldContentOccurances.size(); i++){ + if(oldContentOccurances.get(i).equals(renderContent)){ + occured = true; + jsonString = jsonString.replace(jsonString.substring(occuranceStartEnd[0], occuranceStartEnd[1] + 4), " " + oldSVGOccurances.get(i) + " "); + } + } + + if(!occured){ + List settings = new ArrayList<>(); + settings.add("\\usepackage[braket, qm]{qcircuit} \n"); + settings.add("\\usepackage{amsmath} \n"); + settings.add("\\usepackage{listings} \n"); + settings.add("\\renewcommand{\\arraystretch}{1.5} \n"); + renderContent = renderContent.replace(" end}"," }"); + byte []renderedFile = renderContentViaAPI(renderContent, settings, "svg"); + String id = saveAndUploadFile(renderedFile, "svg"); + jsonString = jsonString.replace(jsonString.substring(occuranceStartEnd[0], occuranceStartEnd[1] + 4), " " + id + " "); + if(countQcircuit < oldContentOccurances.size()){ + if(jaccardSimilarity.apply(oldContentOccurances.get(countQcircuit), renderContent) > 0.8) { + this.discussionService.updateTopicsByImageId(UUID.fromString(oldSVGOccurances.get(countQcircuit).substring(5, oldSVGOccurances.get(countQcircuit).length() - 6)), UUID.fromString(id.substring(5, id.length() - 6))); + } + } + } + countQcircuit++ ; + }else { + break; + } + } + + + Map map = null; + try { + map = mapper.readValue(jsonString, Map.class); + } catch (Exception e) { + return null; + } + return map; + + } + + + public Integer[] getNextOccurance(String content, String begin, String end) { + return new Integer[]{content.indexOf(begin, 0), content.indexOf(end, 0)}; + } + + + /** + * + * @param content LaTeX code + * @param packages LaTeX packages + * @param output Output Format for example "svg" + * @return Renderresult as byte array + */ + public byte[] renderContentViaAPI(String content, List packages, String output){ + LatexContent latexContent = new LatexContent(content,packages,output); + byte[] file = null; + try{ + RestTemplate restTemplate = new RestTemplate(); + final String baseUrl = "http://localhost:"+8082+"/renderLatex/"; + URI uri = new URI(baseUrl); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity<>(latexContent, headers); + ResponseEntity result = restTemplate.postForEntity(uri, entity, byte[].class); + file = result.getBody(); + } catch (Exception e){ + return null; + } + return file; + } + + + /** + * + * @param file File to upload as byte array + * @param output Filetype for example "svg" + * @return ImageId surrounded by Tags + */ + public String saveAndUploadFile(byte[] file, String output) { + Image image = new Image(); + image.setData(file); + image.setFileName("latexRender" + Instant.now().getEpochSecond()); + image.setFileType(output); + Image uploadedImage = imageService.createImage(image); + return "" + uploadedImage.getId().toString() + ""; + } +} diff --git a/src/main/resources/db/migration/V1__ddl.sql b/src/main/resources/db/migration/V1__ddl.sql deleted file mode 100644 index 6342dfa..0000000 --- a/src/main/resources/db/migration/V1__ddl.sql +++ /dev/null @@ -1,19 +0,0 @@ -CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; - -CREATE TABLE pattern -( - id UUID NOT NULL, - uri varchar(255), - name varchar(255), - content jsonb, - PRIMARY KEY (id) -); - -CREATE TABLE pattern_language -( - id UUID NOT NULL, - uri varchar(255), - name varchar(255), - content jsonb, - PRIMARY KEY (id) -); \ No newline at end of file diff --git a/src/main/resources/db/migration/V2__data.sql b/src/main/resources/db/migration/V2__data.sql deleted file mode 100644 index 01f4e8f..0000000 --- a/src/main/resources/db/migration/V2__data.sql +++ /dev/null @@ -1,14 +0,0 @@ -INSERT INTO pattern - ( - id, - content, - name, - uri - ) -VALUES - ( - '531e4cdd-bb78-4769-a0c7-cb948a9f1238', - '{"name":"test pattern","problem":"XXX","context":"YYY","solution":"ZZZ"}', - 'test pattern', - 'http://purl.org/patternpedia/testpattern' - ); \ No newline at end of file diff --git a/src/test/java/com/patternpedia/api/integration/ImageAndDiscussionControllerTest.java b/src/test/java/com/patternpedia/api/integration/ImageAndDiscussionControllerTest.java new file mode 100644 index 0000000..0de1364 --- /dev/null +++ b/src/test/java/com/patternpedia/api/integration/ImageAndDiscussionControllerTest.java @@ -0,0 +1,149 @@ +package com.patternpedia.api.integration; + +import com.patternpedia.api.entities.*; +import com.patternpedia.api.util.IntegrationTestHelper; +import org.json.JSONArray; +import org.json.JSONObject; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.http.HttpEntity; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringRunner; + +import java.net.URI; +import java.util.Base64; +import java.util.UUID; + + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT) +@AutoConfigureMockMvc +public class ImageAndDiscussionControllerTest extends IntegrationTest { + + @Autowired + private IntegrationTestHelper integrationTestHelper; + + @Before + public void cleanUpReposBefore() { + this.integrationTestHelper.cleanUpRepos(); + } + + @LocalServerPort + private int port; + + @Autowired + private TestRestTemplate restTemplate; + + @Test + public void testImagesAndDiscussions() throws Exception { + //Test add-image + Image image = this.integrationTestHelper.getDefaultImage(); + final String baseUrlAddImage = "http://localhost:"+port+"/add-image/"; + URI uriAddImage = new URI(baseUrlAddImage); + HttpEntity requestAddImage = new HttpEntity<>(image); + ResponseEntity resultAddImage = this.restTemplate.postForEntity(uriAddImage, requestAddImage, String.class); + Assert.assertEquals(201, resultAddImage.getStatusCodeValue()); + JSONObject jsonObjectAddImage = new JSONObject(resultAddImage.getBody()); + String imageId = jsonObjectAddImage.get("id").toString(); + + //Test get-image-by-id + final String baseUrlGetImage = "http://localhost:"+port+"/get-image-by-id/"+imageId; + URI uriGetImage = new URI(baseUrlGetImage); + ResponseEntity resultGetImage = this.restTemplate.getForEntity(uriGetImage, String.class); + Assert.assertEquals(200, resultGetImage.getStatusCodeValue()); + //deepcode ignore ReplaceBoxedConstructor~java.lang.String: String.valueOf and toString() doesnt work for byte[] + Assert.assertEquals(resultGetImage.getBody(), new String(image.getData())); + + //Test update-image + Image updatedImage = image; + updatedImage.setId(UUID.fromString(imageId)); + updatedImage.setFileName("TestFilenameUpdate"); + final String baseUrlUpdateImage = "http://localhost:"+port+"/update-image/" +imageId; + URI uriUpdateImage = new URI(baseUrlUpdateImage); + HttpEntity requestUpdateImage = new HttpEntity<>(updatedImage); + ResponseEntity resultUpdateImage = this.restTemplate.postForEntity(uriUpdateImage, requestUpdateImage, String.class); + Assert.assertEquals(200, resultUpdateImage.getStatusCodeValue()); + JSONObject jsonObjectUpdateImage = new JSONObject(resultUpdateImage.getBody()); + Assert.assertEquals(jsonObjectUpdateImage.get("fileName"), updatedImage.getFileName()); + + //Test add-topic + DiscussionTopic discussionTopic = this.integrationTestHelper.getDefaultDiscussionTopic(); + discussionTopic.setImageId(UUID.fromString(imageId)); + final String baseUrlAddTopic = "http://localhost:"+port+"/add-topic/"; + URI uriAddTopic = new URI(baseUrlAddTopic); + HttpEntity requestAddTopic = new HttpEntity<>(discussionTopic); + ResponseEntity resultAddTopic = this.restTemplate.postForEntity(uriAddTopic, requestAddTopic, String.class); + Assert.assertEquals(201, resultAddTopic.getStatusCodeValue()); + JSONObject jsonObjectAddTopic = new JSONObject(resultAddTopic.getBody()); + String topicId = jsonObjectAddTopic.get("id").toString(); + + //Test add-comment + DiscussionComment discussionComment = this.integrationTestHelper.getDefaultDiscussionComment(); + discussionComment.setDiscussionTopic(discussionTopic); + final String baseUrlAddComment = "http://localhost:"+port+"/add-comment/"+topicId; + URI uriAddComment = new URI(baseUrlAddComment); + HttpEntity requestAddComment = new HttpEntity<>(discussionComment); + ResponseEntity resultAddComment = this.restTemplate.postForEntity(uriAddComment, requestAddComment, String.class); + Assert.assertEquals(201, resultAddComment.getStatusCodeValue()); + JSONObject jsonObjectAddComment = new JSONObject(resultAddComment.getBody()); + String commentId = jsonObjectAddComment.get("id").toString(); + + //Test get-comments-by-topic + final String baseUrlGetCommentsByTopic = "http://localhost:"+port+"/get-comments-by-topic/"+ topicId; + URI uriGetCommentsByTopic = new URI(baseUrlGetCommentsByTopic); + ResponseEntity resultGetCommentsByTopic = this.restTemplate.getForEntity(uriGetCommentsByTopic, String.class); + Assert.assertEquals(200, resultGetCommentsByTopic.getStatusCodeValue()); + JSONArray jsonObjectGetCommentByTopic= new JSONArray(resultGetCommentsByTopic.getBody()); + String getCommentText = jsonObjectGetCommentByTopic.getJSONObject(0).get("text").toString(); + Assert.assertEquals(getCommentText, discussionComment.getText()); + + //Test get-image-and-comments-by-id + final String baseUrlGetImageAndCommentsById= "http://localhost:"+port+"/get-image-and-comments-by-id/"+ imageId; + URI uriGetImageAndCommentsById = new URI(baseUrlGetImageAndCommentsById); + ResponseEntity resultGetImageAndCommentsById = this.restTemplate.getForEntity(uriGetImageAndCommentsById, String.class); + Assert.assertEquals(200, resultGetImageAndCommentsById.getStatusCodeValue()); + JSONObject jsonObjectGetImageAndCommentsById= new JSONObject(resultGetImageAndCommentsById.getBody()); + // deepcode ignore ReplaceBoxedConstructor~java.lang.String: String.valueOf and toString() doesnt work for byte[] + String getImage = new String(Base64.getDecoder().decode(jsonObjectGetImageAndCommentsById.get("image").toString())); + Assert.assertTrue(getImage.contains(image.getFileType())); + String topicDescription = jsonObjectGetImageAndCommentsById.getJSONArray("topicModels").getJSONObject(0).getJSONObject("discussionTopic").get("description").toString(); + Assert.assertEquals(topicDescription, discussionTopic.getDescription()); + String commentText = jsonObjectGetImageAndCommentsById.getJSONArray("topicModels").getJSONObject(0).getJSONArray("discussionComments").getJSONObject(0).get("text").toString(); + Assert.assertEquals(commentText, discussionComment.getText()); + + //Test get-topic-by-image + final String baseUrlGetTopicByImage = "http://localhost:"+port+"/get-topic-by-image/"+ imageId; + URI uriGetTopicByImage = new URI(baseUrlGetTopicByImage); + ResponseEntity resultGetTopicByImage = this.restTemplate.getForEntity(uriGetTopicByImage, String.class); + Assert.assertEquals(200, resultGetTopicByImage.getStatusCodeValue()); + JSONArray jsonObjectGetTopicByImage= new JSONArray(resultGetTopicByImage.getBody()); + String getDescription = jsonObjectGetTopicByImage.getJSONObject(0).get("description").toString(); + Assert.assertEquals(getDescription, discussionTopic.getDescription()); + + //Test get-topic-and-comments-by-image + final String baseUrlGetTopicsAndCommentsByImage = "http://localhost:"+port+"/get-topics-and-comments-by-image/"+ imageId; + URI uriGetTopicsAndCommentsByImage = new URI(baseUrlGetTopicsAndCommentsByImage); + ResponseEntity resultGetTopicsAndCommentsByImage = this.restTemplate.getForEntity(uriGetTopicsAndCommentsByImage, String.class); + Assert.assertEquals(200, resultGetTopicsAndCommentsByImage.getStatusCodeValue()); + JSONArray jsonObjectGetTopicsAndCommentsByImage= new JSONArray(resultGetTopicsAndCommentsByImage.getBody()); + String description = jsonObjectGetTopicsAndCommentsByImage.getJSONObject(0).getJSONObject("discussionTopic").get("description").toString(); + Assert.assertEquals(description, discussionTopic.getDescription()); + String commentText1 = jsonObjectGetTopicsAndCommentsByImage.getJSONObject(0).getJSONArray("discussionComments").getJSONObject(0).get("text").toString(); + Assert.assertEquals(commentText1, discussionComment.getText()); + + //Test delete-topic + final String baseUrlDeleteTopic = "http://localhost:"+port+"/delete-topic/"+ topicId; + URI uriDeleteTopic= new URI(baseUrlDeleteTopic); + this.restTemplate.delete(uriDeleteTopic); + ResponseEntity resultGetCommentsByTopicAfterDelete = this.restTemplate.getForEntity(uriGetCommentsByTopic, String.class); + Assert.assertEquals(404, resultGetCommentsByTopicAfterDelete.getStatusCodeValue()); + } +} diff --git a/src/test/java/com/patternpedia/api/util/IntegrationTestHelper.java b/src/test/java/com/patternpedia/api/util/IntegrationTestHelper.java index 31f8ac9..96cf820 100644 --- a/src/test/java/com/patternpedia/api/util/IntegrationTestHelper.java +++ b/src/test/java/com/patternpedia/api/util/IntegrationTestHelper.java @@ -8,15 +8,7 @@ import java.util.UUID; import com.patternpedia.api.entities.*; -import com.patternpedia.api.repositories.DirectedEdgeRepository; -import com.patternpedia.api.repositories.PatternLanguageRepository; -import com.patternpedia.api.repositories.PatternRepository; -import com.patternpedia.api.repositories.PatternSchemaRepository; -import com.patternpedia.api.repositories.PatternSectionSchemaRepository; -import com.patternpedia.api.repositories.PatternViewDirectedEdgeRepository; -import com.patternpedia.api.repositories.PatternViewPatternRepository; -import com.patternpedia.api.repositories.PatternViewRepository; -import com.patternpedia.api.repositories.UndirectedEdgeReository; +import com.patternpedia.api.repositories.*; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -55,6 +47,13 @@ public class IntegrationTestHelper { private ObjectMapper objectMapper; @Autowired private MockMvc mockMvc; + @Autowired + private ImageRepository imageRepository; + @Autowired + private DiscussionCommentRepository discussionCommentRepository; + @Autowired + private DiscussionTopicRepository discussionTopicRepository; + public void cleanUpRepos() { System.out.println("Cleaning up..."); @@ -85,6 +84,15 @@ public void cleanUpRepos() { this.patternLanguageRepository.deleteAll(); System.out.println("Cleaned up patternLanguageRepository"); + + this.imageRepository.deleteAll(); + System.out.println("Cleaned up imageRepository"); + + this.discussionCommentRepository.deleteAll(); + System.out.println("Cleaned up discussionCommentRepository"); + + this.discussionTopicRepository.deleteAll(); + System.out.println("Cleaned up discussionTopicRepository"); } public ObjectNode getDefaultPatternContent() { @@ -217,4 +225,31 @@ public Pattern addPatternToLanguage(PatternLanguage patternLanguage) throws Exce return this.objectMapper.readValue(getPatternResponse.getResponse().getContentAsByteArray(), Pattern.class); } + + public Image getDefaultImage(){ + Image image = new Image(); + image.setId(UUID.randomUUID()); + image.setFileName("testImage"); + image.setFileType("testType"); + image.setData("testFile".getBytes()); + return image; + } + + public DiscussionTopic getDefaultDiscussionTopic(){ + DiscussionTopic discussionTopic = new DiscussionTopic(); + discussionTopic.setId(UUID.randomUUID()); + discussionTopic.setTitle("TestTopic"); + discussionTopic.setDescription("Testdescription"); + discussionTopic.setImageId(this.getDefaultImage().getId()); + return discussionTopic; + } + + public DiscussionComment getDefaultDiscussionComment(){ + DiscussionComment discussionComment = new DiscussionComment(); + discussionComment.setId(UUID.randomUUID()); + discussionComment.setText("TestText"); + discussionComment.setDiscussionTopic(this.getDefaultDiscussionTopic()); + return discussionComment; + } + }