Skip to content

Commit 47449f3

Browse files
committed
Merge branch 'develop' into feature/notifications-page
2 parents 56fb37b + 28636c3 commit 47449f3

23 files changed

+596
-150
lines changed

.github/codecov.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
codecov:
2+
branch: develop
3+
coverage:
4+
status:
5+
project:
6+
default:
7+
# basic
8+
target: 75%
9+
threshold: 0%
10+
base: auto
11+
paths:
12+
- "src"

.github/workflows/codecov-publish.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: CodeCov Coverage Collection
2+
3+
on:
4+
push:
5+
branches:
6+
- "develop"
7+
8+
jobs:
9+
coverage:
10+
runs-on: ubuntu-latest
11+
permissions:
12+
contents: read
13+
packages: write
14+
steps:
15+
- uses: actions/checkout@v3
16+
- name: Set up JDK 19
17+
uses: actions/setup-java@v3
18+
with:
19+
java-version: '19'
20+
distribution: 'temurin'
21+
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
22+
settings-path: ${{ github.workspace }} # location for the settings.xml file
23+
- name: Install dependencies
24+
run: mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V
25+
- name: Run tests and collect coverage
26+
run: mvn -B test
27+
- name: Upload coverage to Codecov
28+
uses: codecov/codecov-action@v3

README

