Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TRIB-289: Refactors AttributeAPI applyPhraseToUser endpoint and all related services to use a DTO #290

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
241de52
TRIB-289: adds AttributesApplyPhraseToUserDTO
mrsbluerose Oct 2, 2024
badf924
TRIB-289: refactors AttributesApplyPhraseToUserDTO
mrsbluerose Oct 2, 2024
3978b2b
TRIB-289: refactors return for PhraseService apply
mrsbluerose Oct 2, 2024
c4b6eb6
TRIB-289: refactors AttAPI applyPhrase return
mrsbluerose Oct 2, 2024
dfa3844
TRIB-289: refactors isPhraseValid back to boolean
mrsbluerose Oct 3, 2024
8b7bb84
TRIB-289: refactors PhraseServiceImplTest with DTO
mrsbluerose Oct 3, 2024
ea1682b
TRIB-289: removes outdated comments from tests
mrsbluerose Oct 3, 2024
1a5b26c
TRIB-289: removes test context from db migration
mrsbluerose Oct 3, 2024
7ae20a3
TRIB-289: updates notification_type table
mrsbluerose Oct 3, 2024
68bc69e
Revert "TRIB-289: removes test context from db migration"
mrsbluerose Oct 3, 2024
56f78b4
TRIB-289: adds in review to NotificationType ent
mrsbluerose Oct 3, 2024
3f219e1
TRIB-289: adds DTO creator to PhraseService intfc
mrsbluerose Oct 3, 2024
65dc7c7
TRIB-289: refactors AttributesAPI apply phrase
mrsbluerose Oct 3, 2024
0982ae1
TRIB-289: removes unused import
mrsbluerose Oct 3, 2024
2184847
TRIB-289: refactors AttributesAPIIT tests
mrsbluerose Oct 3, 2024
ad43785
TRIB-289: refactors PhraseService apply phrase
mrsbluerose Oct 3, 2024
f98973c
TRIB-289: adds PhraseServiceImpl tests for dto
mrsbluerose Oct 3, 2024
0321f8f
TRIB-289: adds PhraseService test for attDTO
mrsbluerose Oct 3, 2024
e96401b
TRIB-289: corrects typo in test names
mrsbluerose Oct 3, 2024
622248a
TRIB-289: updates Swagger doc for AttributesAPI
mrsbluerose Oct 3, 2024
697e4ae
TRIB-289: adds AttributesApplyPhraseToUserDTO
mrsbluerose Oct 2, 2024
18ce0c7
TRIB-289: refactors AttributesApplyPhraseToUserDTO
mrsbluerose Oct 2, 2024
80719b7
TRIB-289: refactors return for PhraseService apply
mrsbluerose Oct 2, 2024
c3b1a71
TRIB-289: refactors AttAPI applyPhrase return
mrsbluerose Oct 2, 2024
786d2df
TRIB-289: refactors isPhraseValid back to boolean
mrsbluerose Oct 3, 2024
ceb7a76
TRIB-289: refactors PhraseServiceImplTest with DTO
mrsbluerose Oct 3, 2024
b394717
TRIB-289: removes outdated comments from tests
mrsbluerose Oct 3, 2024
a81bd09
TRIB-289: removes test context from db migration
mrsbluerose Oct 3, 2024
b7c4e50
TRIB-289: updates notification_type table
mrsbluerose Oct 3, 2024
0f61014
Revert "TRIB-289: removes test context from db migration"
mrsbluerose Oct 3, 2024
bd3afb5
TRIB-289: adds in review to NotificationType ent
mrsbluerose Oct 3, 2024
7ee477f
TRIB-289: adds DTO creator to PhraseService intfc
mrsbluerose Oct 3, 2024
eadb52c
TRIB-289: refactors AttributesAPI apply phrase
mrsbluerose Oct 3, 2024
6997a3e
TRIB-289: removes unused import
mrsbluerose Oct 3, 2024
79cd0a0
TRIB-289: refactors AttributesAPIIT tests
mrsbluerose Oct 3, 2024
9f32531
TRIB-289: refactors PhraseService apply phrase
mrsbluerose Oct 3, 2024
4d73f2c
TRIB-289: adds PhraseServiceImpl tests for dto
mrsbluerose Oct 3, 2024
5dd59cd
TRIB-289: adds PhraseService test for attDTO
mrsbluerose Oct 3, 2024
98bbe7c
TRIB-289: corrects typo in test names
mrsbluerose Oct 3, 2024
65d603b
TRIB-289: updates Swagger doc for AttributesAPI
mrsbluerose Oct 3, 2024
87087c5
Merge branch 'TRIB-289' of github.com:mrsbluerose/tribe-app-backend i…
mrsbluerose Oct 4, 2024
f83d623
TRIB-289: refactors NotificationType type ids
mrsbluerose Oct 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import com.savvato.tribeapp.controllers.annotations.controllers.AttributesAPIController.GetUserPhrasesToBeReviewed;
import com.savvato.tribeapp.controllers.dto.AttributesRequest;
import com.savvato.tribeapp.dto.AttributeDTO;
import com.savvato.tribeapp.dto.GenericResponseDTO;
import com.savvato.tribeapp.dto.AttributesApplyPhraseToUserDTO;
import com.savvato.tribeapp.dto.ToBeReviewedDTO;
import com.savvato.tribeapp.entities.NotificationType;
import com.savvato.tribeapp.services.*;
Expand Down Expand Up @@ -71,25 +71,18 @@ public ResponseEntity<List<ToBeReviewedDTO>> getUserPhrasesToBeReviewed(

@ApplyPhraseToUser
@PostMapping
public ResponseEntity<GenericResponseDTO> applyPhraseToUser(@RequestBody @Valid AttributesRequest req) {
if (phraseService.isPhraseValid(req.adverb, req.verb, req.preposition, req.noun)) {
boolean isPhraseApplied =
phraseService.applyPhraseToUser(
req.userId, req.adverb, req.verb, req.preposition, req.noun);
if (isPhraseApplied) {
sendNotification(true, req.userId);
GenericResponseDTO rtn = GenericResponseService.createDTO("true");
return ResponseEntity.status(HttpStatus.OK).body(rtn);
} else {
sendNotification(false, req.userId);
GenericResponseDTO rtn = GenericResponseService.createDTO("false");
return ResponseEntity.status(HttpStatus.OK).body(rtn);
}
} else {
sendNotification(false, req.userId);
GenericResponseDTO rtn = GenericResponseService.createDTO("false");
public ResponseEntity<AttributesApplyPhraseToUserDTO> applyPhraseToUser(@RequestBody @Valid AttributesRequest req) {

if (!phraseService.isPhraseValid(req.adverb, req.verb, req.preposition, req.noun)) {
AttributesApplyPhraseToUserDTO rtn = phraseService.constructAttributesApplyPhraseToUserDTO(false, false, true, false);
sendNotification(rtn, req.userId);
return ResponseEntity.status(HttpStatus.OK).body(rtn);
}

AttributesApplyPhraseToUserDTO rtn = phraseService.applyPhraseToUser(req.userId, req.adverb, req.verb, req.preposition, req.noun);
sendNotification(rtn, req.userId);

return ResponseEntity.status(HttpStatus.OK).body(rtn);
}

///api/attributes/?phraseId=xx&userId=xx
Expand All @@ -100,15 +93,18 @@ public ResponseEntity deletePhraseFromUser(@Parameter(description = "Phrase ID o
return ResponseEntity.ok().build();
}

private void sendNotification(Boolean approved, Long userId) {
if (approved) {
private void sendNotification(AttributesApplyPhraseToUserDTO dto, Long userId) {
if (dto.isSuccess && dto.isApproved) {
notificationService.createNotification(
NotificationType.ATTRIBUTE_REQUEST_APPROVED,
userId,
NotificationType.ATTRIBUTE_REQUEST_APPROVED.getName(),
"Your attribute has been approved!");

} else {
} else if (dto.isInReview) {
notificationService.createNotification(NotificationType.ATTRIBUTE_REQUEST_IN_REVIEW, userId, NotificationType.ATTRIBUTE_REQUEST_IN_REVIEW.getName(), "Your attribute will be reviewed.");

} else if (dto.isRejected) {
notificationService.createNotification(
NotificationType.ATTRIBUTE_REQUEST_REJECTED,
userId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import com.savvato.tribeapp.controllers.annotations.requests.DocumentedRequestBody;
import com.savvato.tribeapp.controllers.annotations.responses.Success;
import com.savvato.tribeapp.controllers.dto.AttributesRequest;
import com.savvato.tribeapp.dto.AttributesApplyPhraseToUserDTO;
import com.savvato.tribeapp.dto.ToBeReviewedDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import java.lang.annotation.*;
Expand All @@ -16,10 +18,5 @@
description = "Provided a valid AttributesRequest (see schemas), apply phrase to user.")
@DocumentedRequestBody(implementation = AttributesRequest.class)
@Success(
description = "Successfully applied phrase",
examples = {
@ExampleObject(name = "Phrase applied successfully", value = "true"),
@ExampleObject(name = "Failed to apply phrase", value = "false"),
@ExampleObject(name = "Phrase was invalid", value = "false")
})
description = "Successfully applied phrase", implementation = AttributesApplyPhraseToUserDTO.class)
public @interface ApplyPhraseToUser {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.savvato.tribeapp.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;

@Builder
@Schema(description = "Result of applying phrase to user")
public class AttributesApplyPhraseToUserDTO {

@Schema(example = "true")
public boolean isSuccess;

@Schema(example = "true")
public boolean isApproved;

@Schema(example = "true")
public boolean isRejected;

@Schema(example = "true")
public boolean isInReview;

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
public class NotificationType {

public static final NotificationType ATTRIBUTE_REQUEST_APPROVED = new NotificationType(1L, "Attribute request approved", null);
public static final NotificationType ATTRIBUTE_REQUEST_REJECTED = new NotificationType(1L, "Attribute request rejected", null);
public static final NotificationType ATTRIBUTE_REQUEST_REJECTED = new NotificationType(2L, "Attribute request rejected", null);
public static final NotificationType ATTRIBUTE_REQUEST_IN_REVIEW = new NotificationType(3L, "Attribute request in review", null);

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.savvato.tribeapp.services;

import com.savvato.tribeapp.dto.PhraseDTO;
import com.savvato.tribeapp.dto.AttributesApplyPhraseToUserDTO;

import java.util.Map;
import java.util.Optional;
Expand All @@ -9,9 +10,11 @@ public interface PhraseService {

boolean isPhraseValid(String adverb, String verb, String preposition, String noun);

boolean applyPhraseToUser(Long userId, String adverb, String verb, String preposition, String noun);
AttributesApplyPhraseToUserDTO applyPhraseToUser(Long userId, String adverb, String verb, String preposition, String noun);

Optional<Long> findPreviouslyApprovedPhraseId(String adverb, String verb, String preposition, String noun);

Optional<Map<PhraseDTO, Integer>> getPhraseInformationByUserId(Long userId);

AttributesApplyPhraseToUserDTO constructAttributesApplyPhraseToUserDTO(boolean success, boolean approved, boolean rejected, boolean inReview);
}
19 changes: 16 additions & 3 deletions src/main/java/com/savvato/tribeapp/services/PhraseServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.savvato.tribeapp.constants.Constants;
import com.savvato.tribeapp.dto.PhraseDTO;
import com.savvato.tribeapp.dto.projections.PhraseWithUserCountDTO;
import com.savvato.tribeapp.dto.AttributesApplyPhraseToUserDTO;
import com.savvato.tribeapp.entities.*;
import com.savvato.tribeapp.repositories.*;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -128,7 +129,7 @@ public boolean isPhrasePreviouslyRejected(String adverb, String verb, String pre
}

@Override
public boolean applyPhraseToUser(Long userId, String adverb, String verb, String preposition, String noun) {
public AttributesApplyPhraseToUserDTO applyPhraseToUser(Long userId, String adverb, String verb, String preposition, String noun) {

String adverbLowerCase = adverb.isBlank() ? Constants.NULL_VALUE_WORD : changeToLowerCase(adverb);
String verbLowerCase = changeToLowerCase(verb);
Expand All @@ -144,7 +145,8 @@ public boolean applyPhraseToUser(Long userId, String adverb, String verb, String
userPhraseRepository.save(userPhrase);
log.info("Phrase added to user " + userId);

return true;
return constructAttributesApplyPhraseToUserDTO(true,true,false, false);

} else {
Optional<ToBeReviewed> toBeReviewedPhrase = toBeReviewedRepository.findByAdverbAndVerbAndNounAndPreposition(adverbLowerCase, verbLowerCase, nounLowerCase, prepositionLowerCase);

Expand All @@ -166,7 +168,7 @@ public boolean applyPhraseToUser(Long userId, String adverb, String verb, String
log.info("ToBeReviewed phrase has been mapped to user " + userId);
}

return false;
return constructAttributesApplyPhraseToUserDTO(true,false,false,true);
}
}

Expand Down Expand Up @@ -277,4 +279,15 @@ public PhraseDTO constructPhraseDTOFromPhraseInformation(Long phraseId, Long adv
.build();
}

@Override
public AttributesApplyPhraseToUserDTO constructAttributesApplyPhraseToUserDTO(boolean success, boolean approved, boolean rejected, boolean inReview){
AttributesApplyPhraseToUserDTO rtn = AttributesApplyPhraseToUserDTO.builder()
.isSuccess(success)
.isApproved(approved)
.isRejected(rejected)
.isInReview(inReview)
.build();
return rtn;
}

}
16 changes: 16 additions & 0 deletions src/main/resources/db/migration/changelog-202410031119.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">

<changeSet author="audra" id="202410031119-01">
<insert tableName="notification_type">
<column name="id" value="3"/>
<column name="name" value="Attribute request in review"/>
<column name="icon_url" value="hourglass-outline"/>
</insert>
</changeSet>

</databaseChangeLog>
1 change: 1 addition & 0 deletions src/main/resources/db/migration/changelog-master.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
<include file="changelog-202401111012.xml" relativeToChangelogFile="true"/>
<include file="changelog-202401090508.xml" relativeToChangelogFile="true"/>
<include file="changelog-202403270537.xml" relativeToChangelogFile="true"/>
<include file="changelog-202410031119.xml" relativeToChangelogFile="true"/>

</databaseChangeLog>

Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@
import com.savvato.tribeapp.constants.UserTestConstants;
import com.savvato.tribeapp.controllers.AttributesAPIController;
import com.savvato.tribeapp.controllers.dto.AttributesRequest;
import com.savvato.tribeapp.dto.AttributeDTO;
import com.savvato.tribeapp.dto.PhraseDTO;
import com.savvato.tribeapp.dto.ToBeReviewedDTO;
import com.savvato.tribeapp.dto.GenericResponseDTO;
import com.savvato.tribeapp.dto.*;
import com.savvato.tribeapp.entities.NotificationType;
import com.savvato.tribeapp.entities.User;
import com.savvato.tribeapp.services.*;
Expand Down Expand Up @@ -156,27 +153,31 @@ public void applyPhraseToUserWhenPhraseValidAndApplicable() throws Exception {
attributesRequest.noun = NOUN1_WORD;
attributesRequest.preposition = PREPOSITION1_WORD;
String notificationContent = "Your attribute has been approved!";

AttributesApplyPhraseToUserDTO expectedDTO = AttributesApplyPhraseToUserDTO
.builder()
.isSuccess(true)
.isApproved(true)
.isRejected(false)
.isInReview(false)
.build();

when(phraseService.isPhraseValid(anyString(), anyString(), anyString(), anyString()))
.thenReturn(true);
when(phraseService.applyPhraseToUser(
anyLong(), anyString(), anyString(), anyString(), anyString()))
.thenReturn(true);
.thenReturn(expectedDTO);
when(notificationService.createNotification(
any(NotificationType.class), anyLong(), anyString(), anyString()))
.thenReturn(null);
when(GenericResponseService.createDTO(
anyString()))
.thenReturn(GenericResponseDTO.builder()
.responseMessage("true")
.build());

ArgumentCaptor<NotificationType> notificationTypeCaptor =
ArgumentCaptor.forClass(NotificationType.class);
ArgumentCaptor<Long> userIdCaptor = ArgumentCaptor.forClass(Long.class);
ArgumentCaptor<String> notificationTypeNameCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<String> notificationContentCaptor = ArgumentCaptor.forClass(String.class);

String template = "{\"responseMessage\": \"%s\"}";
String expectedMessage = String.format(template, "true");
String expectedMessage = "{\"isSuccess\": true, \"isApproved\": true, \"isRejected\": false, \"isInReview\": false}";

this.mockMvc
.perform(
Expand Down Expand Up @@ -217,29 +218,32 @@ public void applyPhraseToUserWhenPhraseValidButNotApplied() throws Exception {
attributesRequest.noun = NOUN1_WORD;
attributesRequest.preposition = PREPOSITION1_WORD;

AttributesApplyPhraseToUserDTO expectedDTO = AttributesApplyPhraseToUserDTO
.builder()
.isSuccess(true)
.isApproved(false)
.isRejected(false)
.isInReview(true)
.build();

when(phraseService.isPhraseValid(anyString(), anyString(), anyString(), anyString()))
.thenReturn(true);
when(phraseService.applyPhraseToUser(
anyLong(), anyString(), anyString(), anyString(), anyString()))
.thenReturn(false);
.thenReturn(expectedDTO);
when(notificationService.createNotification(
any(NotificationType.class), anyLong(), anyString(), anyString()))
.thenReturn(null);
when(GenericResponseService.createDTO(
anyString()))
.thenReturn(GenericResponseDTO.builder()
.responseMessage("false")
.build());

ArgumentCaptor<NotificationType> notificationTypeCaptor =
ArgumentCaptor.forClass(NotificationType.class);
ArgumentCaptor<Long> userIdCaptor = ArgumentCaptor.forClass(Long.class);
ArgumentCaptor<String> notificationTypeNameCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<String> notificationContentCaptor = ArgumentCaptor.forClass(String.class);
String notificationContent =
"Your attribute was rejected. This attribute is unsuitable and cannot be applied to users.";
"Your attribute will be reviewed.";

String template = "{\"responseMessage\": \"%s\"}";
String expectedMessage = String.format(template, "false");
String expectedMessage = "{\"isSuccess\": true, \"isApproved\": false, \"isRejected\": false, \"isInReview\": true}";

this.mockMvc
.perform(
Expand All @@ -258,11 +262,11 @@ public void applyPhraseToUserWhenPhraseValidButNotApplied() throws Exception {
userIdCaptor.capture(),
notificationTypeNameCaptor.capture(),
notificationContentCaptor.capture());
assertThat(notificationTypeCaptor.getValue()).usingRecursiveComparison().isEqualTo(NotificationType.ATTRIBUTE_REQUEST_REJECTED);
assertThat(notificationTypeCaptor.getValue()).usingRecursiveComparison().isEqualTo(NotificationType.ATTRIBUTE_REQUEST_IN_REVIEW);
assertEquals(userIdCaptor.getValue(), userId);
assertEquals(
notificationTypeNameCaptor.getValue(),
NotificationType.ATTRIBUTE_REQUEST_REJECTED.getName());
NotificationType.ATTRIBUTE_REQUEST_IN_REVIEW.getName());
assertEquals(notificationContentCaptor.getValue(), notificationContent);
}

Expand All @@ -280,16 +284,21 @@ public void applyPhraseToUserWhenPhraseInvalid() throws Exception {
attributesRequest.noun = NOUN1_WORD;
attributesRequest.preposition = PREPOSITION1_WORD;

AttributesApplyPhraseToUserDTO expectedDTO = AttributesApplyPhraseToUserDTO
.builder()
.isSuccess(false)
.isApproved(false)
.isRejected(true)
.isInReview(false)
.build();

when(phraseService.isPhraseValid(anyString(), anyString(), anyString(), anyString()))
.thenReturn(false);
when((phraseService.constructAttributesApplyPhraseToUserDTO(anyBoolean(),anyBoolean(),anyBoolean(),anyBoolean()))).thenReturn(expectedDTO);
when(notificationService.createNotification(
any(NotificationType.class), anyLong(), anyString(), anyString()))
.thenReturn(null);
when(GenericResponseService.createDTO(
anyString()))
.thenReturn(GenericResponseDTO.builder()
.responseMessage("false")
.build());

ArgumentCaptor<NotificationType> notificationTypeCaptor =
ArgumentCaptor.forClass(NotificationType.class);
ArgumentCaptor<Long> userIdCaptor = ArgumentCaptor.forClass(Long.class);
Expand All @@ -298,8 +307,7 @@ public void applyPhraseToUserWhenPhraseInvalid() throws Exception {
String notificationContent =
"Your attribute was rejected. This attribute is unsuitable and cannot be applied to users.";

String template = "{\"responseMessage\": \"%s\"}";
String expectedMessage = String.format(template, "false");
String expectedMessage = "{\"isSuccess\": false, \"isApproved\": false, \"isRejected\": true, \"isInReview\": false}";

this.mockMvc
.perform(
Expand Down
Loading