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

248 notify user by email #258

Open
wants to merge 44 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
6d3abfa
create mail service #248
iam-flo Feb 17, 2025
e794be4
create settings endpoint #248
iam-flo Feb 24, 2025
56062d0
add mail template #248
iam-flo Feb 24, 2025
116445c
chore: update API specs and client
github-actions[bot] Feb 24, 2025
a77adf5
create settings page #248
iam-flo Feb 25, 2025
63eeec2
update mail config #248
iam-flo Feb 25, 2025
c4036b4
update mail config #258
iam-flo Mar 3, 2025
e231a50
update compose #258
iam-flo Mar 3, 2025
e242479
change to debug workflow
FelixTJDietrich Mar 3, 2025
7fce13e
update compose #258
iam-flo Mar 3, 2025
730131f
update compose #258
iam-flo Mar 3, 2025
9e90aaf
update compose #258
iam-flo Mar 3, 2025
db382a6
update compose #258
iam-flo Mar 3, 2025
72f4116
update compose #258
iam-flo Mar 3, 2025
42bb7e6
update formatting #258
iam-flo Mar 3, 2025
6d5dd87
update compose #258
iam-flo Mar 3, 2025
dce0c3f
update workflow #258
iam-flo Mar 3, 2025
09f9bba
update user service #258
iam-flo Mar 3, 2025
d70e4e9
update mail builder #258
iam-flo Mar 3, 2025
14f4b53
update mail #258
iam-flo Mar 4, 2025
378bdfb
update workflow #258
iam-flo Mar 4, 2025
db27ec6
update detect task #258
iam-flo Mar 4, 2025
ccba2aa
update mail #258
iam-flo Mar 4, 2025
b20b939
update keycloak #258
iam-flo Mar 7, 2025
866a24e
update keycloak #258
iam-flo Mar 9, 2025
2375275
update keycloak #258
iam-flo Mar 10, 2025
df4c151
update compose #258
iam-flo Mar 10, 2025
b58372b
update compose #258
iam-flo Mar 10, 2025
c4b9838
update mail service #258
iam-flo Mar 11, 2025
7a14e0b
update mail service #258
iam-flo Mar 11, 2025
26fc418
update mail service #258
iam-flo Mar 11, 2025
52cdbc1
update after prettier #258
iam-flo Mar 11, 2025
db83491
update config #258
iam-flo Mar 11, 2025
d20a027
update config #258
iam-flo Mar 11, 2025
c396d16
chore: update API specs and client
github-actions[bot] Mar 11, 2025
709c1ed
update config #258
iam-flo Mar 11, 2025
3942922
update config #258
iam-flo Mar 11, 2025
1b68704
update config #258
iam-flo Mar 11, 2025
d4692a3
update config #258
iam-flo Mar 11, 2025
50b9c94
update config #258
iam-flo Mar 11, 2025
0155d7b
update config #258
iam-flo Mar 11, 2025
5efb688
create settings page #248
iam-flo Mar 11, 2025
11963ae
update migration #258
iam-flo Mar 11, 2025
e9ba682
update after prettier #258
iam-flo Mar 11, 2025
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
2 changes: 2 additions & 0 deletions docker/compose.app.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ services:
LEADERBOARD_SCHEDULE_DAY: ${LEADERBOARD_SCHEDULE_DAY}
LEADERBOARD_SCHEDULE_TIME: ${LEADERBOARD_SCHEDULE_TIME}
SENTRY_DSN: ${SENTRY_DSN}
POSTFIX_HOST: postfix
POSTFIX_PORT: 25
depends_on:
- postgres
restart: unless-stopped
Expand Down
42 changes: 40 additions & 2 deletions docker/compose.core.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,16 @@ services:
timeout: 20s
retries: 10

postfix:
image: ghcr.io/ls1admin/postfix:latest
container_name: hephaestus-postfix
restart: unless-stopped
volumes:
- ./postfix-config:/config # See https://github.com/ls1admin/postfix-container-tum-mailrelay/tree/main for details
hostname: aet.cit.tum.de
networks:
- shared-network

