Skip to content

Commit 0c9959f

Browse files
authored
Merge branch 'main' into feature/my-checklists-backend
2 parents 9ce6bc1 + 1f37517 commit 0c9959f

File tree

11 files changed

+169
-49
lines changed

11 files changed

+169
-49
lines changed

ticketing-eventing/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Open **Event-Service** via Swagger (http://localhost:8080/swagger-ui/index.html)
2424
- User: zammad
2525
- Password: password
2626
- Method: POST `/api/event`
27-
- X-Zammad-Trigger: `Trigger Name`
27+
- X-Zammad-Trigger: `T2805_Event_Nachricht_In_Postkorb`
2828
- X-Zammad-Delivery: `myID`
2929
- Request Body:
3030
```json
@@ -33,7 +33,7 @@ Open **Event-Service** via Swagger (http://localhost:8080/swagger-ui/index.html)
3333
"status": "closed",
3434
"status_id": "1",
3535
"anliegenart": "technischer Bürgersupport",
36-
"lhmExtId": "33caabe6-317c-4c2d-8bf7-6c36230599db"
36+
"lhmextid": "33caabe6-317c-4c2d-8bf7-6c36230599db"
3737
}
3838
```
3939

ticketing-eventing/eventing-service/src/main/resources/application-local.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,7 @@ logging:
3737
dbs:
3838
eventing:
3939
trigger-mapping:
40-
- trigger-name: "Trigger Name"
40+
- trigger-name: "T2800_Event_Statusaenderung"
4141
action: "state_changed"
42+
- trigger-name: "T2805_Event_Nachricht_In_Postkorb"
43+
action: "send_to_postbox"

ticketing-eventing/mail-handler-service/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343

4444
<!-- Core Frameworks -->
4545
<spring-cloud-dependencies.version>2025.0.0</spring-cloud-dependencies.version> <!-- Must match the chosen Spring Boot version -->
46-
<dbs-handler-core.version>0.1.0</dbs-handler-core.version>
46+
<dbs-handler-core.version>0.2.0</dbs-handler-core.version>
4747

4848
<!-- Logging -->
4949
<logstash-logback-encoder.version>8.1</logstash-logback-encoder.version>
Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,59 @@
11
package de.muenchen.oss.dbs.ticketing.eventing.mailhandler.adapter.out.mail;
22

33
import de.muenchen.oss.dbs.ticketing.eventing.mailhandler.application.port.out.SendMailOutPort;
4+
import jakarta.activation.DataSource;
45
import jakarta.mail.MessagingException;
56
import jakarta.mail.internet.MimeMessage;
7+
import jakarta.mail.util.ByteArrayDataSource;
8+
import java.io.IOException;
9+
import java.io.InputStream;
10+
import java.util.Map;
611
import lombok.RequiredArgsConstructor;
12+
import lombok.extern.slf4j.Slf4j;
713
import org.springframework.mail.javamail.JavaMailSender;
814
import org.springframework.mail.javamail.MimeMessageHelper;
915
import org.springframework.stereotype.Service;
1016

1117
@Service
1218
@RequiredArgsConstructor
19+
@Slf4j
1320
public class MailAdapter implements SendMailOutPort {
1421
private final JavaMailSender mailSender;
1522
private final MailProperties mailProperties;
1623

1724
@Override
18-
public void sendMail(final String recipient, final String subject, final String body) {
25+
public void sendMail(final MailMessage mailMessage) {
1926
final MimeMessage mimeMessage = mailSender.createMimeMessage();
20-
final MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, "utf-8");
2127
try {
28+
final MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "utf-8");
2229
helper.setFrom(mailProperties.getFromAddress());
23-
helper.setTo(recipient);
24-
helper.setSubject(subject);
25-
helper.setText(body, true);
30+
helper.setTo(mailMessage.getRecipient());
31+
helper.setSubject(mailMessage.getSubject());
32+
helper.setText(mailMessage.getBody(), true);
33+
for (final Map.Entry<String, InputStream> entry : mailMessage.getAttachments().entrySet()) {
34+
//TODO is there a way to do this with streaming? The following line loads the attachment into RAM...
35+
final DataSource dataSource = new ByteArrayDataSource(entry.getValue(), "application/octet-stream");
36+
helper.addAttachment(entry.getKey(), dataSource);
37+
//maybe like this?
38+
//helper.addAttachment(e.getKey(), new InputStreamSourceImpl(e.getValue()));
39+
}
2640
mailSender.send(mimeMessage);
27-
} catch (MessagingException e) {
41+
} catch (final MessagingException | IOException e) {
2842
throw new RuntimeException(e);
2943
}
3044
}
45+
46+
// private class InputStreamSourceImpl implements InputStreamSource {
47+
// private final InputStream inputStream;
48+
//
49+
// public InputStreamSourceImpl(InputStream inputStream) {
50+
// this.inputStream = inputStream;
51+
// }
52+
//
53+
// @Override
54+
// public InputStream getInputStream() {
55+
// return inputStream;
56+
// }
57+
// }
58+
3159
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package de.muenchen.oss.dbs.ticketing.eventing.mailhandler.adapter.out.mail;
2+
3+
import java.io.InputStream;
4+
import java.util.Map;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Data;
7+
8+
@Data
9+
@AllArgsConstructor
10+
public class MailMessage {
11+
private String recipient;
12+
private String subject;
13+
private String body;
14+
private Map<String, InputStream> attachments;
15+
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package de.muenchen.oss.dbs.ticketing.eventing.mailhandler.application.port.out;
22

3+
import de.muenchen.oss.dbs.ticketing.eventing.mailhandler.adapter.out.mail.MailMessage;
4+
35
public interface SendMailOutPort {
4-
void sendMail(String recipient, String subject, String body);
6+
void sendMail(MailMessage mailMessage);
57
}

ticketing-eventing/mail-handler-service/src/main/java/de/muenchen/oss/dbs/ticketing/eventing/mailhandler/application/usecase/EventHandlingUseCase.java

Lines changed: 91 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
package de.muenchen.oss.dbs.ticketing.eventing.mailhandler.application.usecase;
22

33
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
4+
import de.muenchen.oss.dbs.ticketing.eai.client.model.ArticleAttachment;
45
import de.muenchen.oss.dbs.ticketing.eai.client.model.ArticleInternal;
56
import de.muenchen.oss.dbs.ticketing.eai.client.model.TicketInternal;
7+
import de.muenchen.oss.dbs.ticketing.eai.client.model.UpdateTicketDTO;
68
import de.muenchen.oss.dbs.ticketing.eventing.handlercore.application.port.in.EventHandlerInPort;
79
import de.muenchen.oss.dbs.ticketing.eventing.handlercore.application.port.out.TicketingOutPort;
810
import de.muenchen.oss.dbs.ticketing.eventing.handlercore.domain.model.Event;
11+
import de.muenchen.oss.dbs.ticketing.eventing.mailhandler.adapter.out.mail.MailMessage;
912
import de.muenchen.oss.dbs.ticketing.eventing.mailhandler.application.port.out.SendMailOutPort;
1013
import de.muenchen.oss.dbs.ticketing.eventing.mailhandler.config.MailHandlerProperties;
14+
import de.muenchen.oss.dbs.ticketing.eventing.mailhandler.exceptions.NoValidArticleException;
1115
import java.io.IOException;
1216
import java.io.InputStream;
17+
import java.util.HashMap;
1318
import java.util.Map;
1419
import java.util.stream.Collectors;
1520
import lombok.RequiredArgsConstructor;
@@ -22,6 +27,12 @@
2227
public class EventHandlingUseCase implements EventHandlerInPort {
2328
private static final String INTERNAL_ATTACHMENTS_ARTICLE_TITLE = "Interner Artikel für interne Anhänge.";
2429
private static final String FORM_ATTACHMENT_NAME = "XML-Daten.xml";
30+
public static final String TICKETING_VERTRAUENSNIVEAU = "ticketingVertrauensniveau";
31+
public static final String LEGACY_POSTKORB_HANDLE = "legacyPostkorbHandle";
32+
public static final String ACCOUNT_SOURCE = "accountSource";
33+
34+
public static final String TO_POSTBOX_DEFAULT = "send";
35+
public static final String TO_POSTBOX_HIGH = "send_high_authLevel";
2536

2637
private final XmlMapper xmlMapper = new XmlMapper();
2738
private final MailHandlerProperties mailHandlerProperties;
@@ -36,52 +47,94 @@ public void handleEvent(final Event event) {
3647
return;
3748
}
3849
log.info("Handling event");
39-
// find event ticket
40-
final TicketInternal ticket = ticketingOutPort.getTicket(event.ticket());
41-
// get parsed form
42-
final Map<String, Object> form = getParsedForm(ticket);
43-
// send mail
44-
final String subject = buildSubject(ticket, form);
45-
final String body = buildBody(ticket);
46-
sendMailOutport.sendMail(
47-
mailHandlerProperties.getRecipient(), subject, body);
48-
log.info("Handled event successfully");
50+
try {
51+
// find event ticket
52+
final TicketInternal ticket = ticketingOutPort.getTicket(event.ticket());
53+
//check if ticket should be sent
54+
if (!isRelevantTicket(ticket)) {
55+
log.debug("Ticket not relevant");
56+
return;
57+
}
58+
// get parsed form
59+
final Map<String, Object> form = getParsedForm(ticket);
60+
//find relevant article
61+
final ArticleInternal article = findRelevantArticle(ticket);
62+
sendMail(ticket, form, article);
63+
64+
//reset flag sende_nachricht_nach_extern
65+
resetTicket(ticket);
66+
67+
log.info("Event handled successfully");
68+
} catch (NoValidArticleException e) {
69+
log.error(e.getMessage());
70+
log.error("Event NOT handled successfully");
71+
}
4972
}
5073

5174
private boolean isRelevantEvent(final Event event) {
5275
log.debug("checking event: " + event);
5376
return
54-
// state was changed
55-
mailHandlerProperties.getStateChangeAction().equals(event.action()) &&
56-
// new state is closed
57-
mailHandlerProperties.getClosedState().equals(event.status()) &&
58-
// is relevant anliegen
77+
// state was changed by trigger send-to-postbox
78+
mailHandlerProperties.getTicketChangeAction().equals(event.action()) &&
79+
// is relevant anliegen
5980
mailHandlerProperties.getRelevantTicketTypes().contains(event.anliegenart()) &&
6081
// user does have an lhmExtId (i.e. BayernID or BundID user)
6182
event.lhmExtId() != null && !event.lhmExtId().isEmpty();
6283
}
6384

64-
private String buildSubject(final TicketInternal ticket, final Map<String, Object> form) {
65-
return "[%s;%s;%s;%s] Ihr Anliegen '%s' wurde abschließend bearbeitet"
66-
.formatted(
67-
form.get("legacyPostkorbHandle"),
68-
form.get("accountSource"),
69-
form.get("ticketingVertrauensniveau"),
70-
"Dummy",
71-
ticket.getTitle());
85+
private boolean isRelevantTicket(final TicketInternal ticket) {
86+
log.debug("Checking value of sende_nachricht_nach_extern: " + ticket.getSendeNachrichtNachExtern());
87+
return TO_POSTBOX_DEFAULT.equals(ticket.getSendeNachrichtNachExtern()) || TO_POSTBOX_HIGH.equals(ticket.getSendeNachrichtNachExtern());
7288
}
7389

74-
private String buildBody(final TicketInternal ticket) {
90+
private ArticleInternal findRelevantArticle(final TicketInternal ticket) throws NoValidArticleException {
7591
assert ticket.getArticles() != null;
7692
return ticket.getArticles().stream()
77-
// only public articles of type "web" or "note"
93+
// find last public articles of type "note"
7894
.filter(i -> Boolean.FALSE.equals(i.getInternal()) &&
79-
(ArticleInternal.TypeEnum.WEB.equals(i.getType()) || ArticleInternal.TypeEnum.NOTE.equals(i.getType())))
80-
// format single article
81-
.map(i -> "Titel: %s<br>Body: %s".formatted(i.getSubject(), i.getBody()))
82-
// build body
83-
.collect(Collectors.joining("<hr>"));
95+
ArticleInternal.TypeEnum.NOTE.equals(i.getType()))
96+
.reduce((first, second) -> second)
97+
.orElseThrow(() -> new NoValidArticleException("no valid article found in ticket " + ticket.getId()));
98+
}
99+
100+
private void sendMail(final TicketInternal ticket, final Map<String, Object> form, final ArticleInternal article) {
101+
final String recipient = mailHandlerProperties.getRecipient();
102+
final String subject = buildSubject(ticket, form);
103+
log.debug("Created subject: " + subject);
104+
final String body = buildBody(article);
105+
log.debug("Created body: " + body);
106+
final Map<String, InputStream> attachments = buildAttachments(article);
107+
sendMailOutport.sendMail(new MailMessage(recipient, subject, body, attachments));
108+
}
84109

110+
private String buildSubject(final TicketInternal ticket, final Map<String, Object> form) {
111+
final String authlevel;
112+
if (form.get(TICKETING_VERTRAUENSNIVEAU) == null) {
113+
log.error("no ticketingVertrauensniveau found in ticket " + ticket.getId() + " - setting level1");
114+
authlevel = "level1";
115+
} else {
116+
authlevel = TO_POSTBOX_HIGH.equals(ticket.getSendeNachrichtNachExtern()) ? "level3" : String.valueOf(form.get(TICKETING_VERTRAUENSNIVEAU));
117+
}
118+
119+
return "[%s;%s;%s;%s] Neue Nachricht zu Ihrem Anliegen '%s'"
120+
.formatted(
121+
form.get(LEGACY_POSTKORB_HANDLE),
122+
form.get(ACCOUNT_SOURCE),
123+
authlevel,
124+
"Zammad-Eventing",
125+
ticket.getTitle());
126+
}
127+
128+
private String buildBody(final ArticleInternal article) {
129+
return article.getBody();
130+
}
131+
132+
private Map<String, InputStream> buildAttachments(final ArticleInternal article) {
133+
if (article.getAttachments() == null) {
134+
return new HashMap<>();
135+
}
136+
return article.getAttachments().stream().collect(Collectors.toMap(ArticleAttachment::getFilename,
137+
a -> ticketingOutPort.getAttachmentContent(article.getTicketId(), article.getId(), a.getId())));
85138
}
86139

87140
private Map<String, Object> getParsedForm(final TicketInternal ticket) {
@@ -109,4 +162,12 @@ private Map<String, Object> getParsedForm(final TicketInternal ticket) {
109162
throw new RuntimeException(e);
110163
}
111164
}
165+
166+
private void resetTicket(final TicketInternal ticket) {
167+
final UpdateTicketDTO updateTicketDTO = new UpdateTicketDTO();
168+
updateTicketDTO.setId(ticket.getId());
169+
updateTicketDTO.setDirektkennwort(ticket.getDirektkennwort());
170+
updateTicketDTO.setSendeNachrichtNachExtern(null);
171+
ticketingOutPort.updateTicket(updateTicketDTO);
172+
}
112173
}

ticketing-eventing/mail-handler-service/src/main/java/de/muenchen/oss/dbs/ticketing/eventing/mailhandler/config/MailHandlerProperties.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ public class MailHandlerProperties {
1818
@NotBlank
1919
private String recipient;
2020
@NotBlank
21-
private String stateChangeAction;
22-
@NotBlank
23-
private String closedState;
21+
private String ticketChangeAction;
2422
@NotNull
2523
private List<String> relevantTicketTypes;
2624
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package de.muenchen.oss.dbs.ticketing.eventing.mailhandler.exceptions;
2+
3+
import java.io.Serial;
4+
5+
public class NoValidArticleException extends Exception {
6+
// Default access modifier is private
7+
@Serial
8+
private static final long serialVersionUID = 1L;
9+
10+
public NoValidArticleException(final String s) {
11+
super(s);
12+
}
13+
}

ticketing-eventing/mail-handler-service/src/main/resources/application-local.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ spring:
2323
client:
2424
provider:
2525
zammad:
26-
issuer-uri: TODO
26+
issuer-uri: https://ssodev.muenchen.de/auth/realms/KM81
2727
registration:
2828
zammad:
2929
provider: zammad
3030
authorization-grant-type: client_credentials
31-
client-id: TODO
32-
client-secret: TODO
31+
client-id: mpdz-ticketing-fk
32+
client-secret: V8zmjkcqgXE9xIWhljjVT45Bxli2c02B
3333

3434
dbs:
3535
eai:

0 commit comments

Comments
 (0)