Skip to content

Commit

Permalink
VKT(Frontend & Backend): Paytrail payment link and email sending fixe…
Browse files Browse the repository at this point in the history
…s [deploy]
  • Loading branch information
jrkkp committed Nov 19, 2024
1 parent 5b76a74 commit 752f571
Show file tree
Hide file tree
Showing 17 changed files with 487 additions and 221 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public record ExaminerEnrollmentAppointmentDTO(
@NonNull @NotBlank String lastName,
@NonNull @NotNull List<ClerkPaymentDTO> payments,
ExaminerExamEventDTO examEvent,
ExaminerAuthLinkDTO authLink
ExaminerAuthLinkDTO authLink,
String paymentLinkUrl
)
implements EnrollmentDTOSkillFields {}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import io.swagger.v3.oas.annotations.Operation;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import java.io.IOException;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;

Expand Down Expand Up @@ -59,6 +60,15 @@ public ExaminerEnrollmentAppointmentDTO getEnrollmentAppointment(
return examinerEnrollmentService.getEnrollmentAppointment(oid, enrollmentAppointmentId);
}

@PostMapping(path = "/appointment/{enrollmentAppointmentId:\\d+}/sendAuthLink", consumes = ALL_VALUE)
@Operation(tags = TAG_ENROLLMENT, summary = "Send enrollment appointment auth link")
public ExaminerEnrollmentAppointmentDTO sendEnrollmentAppointmentLink(
@PathVariable final String oid,
@PathVariable final long enrollmentAppointmentId
) throws IOException, InterruptedException {
return examinerEnrollmentService.sendEnrollmentAppointmentLink(oid, enrollmentAppointmentId);
}

