Skip to content

Commit 12f4cea

Browse files
authored
Merge pull request #1263 from fnkbsi/MailUserAtTransactionStop
Mail user on TransactionStop and SuspendedEV event
2 parents 93fa59a + 4ba01b0 commit 12f4cea

21 files changed

+493
-12
lines changed

src/main/java/de/rwth/idsg/steve/NotificationFeature.java

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@
2121
import lombok.Getter;
2222
import lombok.RequiredArgsConstructor;
2323

24+
import java.util.Arrays;
25+
import java.util.List;
26+
import java.util.stream.Collectors;
27+
28+
import static de.rwth.idsg.steve.utils.StringUtils.joinByComma;
29+
import static de.rwth.idsg.steve.utils.StringUtils.splitByComma;
30+
2431
/**
2532
* @author Sevket Goekay <[email protected]>
2633
* @since 22.01.2016
@@ -30,17 +37,31 @@ public enum NotificationFeature {
3037

3138
// Ocpp related
3239
//
33-
OcppStationBooted(" a charging station sends a boot notification (Note: This activates notifications about failed connection attempts for unregistered JSON stations, as well)"),
34-
OcppStationStatusFailure(" a connector gets faulted"),
35-
OcppStationWebSocketConnected(" a JSON charging station connects"),
36-
OcppStationWebSocketDisconnected(" a JSON charging station disconnects"),
37-
OcppTransactionStarted(" a charging station starts a transaction"),
38-
OcppTransactionEnded(" a charging station ends a transaction");
40+
OcppStationBooted(false, " a charging station sends a boot notification (Note: This activates notifications about failed connection attempts for unregistered JSON stations, as well)"),
41+
OcppStationStatusFailure(true, " a connector gets faulted"),
42+
OcppStationWebSocketConnected(false, " a JSON charging station connects"),
43+
OcppStationWebSocketDisconnected(false, " a JSON charging station disconnects"),
44+
OcppTransactionStarted(true, " a charging station starts a transaction"),
45+
OcppStationStatusSuspendedEV(true, " a EV suspended charging"),
46+
OcppTransactionEnded(true, " a charging station ends a transaction");
3947

48+
private static final List<NotificationFeature> userNotificationFeatures = Arrays.stream(NotificationFeature.values())
49+
.filter(NotificationFeature::isForUser)
50+
.collect(Collectors.toList());
51+
52+
/**
53+
* Whether this notification type is intended for end-users as well.
54+
*/
55+
@Getter
56+
private final boolean forUser;
4057

4158
@Getter
4259
private final String text;
4360