Lines changed: 0 additions & 11 deletions
This file was deleted.

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[![codecov](https://codecov.io/gh/savvato-software/tribe-app-backend/graph/badge.svg?token=3ZHKR797YM)](https://codecov.io/gh/savvato-software/tribe-app-backend)
2+
3+
=Tribe App Backend
4+
5+
This is the backend to the wonderful Tribe App.
6+
7+
Let's see, what can I tell you about it..
8+
9+
It is collaboratively produced by a bunch of people who are learning to become better developers by taking tasks,
10+
researching and completing them, just as they would in a real job. This is called the Mock Programming Job. You can find
11+
more about the DMPJ at our meetup page: https://www.meetup.com/denver-mock-programming-job-meetup/
12+
13+
info@savvato.com
Lines changed: 79 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,110 @@
11
package com.savvato.tribeapp.controllers;
22

33
import com.savvato.tribeapp.controllers.annotations.controllers.AttributesAPIController.ApplyPhraseToUser;
4+
import com.savvato.tribeapp.controllers.annotations.controllers.AttributesAPIController.DeletePhraseFromUser;
45
import com.savvato.tribeapp.controllers.annotations.controllers.AttributesAPIController.GetAttributesForUser;
6+
import com.savvato.tribeapp.controllers.annotations.controllers.AttributesAPIController.GetUserPhrasesToBeReviewed;
57
import com.savvato.tribeapp.controllers.dto.AttributesRequest;
68
import com.savvato.tribeapp.dto.AttributeDTO;
9+
import com.savvato.tribeapp.dto.ToBeReviewedDTO;
710
import com.savvato.tribeapp.entities.NotificationType;
8-
import com.savvato.tribeapp.services.AttributesService;
9-
import com.savvato.tribeapp.services.NotificationService;
10-
import com.savvato.tribeapp.services.PhraseService;
11+
import com.savvato.tribeapp.services.*;
1112
import io.swagger.v3.oas.annotations.Parameter;
1213
import io.swagger.v3.oas.annotations.tags.Tag;
14+
import java.util.List;
15+
import java.util.Optional;
16+
import javax.validation.Valid;
1317
import org.springframework.beans.factory.annotation.Autowired;
1418
import org.springframework.http.HttpStatus;
1519
import org.springframework.http.ResponseEntity;
1620
import org.springframework.web.bind.annotation.*;
1721

18-
import javax.validation.Valid;
19-
import java.util.List;
20-
import java.util.Optional;
21-
2222
@RestController
2323
@RequestMapping("/api/attributes")
2424
@Tag(
25-
name = "attributes",
26-
description = "Everything about attributes, e.g. \"plays chess competitively\"")
25+
name = "attributes",
26+
description = "Everything about attributes, e.g. \"plays chess competitively\"")
2727
public class AttributesAPIController {
2828

29-
@Autowired
30-
AttributesService attributesService;
29+
@Autowired
30+
AttributesService attributesService;
3131

32-
@Autowired
33-
PhraseService phraseService;
32+
@Autowired
33+
PhraseService phraseService;
3434

35-
@Autowired
36-
NotificationService notificationService;
35+
@Autowired
36+
NotificationService notificationService;
3737

38-
AttributesAPIController() {
39-
}
38+
@Autowired
39+
UserPhraseService userPhraseService;
40+
41+
@Autowired
42+
ReviewSubmittingUserService reviewSubmittingUserService;
43+
44+
AttributesAPIController() {}
4045

41-
@GetAttributesForUser
42-
@GetMapping("/{userId}")
43-
public ResponseEntity<List<AttributeDTO>> getAttributesForUser(
44-
@Parameter(description = "User ID of user", example = "1") @PathVariable Long userId) {
46+
@GetAttributesForUser
47+
@GetMapping("/{userId}")
48+
public ResponseEntity<List<AttributeDTO>> getAttributesForUser(
49+
@Parameter(description = "User ID of user", example = "1") @PathVariable Long userId) {
4550

46-
Optional<List<AttributeDTO>> opt = attributesService.getAttributesByUserId(userId);
51+
Optional<List<AttributeDTO>> opt = attributesService.getAttributesByUserId(userId);
4752

48-
if (opt.isPresent()) return ResponseEntity.status(HttpStatus.OK).body(opt.get());
49-
else return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
53+
if (opt.isPresent()) return ResponseEntity.status(HttpStatus.OK).body(opt.get());
54+
else return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
55+
}
56+
57+
@GetUserPhrasesToBeReviewed
58+
@GetMapping("/in-review/{userId}")
59+
public ResponseEntity<List<ToBeReviewedDTO>> getUserPhrasesToBeReviewed(
60+
@Parameter(description = "User ID of user", example = "1")
61+
@PathVariable Long userId) {
62+
List<ToBeReviewedDTO> rtn = reviewSubmittingUserService.getUserPhrasesToBeReviewed(userId);
63+
return ResponseEntity.status(HttpStatus.OK).body(rtn);
5064
}
5165

52-
@ApplyPhraseToUser
53-
@PostMapping
54-
public ResponseEntity<Boolean> applyPhraseToUser(@RequestBody @Valid AttributesRequest req) {
55-
if (phraseService.isPhraseValid(req.adverb, req.verb, req.preposition, req.noun)) {
56-
boolean isPhraseApplied =
57-
phraseService.applyPhraseToUser(
58-
req.userId, req.adverb, req.verb, req.preposition, req.noun);
59-
if (isPhraseApplied) {
60-
sendNotification(true, req.userId);
61-
return ResponseEntity.status(HttpStatus.OK).body(true);
62-
} else {
63-
sendNotification(false, req.userId);
64-
return ResponseEntity.status(HttpStatus.OK).body(false);
65-
}
66-
} else {
67-
sendNotification(false, req.userId);
68-
return ResponseEntity.status(HttpStatus.OK).body(false);
69-
}
66+
@ApplyPhraseToUser
67+
@PostMapping
68+
public ResponseEntity<Boolean> applyPhraseToUser(@RequestBody @Valid AttributesRequest req) {
69+
if (phraseService.isPhraseValid(req.adverb, req.verb, req.preposition, req.noun)) {
70+
boolean isPhraseApplied =
71+
phraseService.applyPhraseToUser(
72+
req.userId, req.adverb, req.verb, req.preposition, req.noun);
73+
if (isPhraseApplied) {
74+
sendNotification(true, req.userId);
75+
return ResponseEntity.status(HttpStatus.OK).body(true);
76+
} else {
77+
sendNotification(false, req.userId);
78+
return ResponseEntity.status(HttpStatus.OK).body(false);
79+
}
80+
} else {
81+
sendNotification(false, req.userId);
82+
return ResponseEntity.status(HttpStatus.OK).body(false);
7083
}
84+
}
85+
86+
///api/attributes/?phraseId=xx&userId=xx
87+
@DeletePhraseFromUser
88+
@DeleteMapping
89+
public ResponseEntity deletePhraseFromUser(@Parameter(description = "Phrase ID of phrase", example = "1") @RequestParam("phraseId") Long phraseId, @Parameter(description = "User ID of user", example = "1") @RequestParam("userId") Long userId) {
90+
userPhraseService.deletePhraseFromUser(phraseId, userId);
91+
return ResponseEntity.ok().build();
92+
}
93+
94+
private void sendNotification(Boolean approved, Long userId) {
95+
if (approved) {
96+
notificationService.createNotification(
97+
NotificationType.ATTRIBUTE_REQUEST_APPROVED,
98+
userId,
99+
NotificationType.ATTRIBUTE_REQUEST_APPROVED.getName(),
100+
"Your attribute has been approved!");
71101

72-
private void sendNotification(Boolean approved, Long userId) {
73-
if (approved) {
74-
notificationService.createNotification(
75-
NotificationType.ATTRIBUTE_REQUEST_APPROVED,
76-
userId,
77-
NotificationType.ATTRIBUTE_REQUEST_APPROVED.getName(),
78-
"Your attribute has been approved!");
79-
} else {
80-
notificationService.createNotification(
81-
NotificationType.ATTRIBUTE_REQUEST_REJECTED,
82-
userId,
83-
NotificationType.ATTRIBUTE_REQUEST_REJECTED.getName(),
84-
"Your attribute was rejected. This attribute is unsuitable and cannot be applied to users.");
85-
86-
}
102+
} else {
103+
notificationService.createNotification(
104+
NotificationType.ATTRIBUTE_REQUEST_REJECTED,
105+
userId,
106+
NotificationType.ATTRIBUTE_REQUEST_REJECTED.getName(),
107+
"Your attribute was rejected. This attribute is unsuitable and cannot be applied to users.");
87108
}
109+
}
88110
}
Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package com.savvato.tribeapp.controllers;
22

3+
import com.savvato.tribeapp.config.principal.UserPrincipal;
34
import com.savvato.tribeapp.controllers.annotations.controllers.ConnectAPIController.Connect;
45
import com.savvato.tribeapp.controllers.annotations.controllers.ConnectAPIController.GetQRCodeString;
56
import com.savvato.tribeapp.controllers.dto.ConnectRequest;
67
import com.savvato.tribeapp.dto.ConnectIncomingMessageDTO;
78
import com.savvato.tribeapp.services.ConnectService;
89
import io.swagger.v3.oas.annotations.Parameter;
910
import io.swagger.v3.oas.annotations.tags.Tag;
11+
import java.util.Optional;
12+
import javax.validation.Valid;
1013
import org.springframework.beans.factory.annotation.Autowired;
1114
import org.springframework.http.HttpStatus;
1215
import org.springframework.http.ResponseEntity;
@@ -15,49 +18,48 @@
1518
import org.springframework.messaging.handler.annotation.Payload;
1619
import org.springframework.web.bind.annotation.*;
1720

18-
import javax.validation.Valid;
19-
import java.util.Optional;
20-
2121
@RestController
2222
@Tag(name = "connect", description = "Connections between users")
2323
@RequestMapping("/api/connect")
2424
public class ConnectAPIController {
25-
@Autowired
26-
ConnectService connectService;
25+
@Autowired ConnectService connectService;
2726

28-
ConnectAPIController() {
29-
}
27+
ConnectAPIController() {}
3028

31-
@GetQRCodeString
32-
@GetMapping("/{userId}")
33-
public ResponseEntity getQrCodeString(
34-
@Parameter(description = "The user ID of a user", example = "1") @PathVariable Long userId) {
29+
@GetQRCodeString
30+
@GetMapping("/{userId}")
31+
public ResponseEntity getQrCodeString(
32+
@Parameter(description = "The user ID of a user", example = "1") @PathVariable Long userId) {
3533

36-
Optional<String> opt = connectService.storeQRCodeString(userId);
34+
Optional<String> opt = connectService.storeQRCodeString(userId);
3735

38-
if (opt.isPresent()) {
39-
return ResponseEntity.status(HttpStatus.OK).body(opt.get());
40-
} else {
41-
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
42-
}
36+
if (opt.isPresent()) {
37+
return ResponseEntity.status(HttpStatus.OK).body(opt.get());
38+
} else {
39+
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
4340
}
41+
}
4442

45-
@Connect
46-
@PostMapping
47-
public boolean connect(@Valid @RequestBody ConnectRequest connectRequest) {
48-
if (connectService.validateQRCode(
49-
connectRequest.qrcodePhrase, connectRequest.toBeConnectedWithUserId)) {
50-
boolean isConnectionSaved =
51-
connectService.saveConnectionDetails(
52-
connectRequest.requestingUserId, connectRequest.toBeConnectedWithUserId);
53-
return isConnectionSaved;
54-
} else {
55-
return false;
56-
}
43+
@Connect
44+
@PostMapping
45+
public boolean connect(@RequestBody @Valid ConnectRequest connectRequest) {
46+
if (connectService.validateQRCode(
47+
connectRequest.qrcodePhrase, connectRequest.toBeConnectedWithUserId)) {
48+
boolean isConnectionSaved =
49+
connectService.saveConnectionDetails(
50+
connectRequest.requestingUserId, connectRequest.toBeConnectedWithUserId);
51+
if (isConnectionSaved) {
52+
return true;
53+
} else {
54+
return false;
55+
}
56+
} else {
57+
return false;
5758
}
59+
}
5860

59-
@MessageMapping("/connect/room")
60-
public void connect(@Payload ConnectIncomingMessageDTO incoming, @Header("simpSessionId") String sessionId) {
61-
connectService.connect(incoming);
62-
}
61+
@MessageMapping("/connect/room")
62+
public void connect(@Payload ConnectIncomingMessageDTO incoming, @Header("simpSessionId") String sessionId) {
63+
connectService.connect(incoming);
64+
}
6365
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.savvato.tribeapp.controllers.annotations.controllers.AttributesAPIController;
2+
3+
import com.savvato.tribeapp.controllers.annotations.responses.Success;
4+
import io.swagger.v3.oas.annotations.Operation;
5+
6+
import java.lang.annotation.*;
7+
8+
/** Documentation for deleting a phrase association for a user */
9+
@Target({ElementType.METHOD, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
10+
@Retention(RetentionPolicy.RUNTIME)
11+
@Documented
12+
@Operation(
13+
summary = "Delete association of this phrase from this user.",
14+
description = "Provide a phrase ID, delete UserPhrase record from user_phrase table.")
15+
@Success(
16+
description = "Successfully deleted UserPhrase.", noContent = true)
17+
public @interface DeletePhraseFromUser {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.savvato.tribeapp.controllers.annotations.controllers.AttributesAPIController;
2+
3+
import com.savvato.tribeapp.controllers.annotations.responses.BadRequest;
4+
import com.savvato.tribeapp.controllers.annotations.responses.Success;
5+
import com.savvato.tribeapp.dto.ToBeReviewedDTO;
6+
import io.swagger.v3.oas.annotations.Operation;
7+
import io.swagger.v3.oas.annotations.media.ArraySchema;
8+
import io.swagger.v3.oas.annotations.media.Schema;
9+
import java.lang.annotation.*;
10+
11+
/** Documentation for getting a user's to be reviewed phrases */
12+
@Target({ElementType.METHOD, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
13+
@Retention(RetentionPolicy.RUNTIME)
14+
@Documented
15+
@Operation(
16+
summary = "Get phrases to be reviewed for a user",
17+
description = "Provided a valid user ID, get phrases to be reviewed for that user.")
18+
@Success(
19+
description = "Successfully retrieved user to be reviewed phrases",
20+
array = @ArraySchema(schema = @Schema(implementation = ToBeReviewedDTO.class)))
21+
@BadRequest(description = "Failed to retrieve to be reviewed phrases for user.", noContent = true)
22+
public @interface GetUserPhrasesToBeReviewed {}

src/main/java/com/savvato/tribeapp/dto/PhraseDTO.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
@Schema(description = "A phrase DTO")
88
public class PhraseDTO {
99

10+
@Schema(example = "1")
11+
public long id;
12+
1013
@Schema(example = "enthusiastically")
1114
public String adverb;
1215

src/main/java/com/savvato/tribeapp/dto/ToBeReviewedDTO.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
@Builder
77
public class ToBeReviewedDTO {
88

9+
@Schema(example = "1")
10+
public Long id;
11+
912
@Schema(implementation = Boolean.class, example = "false")
1013
public Boolean hasBeenGroomed;
1114

0 commit comments

Comments
 (0)