@PutMapping(path = "/appointment/{enrollmentAppointmentId:\\d+}/grades")
@Operation(tags = TAG_ENROLLMENT, summary = "Update enrollment appointment grades")
public ExaminerEnrollmentGradesDTO upsertEnrollmentAppointmentGrades(
Expand Down
1 change: 1 addition & 0 deletions backend/vkt/src/main/java/fi/oph/vkt/model/EmailType.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
public enum EmailType {
ENROLLMENT_CONFIRMATION,
ENROLLMENT_TO_QUEUE_CONFIRMATION,
ENROLLMENT_APPOINTMENT_AUTH_LINK,
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ public class EnrollmentAppointment extends EnrollmentCommon {
@Column(name = "message")
private String message;

@Size(max = 255)
@Column(name = "payment_link_hash", unique = true)
private String paymentLinkHash;

@Size(max = 255)
@Column(name = "auth_hash", unique = true)
private String authHash;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package fi.oph.vkt.service;

import static fi.oph.vkt.util.LocalisationUtil.localeFI;
import static fi.oph.vkt.util.LocalisationUtil.localeSV;

import fi.oph.vkt.model.EmailType;
import fi.oph.vkt.model.Enrollment;
import fi.oph.vkt.model.EnrollmentAppointment;
import fi.oph.vkt.model.EnrollmentCommon;
import fi.oph.vkt.model.ExamEvent;
import fi.oph.vkt.model.ExamEventCommon;
import fi.oph.vkt.model.ExaminerExamEvent;
import fi.oph.vkt.model.type.ExamLanguage;
import fi.oph.vkt.service.email.EmailAttachmentData;
import fi.oph.vkt.service.email.EmailData;
import fi.oph.vkt.service.email.EmailService;
import fi.oph.vkt.util.LocalisationUtil;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class AbstractEnrollmentEmailService {

protected void createEmail(
final EmailService emailService,
final String recipientName,
final String recipientAddress,
final String subject,
final String body,
final List<EmailAttachmentData> attachments,
final EmailType emailType
) {
final EmailData emailData = EmailData
.builder()
.recipientName(recipientName)
.recipientAddress(recipientAddress)
.subject(subject)
.body(body)
.attachments(attachments)
.build();

emailService.saveEmail(emailType, emailData);
}

protected Map<String, Object> getEmailParams(final EnrollmentCommon enrollment, final ExamEventCommon examEvent) {
final Map<String, Object> params = new HashMap<>(Map.of());

if (examEvent.getLanguage() == ExamLanguage.FI) {
params.put("examLanguageFI", LocalisationUtil.translate(localeFI, "lang.finnish"));
params.put("examLanguageSV", LocalisationUtil.translate(localeSV, "lang.finnish"));
} else {
params.put("examLanguageFI", LocalisationUtil.translate(localeFI, "lang.swedish"));
params.put("examLanguageSV", LocalisationUtil.translate(localeSV, "lang.swedish"));
}

params.put("skillsFI", getEmailParamSkills(enrollment, localeFI, params.get("examLanguageFI")));
params.put("skillsSV", getEmailParamSkills(enrollment, localeSV, params.get("examLanguageSV")));

params.put("partialExamsFI", getEmailParamPartialExams(enrollment, localeFI));
params.put("partialExamsSV", getEmailParamPartialExams(enrollment, localeSV));

params.put("examLevelFI", LocalisationUtil.translate(localeFI, "examLevel.excellent"));
params.put("examLevelSV", LocalisationUtil.translate(localeSV, "examLevel.excellent"));

params.put("examDate", examEvent.getDate().format(DateTimeFormatter.ofPattern("dd.MM.yyyy")));

params.put("type", "enrollment");
params.put("isFree", false);

return params;
}

private String getEmailParamSkills(final EnrollmentCommon enrollment, final Locale locale, final Object... args) {
return joinNonEmptyStrings(
Stream.of(
enrollment.isTextualSkill() ? LocalisationUtil.translate(locale, "skill.textual", args) : "",
enrollment.isOralSkill() ? LocalisationUtil.translate(locale, "skill.oral", args) : "",
enrollment.isUnderstandingSkill() ? LocalisationUtil.translate(locale, "skill.understanding", args) : ""
)
);
}

private String getEmailParamPartialExams(final EnrollmentCommon enrollment, final Locale locale) {
return joinNonEmptyStrings(
Stream.of(
enrollment.isWritingPartialExam() ? LocalisationUtil.translate(locale, "partialExam.writing") : "",
enrollment.isReadingComprehensionPartialExam()
? LocalisationUtil.translate(locale, "partialExam.readingComprehension")
: "",
enrollment.isSpeakingPartialExam() ? LocalisationUtil.translate(locale, "partialExam.speaking") : "",
enrollment.isSpeechComprehensionPartialExam()
? LocalisationUtil.translate(locale, "partialExam.speechComprehension")
: ""
)
);
}

private String joinNonEmptyStrings(final Stream<String> stream) {
return stream.filter(s -> !s.isEmpty()).collect(Collectors.joining(", "));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package fi.oph.vkt.service;

import static fi.oph.vkt.util.LocalisationUtil.localeFI;
import static fi.oph.vkt.util.LocalisationUtil.localeSV;

import fi.oph.vkt.model.EmailType;
import fi.oph.vkt.model.Enrollment;
import fi.oph.vkt.model.EnrollmentAppointment;
import fi.oph.vkt.model.Person;
import fi.oph.vkt.service.email.EmailAttachmentData;
import fi.oph.vkt.service.email.EmailService;
import fi.oph.vkt.service.receipt.ReceiptRenderer;
import fi.oph.vkt.util.ClerkEnrollmentUtil;
import fi.oph.vkt.util.LocalisationUtil;
import fi.oph.vkt.util.TemplateRenderer;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class ExaminerEnrollmentEmailService extends AbstractEnrollmentEmailService {

private final EmailService emailService;
private final Environment environment;
private final TemplateRenderer templateRenderer;

@Transactional
public void sendEnrollmentAppointmentAuthLink(final EnrollmentAppointment enrollment)
throws IOException, InterruptedException {
final String baseUrlAPI = environment.getRequiredProperty("app.base-url.api");
final Map<String, Object> templateParams = getEmailParams(enrollment, enrollment.getExaminerExamEvent());
final String authUrl = ClerkEnrollmentUtil.getAuthUrl(baseUrlAPI, enrollment.getId(), enrollment.getAuthHash());

templateParams.put("type", "enrollment");
templateParams.put("enrollmentAuthLink", authUrl);

final String recipientName = enrollment.getFirstName() + " " + enrollment.getLastName();
final String recipientAddress = enrollment.getEmail();
final String subject = String.format(
"%s | %s",
LocalisationUtil.translate(localeFI, "subject.enrollment-confirmation"),
LocalisationUtil.translate(localeSV, "subject.enrollment-confirmation")
);
final String body = templateRenderer.renderEnrollmentAppointmentAuthLink(templateParams);

createEmail(
emailService,
recipientName,
recipientAddress,
subject,
body,
List.of(),
EmailType.ENROLLMENT_APPOINTMENT_AUTH_LINK
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import fi.oph.vkt.util.UUIDSource;
import fi.oph.vkt.util.exception.APIException;
import fi.oph.vkt.util.exception.APIExceptionType;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Objects;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
Expand All @@ -33,6 +35,7 @@ public class ExaminerEnrollmentService extends AbstractEnrollmentService {
private final ExaminerExamEventRepository examinerExamEventRepository;
private final Environment environment;
private final UUIDSource uuidSource;
private final ExaminerEnrollmentEmailService examinerEnrollmentEmailService;

private static void checkExaminerOid(EnrollmentAppointment enrollmentAppointment, String oid) {
if (!enrollmentAppointment.getExaminer().getOid().equals(oid)) {
Expand Down Expand Up @@ -94,6 +97,10 @@ public ExaminerEnrollmentAppointmentDTO convertToAppointment(final String oid, f
enrollmentAppointment.setAuthHash(uuidSource.getRandomNonce());
}

if (enrollmentAppointment.getPaymentLinkHash() == null) {
enrollmentAppointment.setPaymentLinkHash(uuidSource.getRandomNonce());
}

enrollmentAppointmentRepository.flush();

return ClerkEnrollmentUtil.createClerkEnrollmentAppointmentDTO(enrollmentAppointment, baseUrlAPI);
Expand Down Expand Up @@ -214,4 +221,26 @@ private ExaminerEnrollmentGradesDTO createGradesDTO(final EnrollmentGrade enroll
private EnrollmentGradeDTO createGradeDTO(final EnrollmentGradeType grade, final String comment) {
return grade == null ? null : EnrollmentGradeDTO.builder().grade(grade).comment(comment).build();
}

@Transactional
public ExaminerEnrollmentAppointmentDTO sendEnrollmentAppointmentLink(
final String oid,
final long enrollmentAppointmentId
) throws IOException, InterruptedException {
final EnrollmentAppointment enrollmentAppointment = enrollmentAppointmentRepository.getReferenceById(
enrollmentAppointmentId
);
final String baseUrlAPI = environment.getRequiredProperty("app.base-url.api");

checkExaminerOid(enrollmentAppointment, oid);

enrollmentAppointment.setExpiresAt(LocalDateTime.now().plusDays(3));
enrollmentAppointment.setSentAt(LocalDateTime.now());

examinerEnrollmentEmailService.sendEnrollmentAppointmentAuthLink(enrollmentAppointment);

enrollmentAppointmentRepository.flush();

return ClerkEnrollmentUtil.createClerkEnrollmentAppointmentDTO(enrollmentAppointment, baseUrlAPI);
}
}
Loading

0 comments on commit 752f571

Please sign in to comment.