From be19dd5461e4b145fc3cf0ed318b317c82d0818f Mon Sep 17 00:00:00 2001 From: Jarkko Pesonen <435495+jrkkp@users.noreply.github.com> Date: Fri, 21 Feb 2025 10:29:51 +0200 Subject: [PATCH] =?UTF-8?q?VKT(Backend)=20Viestint=C3=A4palvelu=20attachme?= =?UTF-8?q?nt=20proof=20of=20concept=20implementation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/fi/oph/vkt/api/PublicController.java | 25 +++++++- .../EmailSenderViestintapalveluNew.java | 62 +++++++++++++++---- 2 files changed, 73 insertions(+), 14 deletions(-) diff --git a/backend/vkt/src/main/java/fi/oph/vkt/api/PublicController.java b/backend/vkt/src/main/java/fi/oph/vkt/api/PublicController.java index e6834a3a8..d190d8943 100644 --- a/backend/vkt/src/main/java/fi/oph/vkt/api/PublicController.java +++ b/backend/vkt/src/main/java/fi/oph/vkt/api/PublicController.java @@ -1,5 +1,6 @@ package fi.oph.vkt.api; +import static fi.oph.vkt.util.LocalisationUtil.localeFI; import com.fasterxml.jackson.core.JsonProcessingException; import fi.oph.vkt.api.dto.PublicEducationDTO; import fi.oph.vkt.api.dto.PublicEnrollmentCreateDTO; @@ -16,6 +17,7 @@ import fi.oph.vkt.model.type.EnrollmentType; import fi.oph.vkt.model.type.ExamLevel; import fi.oph.vkt.model.type.FreeEnrollmentType; +import fi.oph.vkt.repository.EnrollmentRepository; import fi.oph.vkt.service.FeatureFlagService; import fi.oph.vkt.service.PaymentService; import fi.oph.vkt.service.PublicAuthService; @@ -23,9 +25,13 @@ import fi.oph.vkt.service.PublicExamEventService; import fi.oph.vkt.service.PublicPersonService; import fi.oph.vkt.service.PublicReservationService; +import fi.oph.vkt.service.email.EmailAttachmentData; import fi.oph.vkt.service.email.EmailData; import fi.oph.vkt.service.email.sender.EmailSenderViestintapalveluNew; import fi.oph.vkt.service.koski.KoskiService; +import fi.oph.vkt.service.receipt.ReceiptData; +import fi.oph.vkt.service.receipt.ReceiptRenderer; +import fi.oph.vkt.util.LocalisationUtil; import fi.oph.vkt.util.SessionUtil; import fi.oph.vkt.util.UIRouteUtil; import fi.oph.vkt.util.exception.APIException; @@ -39,6 +45,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.concurrent.ExecutionException; @@ -95,6 +102,12 @@ public class PublicController { @Resource private CasClient casClient; + @Resource + private EnrollmentRepository enrollmentRepository; + + @Resource + private ReceiptRenderer receiptRenderer; + @GetMapping(path = "/examEvent") public List list() { return publicExamEventService.listExamEvents(ExamLevel.EXCELLENT); @@ -339,6 +352,16 @@ public void paymentSuccess( public void email( ) throws IOException, ExecutionException, InterruptedException { final EmailSenderViestintapalveluNew emailSenderViestintapalveluNew = new EmailSenderViestintapalveluNew(casClient, Constants.SERVICENAME, Constants.EMAIL_SENDER_NAME); + + final byte[] receiptBytes = "test".getBytes(); + + final EmailAttachmentData emailAttachmentData = EmailAttachmentData + .builder() + .name("test.txt") + .contentType("text/plain") + .data(receiptBytes) + .build(); + emailSenderViestintapalveluNew.sendEmail( EmailData .builder() @@ -347,7 +370,7 @@ public void email( .recipientAddress("test@test.invalid") .recipientName("Test") .body("This is a test") - .attachments(List.of()) + .attachments(List.of(emailAttachmentData)) .build() ); } diff --git a/backend/vkt/src/main/java/fi/oph/vkt/service/email/sender/EmailSenderViestintapalveluNew.java b/backend/vkt/src/main/java/fi/oph/vkt/service/email/sender/EmailSenderViestintapalveluNew.java index 358379269..661cc88fc 100644 --- a/backend/vkt/src/main/java/fi/oph/vkt/service/email/sender/EmailSenderViestintapalveluNew.java +++ b/backend/vkt/src/main/java/fi/oph/vkt/service/email/sender/EmailSenderViestintapalveluNew.java @@ -18,9 +18,8 @@ import org.asynchttpclient.Request; import org.asynchttpclient.RequestBuilder; import org.asynchttpclient.Response; +import org.asynchttpclient.request.body.multipart.ByteArrayPart; import org.asynchttpclient.util.HttpConstants; -import org.springframework.http.MediaType; -import reactor.core.publisher.Mono; @RequiredArgsConstructor public class EmailSenderViestintapalveluNew implements EmailSender { @@ -36,14 +35,15 @@ public class EmailSenderViestintapalveluNew implements EmailSender { @Override public String sendEmail(final EmailData emailData) throws JsonProcessingException, ExecutionException, InterruptedException { final ObjectMapper objectMapper = new ObjectMapper(); - final Map postData = createPostData(emailData); + final List attachments = createAndPostAttachments(emailData.attachments()); + final Map postData = createPostData(emailData, attachments); final String body = objectMapper.writeValueAsString(postData); final Request request = new RequestBuilder() .setUrl("https://viestinvalitys.testiopintopolku.fi/lahetys/v1/viestit") .setMethod("POST") .setBody(body) - .setRequestTimeout(Duration.ofMillis(10000)) + .setRequestTimeout(Duration.ofSeconds(10)) .addHeader("Caller-Id", callerId) .addHeader("Content-Type", "application/json") .addHeader("Accept", "application/json") @@ -53,7 +53,7 @@ public String sendEmail(final EmailData emailData) throws JsonProcessingExceptio final Response response = casClient.executeAndRetryWithCleanSessionOnStatusCodes(request, Set.of(401)).get(); if (response.getStatusCode() == HttpConstants.ResponseStatusCodes.OK_200) { - return parseExternalId(response.getResponseBody()); + return parseExternalId(response.getResponseBody(), "lahetysTunniste"); } } catch (Exception e) { throw e; @@ -62,7 +62,7 @@ public String sendEmail(final EmailData emailData) throws JsonProcessingExceptio return null; } - private Map createPostData(final EmailData emailData) { + private Map createPostData(final EmailData emailData, final List attachments) { final Map senderFields = Map.of("nimi", sender, "sahkopostiOsoite", "noreply@opintopolku.fi"); // Allowed characters: a-z, A-Z, 0-9 ja -_. final String emailKeyPrefix = "vkt-email-"; @@ -90,26 +90,62 @@ private Map createPostData(final EmailData emailData) { "sailytysaika", expirationDays, "idempotencyKey", - emailKeyPrefix + emailData.id() - //"attachments", - //createAttachments(emailData.attachments()) + emailKeyPrefix + emailData.id(), + "liitteidenTunnisteet", + attachments ); } - private List> createAttachments(final List attachments) { + private String postAttachment(final EmailAttachmentData attachment) throws ExecutionException, InterruptedException, JsonProcessingException { + final Request request = new RequestBuilder() + .setUrl("https://viestinvalitys.testiopintopolku.fi/lahetys/v1/liitteet") + .setMethod("POST") + .addBodyPart(new ByteArrayPart("liite", attachment.data(), attachment.contentType(), null, attachment.name())) + .setRequestTimeout(Duration.ofSeconds(10)) + .addHeader("Caller-Id", callerId) + .addHeader("Content-Type", "multipart/form-data") + .addHeader("Accept", "application/json") + .build(); + + try { + final Response response = casClient.executeAndRetryWithCleanSessionOnStatusCodes(request, Set.of(401)).get(); + + if (response.getStatusCode() == HttpConstants.ResponseStatusCodes.OK_200) { + final String id = parseExternalId(response.getResponseBody(), "liiteTunniste"); + + if (id == null) { + throw new RuntimeException(""); + } + + return id; + } else { + throw new RuntimeException(""); + } + } catch (Exception e) { + throw e; + } + } + + private List createAndPostAttachments(final List attachments) { return Optional .ofNullable(attachments) .map(nonNullAttachments -> nonNullAttachments .stream() - .map(a -> Map.of("data", a.data(), "name", a.name(), "contentType", a.contentType())) + .map(attachment -> { + try { + return postAttachment(attachment); + } catch (final Exception e) { + throw new RuntimeException(e); + } + }) .toList() ) .orElse(List.of()); } - private String parseExternalId(final String result) throws JsonProcessingException { + private String parseExternalId(final String result, final String key) throws JsonProcessingException { final Map map = OBJECT_MAPPER.readValue(result, new TypeReference<>() {}); - return map.get("id"); + return map.get(key); } }