networks:
shared-network:
name: shared-network
Expand Down Expand Up @@ -362,14 +372,18 @@ configs:
"description": "Administrator privileges",
"composites": {
"realm": [
"mentor_access"
"mentor_access",
"notification_access"
]
}
},
{
"name": "mentor_access",
"description": "Access to AI Mentor"
},
{ "name": "notification_access",
"description": "Get notification emails"
},
{
"name": "offline_access",
"description": "${role_offline-access}",
Expand Down Expand Up @@ -407,5 +421,29 @@ configs:
"description": "${role_default-roles}",
"composite": true,
"clientRole": false
}
},
"users": [
{
"id": "d7b8cea0-f9f6-43df-8a27-eb9a18fb87cb",
"username": "service-account-hephaestus-confidential",
"emailVerified": false,
"createdTimestamp": 1741338840502,
"enabled": true,
"totp": false,
"serviceAccountClientId": "hephaestus-confidential",
"disableableCredentialTypes": [],
"requiredActions": [],
"realmRoles": [
"default-roles-hephaestus"
],
"clientRoles": {
"realm-management": [
"view-users",
"query-users",
"manage-users"
]
},
"notBefore": 0,
"groups": []
}]
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,15 @@
"containerId": "69732d45-1431-4909-9335-9f39e304f823",
"attributes": {}
},
{
"id": "af3b75a2-9733-44e5-bdc3-1e3e32459743",
"name": "notification_access",
"description": "",
"composite": false,
"clientRole": false,
"containerId": "69732d45-1431-4909-9335-9f39e304f823",
"attributes": {}
},
{
"id": "a0bcdf44-1bd1-4b3d-8667-021f2bc8802b",
"name": "uma_authorization",
Expand All @@ -97,7 +106,8 @@
"composite": true,
"composites": {
"realm": [
"mentor_access"
"mentor_access",
"notification_access"
]
},
"clientRole": false,
Expand Down Expand Up @@ -457,6 +467,29 @@
"webAuthnPolicyPasswordlessAcceptableAaguids": [],
"webAuthnPolicyPasswordlessExtraOrigins": [],
"users": [
{
"id": "d7b8cea0-f9f6-43df-8a27-eb9a18fb87cb",
"username": "service-account-hephaestus-confidential",
"emailVerified": false,
"createdTimestamp": 1741338840502,
"enabled": true,
"totp": false,
"serviceAccountClientId": "hephaestus-confidential",
"disableableCredentialTypes": [],
"requiredActions": [],
"realmRoles": [
"default-roles-hephaestus"
],
"clientRoles": {
"realm-management": [
"view-users",
"query-users",
"manage-users"
]
},
"notBefore": 0,
"groups": []
},
{
"id": "9d394368-150d-4559-b005-758a50cbb353",
"username": "admin",
Expand Down
34 changes: 34 additions & 0 deletions server/application-server/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,35 @@ paths:
responses:
"200":
description: OK
/user/settings:
get:
tags:
- user
operationId: getUserSettings
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: "#/components/schemas/UserSettings"
post:
tags:
- user
operationId: updateUserSettings
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/UserSettings"
required: true
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: "#/components/schemas/UserSettings"
/mentor/sessions:
get:
tags:
Expand Down Expand Up @@ -696,6 +725,11 @@ components:
- id
- name
- nameWithOwner
UserSettings:
type: object
properties:
receiveNotifications:
type: boolean
UserTeams:
type: object
properties:
Expand Down
8 changes: 8 additions & 0 deletions server/application-server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,14 @@
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
import de.tum.in.www1.hephaestus.gitprovider.issue.Issue;
import de.tum.in.www1.hephaestus.gitprovider.pullrequest.PullRequest;
import de.tum.in.www1.hephaestus.gitprovider.pullrequest.PullRequestRepository;
import de.tum.in.www1.hephaestus.gitprovider.user.UserRepository;
import jakarta.transaction.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
Expand All @@ -31,9 +29,6 @@ public class ActivityService {
@Autowired
private PullRequestBadPracticeDetector pullRequestBadPracticeDetector;

@Autowired
private UserRepository userRepository;

public ActivityDTO getActivity(String login) {
logger.info("Getting activity for user with login: {}", login);

Expand Down Expand Up @@ -95,6 +90,7 @@ public List<PullRequestBadPracticeDTO> detectBadPracticesForPullRequest(Long pul
List<PullRequestBadPractice> detectedBadPractices = pullRequestBadPracticeDetector.detectAndSyncBadPractices(
pullRequest
);

return detectedBadPractices.stream().map(PullRequestBadPracticeDTO::fromPullRequestBadPractice).toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

import de.tum.in.www1.hephaestus.gitprovider.label.Label;
import de.tum.in.www1.hephaestus.gitprovider.pullrequest.PullRequest;
import de.tum.in.www1.hephaestus.notification.MailService;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
Expand All @@ -11,12 +14,20 @@
@Component
public class BadPracticeDetectorScheduler {

private static final Logger logger = LoggerFactory.getLogger(BadPracticeDetectorScheduler.class);

private static final String READY_TO_MERGE = "ready to merge";

@Qualifier("applicationTaskExecutor")
@Autowired
TaskExecutor taskExecutor;

@Autowired
PullRequestBadPracticeDetector pullRequestBadPracticeDetector;

@Autowired
MailService mailService;

public void detectBadPracticeForPrIfReadyToMerge(
PullRequest pullRequest,
Set<Label> oldLabels,
Expand All @@ -26,7 +37,13 @@ public void detectBadPracticeForPrIfReadyToMerge(
newLabels.stream().anyMatch(label -> READY_TO_MERGE.equals(label.getName())) &&
oldLabels.stream().noneMatch(label -> READY_TO_MERGE.equals(label.getName()))
) {
logger.info(
"Scheduling bad practice detection for pull request because it is ready to merge: {}",
pullRequest.getId()
);
BadPracticeDetectorTask badPracticeDetectorTask = new BadPracticeDetectorTask();
badPracticeDetectorTask.setPullRequestBadPracticeDetector(pullRequestBadPracticeDetector);
badPracticeDetectorTask.setMailService(mailService);
badPracticeDetectorTask.setPullRequest(pullRequest);
taskExecutor.execute(badPracticeDetectorTask);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,38 @@
package de.tum.in.www1.hephaestus.activity.badpracticedetector;

import de.tum.in.www1.hephaestus.activity.model.PullRequestBadPractice;
import de.tum.in.www1.hephaestus.gitprovider.pullrequest.PullRequest;
import de.tum.in.www1.hephaestus.gitprovider.user.User;
import de.tum.in.www1.hephaestus.notification.MailService;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
@Getter
@Setter
public class BadPracticeDetectorTask implements Runnable {

@Autowired
private PullRequestBadPracticeDetector pullRequestBadPracticeDetector;

private MailService mailService;

private PullRequest pullRequest;

@Override
public void run() {
pullRequestBadPracticeDetector.detectAndSyncBadPractices(pullRequest);
List<PullRequestBadPractice> badPractices = pullRequestBadPracticeDetector.detectAndSyncBadPractices(
pullRequest
);

List<PullRequestBadPractice> unResolvedBadPractices = badPractices
.stream()
.filter(badPractice -> !badPractice.isResolved())
.toList();

if (!unResolvedBadPractices.isEmpty()) {
for (User user : pullRequest.getAssignees()) {
mailService.sendBadPracticesDetectedInPullRequestEmail(user, pullRequest, badPractices);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ public class User extends BaseGitServiceEntity {
@ToString.Exclude
private Set<PullRequestReviewComment> reviewComments = new HashSet<>();

@NonNull
private boolean notificationsEnabled = false;

// Current ranking points for the leaderboard leagues
private int leaguePoints;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

Expand All @@ -27,6 +29,9 @@ public class UserController {
@Autowired
private Keycloak keycloak;

@Autowired
private UserRepository userRepository;

@Value("${keycloak.realm}")
private String realm;

Expand Down Expand Up @@ -56,4 +61,26 @@ public ResponseEntity<Void> deleteUser(JwtAuthenticationToken auth) {
}
return ResponseEntity.ok().build();
}

@GetMapping("/settings")
public ResponseEntity<UserSettingsDTO> getUserSettings() {
var user = userRepository.getCurrentUser();
if (user.isEmpty()) {
return ResponseEntity.notFound().build();
}

UserSettingsDTO userSettings = userService.getUserSettings(user.get());
return ResponseEntity.ok(userSettings);
}

@PostMapping("/settings")
public ResponseEntity<UserSettingsDTO> updateUserSettings(@RequestBody UserSettingsDTO userSettings) {
var user = userRepository.getCurrentUser();
if (user.isEmpty()) {
return ResponseEntity.notFound().build();
}

UserSettingsDTO updatedUserSettings = userService.updateUserSettings(user.get(), userSettings);
return ResponseEntity.ok(updatedUserSettings);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,16 @@ public Optional<UserProfileDTO> getUserProfile(String login) {
new UserProfileDTO(user, firstContribution, contributedRepositories, reviewActivity, openPullRequests)
);
}

public UserSettingsDTO getUserSettings(User user) {
logger.info("Getting user settings with userId: " + user);
return new UserSettingsDTO(user.isNotificationsEnabled());
}

public UserSettingsDTO updateUserSettings(User user, UserSettingsDTO userSettings) {
logger.info("Updating user settings with userId: " + user);
user.setNotificationsEnabled(userSettings.receiveNotifications());
userRepository.save(user);
return new UserSettingsDTO(user.isNotificationsEnabled());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package de.tum.in.www1.hephaestus.gitprovider.user;

public record UserSettingsDTO(boolean receiveNotifications) {}
Loading