61+
public static List<NotificationFeature> getUserValues() {
62+
return userNotificationFeatures;
63+
}
64+
4465
public static NotificationFeature fromName(String v) {
4566
for (NotificationFeature c: NotificationFeature.values()) {
4667
if (c.name().equalsIgnoreCase(v)) {
@@ -49,4 +70,14 @@ public static NotificationFeature fromName(String v) {
4970
}
5071
throw new IllegalArgumentException(v);
5172
}
73+
74+
public static List<NotificationFeature> splitFeatures(String str) {
75+
return splitByComma(str).stream()
76+
.map(NotificationFeature::fromName)
77+
.collect(Collectors.toList());
78+
}
79+
80+
public static String joinFeatures(List<NotificationFeature> enablesFeatures) {
81+
return joinByComma(enablesFeatures);
82+
}
5283
}

src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import org.springframework.format.support.FormattingConversionService;
4444
import org.springframework.http.converter.HttpMessageConverter;
4545
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
46+
import org.springframework.scheduling.annotation.EnableAsync;
4647
import org.springframework.scheduling.annotation.EnableScheduling;
4748
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
4849
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
@@ -67,6 +68,7 @@
6768
@Configuration
6869
@EnableWebMvc
6970
@EnableScheduling
71+
@EnableAsync
7072
@ComponentScan("de.rwth.idsg.steve")
7173
public class BeanConfiguration implements WebMvcConfigurer {
7274

src/main/java/de/rwth/idsg/steve/repository/dto/InsertTransactionParams.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import lombok.Builder;
2222
import lombok.Getter;
23+
import lombok.ToString;
2324
import org.joda.time.DateTime;
2425

2526
/**
@@ -28,6 +29,7 @@
2829
*/
2930
@Getter
3031
@Builder
32+
@ToString
3133
public class InsertTransactionParams {
3234
private final String chargeBoxId;
3335
private final int connectorId;

src/main/java/de/rwth/idsg/steve/repository/dto/UpdateTransactionParams.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import jooq.steve.db.enums.TransactionStopEventActor;
2222
import lombok.Builder;
2323
import lombok.Getter;
24+
import lombok.ToString;
2425
import org.joda.time.DateTime;
2526

2627
/**
@@ -29,6 +30,7 @@
2930
*/
3031
@Getter
3132
@Builder
33+
@ToString
3234
public class UpdateTransactionParams {
3335
private final String chargeBoxId;
3436
private final int transactionId;

src/main/java/de/rwth/idsg/steve/repository/dto/User.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919
package de.rwth.idsg.steve.repository.dto;
2020

21+
import de.rwth.idsg.steve.NotificationFeature;
2122
import jooq.steve.db.tables.records.AddressRecord;
2223
import jooq.steve.db.tables.records.UserRecord;
2324
import lombok.Builder;
@@ -38,6 +39,7 @@ public static final class Overview {
3839
private final Integer userPk;
3940
private final String name, phone, email;
4041
private final List<OcppTagEntry> ocppTagEntries;
42+
private final List<NotificationFeature> notificationFeatures;
4143
}
4244

4345
@Getter

src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,10 @@ private List<Condition> getConditions(TransactionQueryForm form) {
289289
conditions.add(CONNECTOR.CHARGE_BOX_ID.eq(form.getChargeBoxId()));
290290
}
291291

292+
if (form.isConnectorIdSet()) {
293+
conditions.add(CONNECTOR.CONNECTOR_ID.eq(form.getConnectorId()));
294+
}
295+
292296
if (form.isOcppIdTagSet()) {
293297
conditions.add(TRANSACTION.ID_TAG.eq(form.getOcppIdTag()));
294298
}

src/main/java/de/rwth/idsg/steve/repository/impl/UserRepositoryImpl.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
package de.rwth.idsg.steve.repository.impl;
2020

2121
import com.google.common.base.Strings;
22+
import de.rwth.idsg.steve.NotificationFeature;
2223
import de.rwth.idsg.steve.SteveException;
2324
import de.rwth.idsg.steve.repository.AddressRepository;
2425
import de.rwth.idsg.steve.repository.UserRepository;
@@ -32,7 +33,7 @@
3233
import org.jooq.DSLContext;
3334
import org.jooq.Field;
3435
import org.jooq.Record1;
35-
import org.jooq.Record5;
36+
import org.jooq.Record6;
3637
import org.jooq.Result;
3738
import org.jooq.SelectConditionStep;
3839
import org.jooq.exception.DataAccessException;
@@ -77,6 +78,7 @@ public List<User.Overview> getOverview(UserQueryForm form) {
7778
.phone(r.value4())
7879
.email(r.value5())
7980
.ocppTagEntries(tags)
81+
.notificationFeatures(NotificationFeature.splitFeatures(r.value6()))
8082
.build();
8183

8284
// TODO: Improve later. This is not efficient, because we filter after fetching all results. However, this
@@ -165,7 +167,7 @@ public void delete(int userPk) {
165167
// Private helpers
166168
// -------------------------------------------------------------------------
167169

168-
private Result<Record5<Integer, String, String, String, String>> getOverviewInternal(UserQueryForm form) {
170+
private Result<Record6<Integer, String, String, String, String, String>> getOverviewInternal(UserQueryForm form) {
169171
List<Condition> conditions = new ArrayList<>();
170172

171173
if (form.isSetUserPk()) {
@@ -200,7 +202,8 @@ private Result<Record5<Integer, String, String, String, String>> getOverviewInte
200202
USER.FIRST_NAME,
201203
USER.LAST_NAME,
202204
USER.PHONE,
203-
USER.E_MAIL)
205+
USER.E_MAIL,
206+
USER.NOTIFICATION_FEATURES)
204207
.from(USER)
205208
.where(conditions)
206209
.fetch();
@@ -257,6 +260,7 @@ private Integer addInternal(DSLContext ctx, UserForm form, Integer addressPk) {
257260
.set(USER.E_MAIL, form.getEMail())
258261
.set(USER.NOTE, form.getNote())
259262
.set(USER.ADDRESS_PK, addressPk)
263+
.set(USER.NOTIFICATION_FEATURES, NotificationFeature.joinFeatures(form.getNotificationFeatures()))
260264
.returning(USER.USER_PK)
261265
.fetchOne()
262266
.getUserPk();
@@ -275,6 +279,7 @@ private void updateInternal(DSLContext ctx, UserForm form, Integer addressPk) {
275279
.set(USER.E_MAIL, form.getEMail())
276280
.set(USER.NOTE, form.getNote())
277281
.set(USER.ADDRESS_PK, addressPk)
282+
.set(USER.NOTIFICATION_FEATURES, NotificationFeature.joinFeatures(form.getNotificationFeatures()))
278283
.where(USER.USER_PK.eq(form.getUserPk()))
279284
.execute();
280285
}

src/main/java/de/rwth/idsg/steve/service/CentralSystemService16_Service.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import de.rwth.idsg.steve.repository.dto.UpdateTransactionParams;
2828
import de.rwth.idsg.steve.service.notification.OccpStationBooted;
2929
import de.rwth.idsg.steve.service.notification.OcppStationStatusFailure;
30+
import de.rwth.idsg.steve.service.notification.OcppStationStatusSuspendedEV;
3031
import de.rwth.idsg.steve.service.notification.OcppTransactionEnded;
3132
import de.rwth.idsg.steve.service.notification.OcppTransactionStarted;
3233
import jooq.steve.db.enums.TransactionStopEventActor;
@@ -147,6 +148,11 @@ public StatusNotificationResponse statusNotification(
147148
chargeBoxIdentity, parameters.getConnectorId(), parameters.getErrorCode().value()));
148149
}
149150

151+
if (parameters.getStatus() == ChargePointStatus.SUSPENDED_EV) {
152+
applicationEventPublisher.publishEvent(new OcppStationStatusSuspendedEV(
153+
chargeBoxIdentity, parameters.getConnectorId(), parameters.getTimestamp()));
154+
}
155+
150156
return new StatusNotificationResponse();
151157
}
152158

src/main/java/de/rwth/idsg/steve/service/MailService.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222

2323
import jakarta.mail.MessagingException;
2424

25+
import java.util.List;
26+
2527
/**
2628
* @author Sevket Goekay <[email protected]>
2729
* @since 11.03.2025
@@ -35,4 +37,6 @@ public interface MailService {
3537
void sendAsync(String subject, String body);
3638

3739
void send(String subject, String body) throws MessagingException;
40+
41+
void send(String subject, String body, List<String> eMailAddresses);
3842
}

src/main/java/de/rwth/idsg/steve/service/MailServiceDefault.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@
3636
import jakarta.mail.internet.InternetAddress;
3737
import jakarta.mail.internet.MimeMessage;
3838

39+
import java.util.List;
3940
import java.util.Properties;
41+
import java.util.function.Function;
4042

4143
/**
4244
* @author Sevket Goekay <[email protected]>
@@ -77,6 +79,19 @@ public void sendAsync(String subject, String body) {
7779

7880
@Override
7981
public void send(String subject, String body) throws MessagingException {
82+
sendInternal(subject, body, MailSettings::getRecipients);
83+
}
84+
85+
@Override
86+
public void send(String subject, String body, List<String> eMailAddresses) {
87+
try {
88+
sendInternal(subject, body, mailSettings -> eMailAddresses);
89+
} catch (MessagingException e) {
90+
log.error("Failed to send mail", e);
91+
}
92+
}
93+
94+
public void sendInternal(String subject, String body, Function<MailSettings, List<String>> emailAddressProvider) throws MessagingException {
8095
MailSettings settings = getSettings();
8196
Session session = createSession(settings);
8297

@@ -85,7 +100,7 @@ public void send(String subject, String body) throws MessagingException {
85100
mail.setContent(body, "text/plain");
86101
mail.setFrom(new InternetAddress(settings.getFrom()));
87102

88-
for (String rep : settings.getRecipients()) {
103+
for (String rep : emailAddressProvider.apply(settings)) {
89104
mail.addRecipient(Message.RecipientType.TO, new InternetAddress(rep));
90105
}
91106

0 commit comments

Comments
 (0)