From 0464281a38fb3e416f549bfea15c25baf877d7fb Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Sun, 14 Jan 2024 11:09:22 +0100 Subject: [PATCH] Refactor code to use Form objects instead of JSON strings Several parts of the code have been altered to use Form objects instead of JSON strings. Changes include updating the FormQueryResult record type to hold an Optional
instead of an Optional, altering methods in the StoreFormQuery class to insert Form data into the database and replacing JSON handling methods in the FormQuery class with Form object oriented methods. A 'form_class' field has also been added to the 'form' table in the database to aid form identification and reconstruction from stored data. --- .../forms/contact/ContactController.java | 2 +- .../alttd/forms/contact/StoreFormQuery.java | 11 ++++--- .../com/alttd/forms/database/Database.java | 2 +- .../alttd/forms/mail/mail_forms/MailForm.java | 15 ++++------ .../alttd/forms/verify_mail/FormQuery.java | 30 +++++++++++++++---- .../forms/verify_mail/FormQueryResult.java | 4 ++- .../forms/verify_mail/VerifyController.java | 11 +++---- 7 files changed, 48 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/alttd/forms/contact/ContactController.java b/src/main/java/com/alttd/forms/contact/ContactController.java index ed4f797..9af7a39 100644 --- a/src/main/java/com/alttd/forms/contact/ContactController.java +++ b/src/main/java/com/alttd/forms/contact/ContactController.java @@ -21,7 +21,7 @@ public class ContactController { public CompletableFuture> submitForm(@Valid @RequestBody ContactFormData formData) { logger.debug(formData.toString()); - CompletableFuture storeFormForVerificationCode = new StoreFormQuery().storeFormForVerificationCode(formData.toJsonString(), formData.email); + CompletableFuture storeFormForVerificationCode = new StoreFormQuery().storeFormForVerificationCode(formData.email, formData); return storeFormForVerificationCode.thenCompose(code -> Verify.verifyEmail(formData.email, code).thenApply(verificationResult -> { if (verificationResult == VerificationResult.VERIFICATION_SENT) { //TODO if this is ok tell the user they have x min to verify if they fail to do so they have to remake the form diff --git a/src/main/java/com/alttd/forms/contact/StoreFormQuery.java b/src/main/java/com/alttd/forms/contact/StoreFormQuery.java index 5271ade..aa7c746 100644 --- a/src/main/java/com/alttd/forms/contact/StoreFormQuery.java +++ b/src/main/java/com/alttd/forms/contact/StoreFormQuery.java @@ -7,6 +7,8 @@ import java.time.Instant; import java.util.Optional; import java.util.Random; import java.util.concurrent.CompletableFuture; + +import com.alttd.forms.form.Form; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -19,11 +21,12 @@ public class StoreFormQuery { return 100000 + random.nextInt(900000); } - private Optional insertForm(Connection connection, String form) { - String insertForm = "INSERT INTO form (creation_date, form_json) VALUES (?, ?)"; + private Optional insertForm(Connection connection, Form form) { + String insertForm = "INSERT INTO form (creation_date, form_json, form_class) VALUES (?, ?, ?)"; try (PreparedStatement stmt = connection.prepareStatement(insertForm, Statement.RETURN_GENERATED_KEYS)) { stmt.setLong(1, Instant.now().toEpochMilli()); - stmt.setString(2, form); + stmt.setString(2, form.toJsonString()); + stmt.setString(3, form.getClass().getSimpleName()); int affectedRows = stmt.executeUpdate(); if (affectedRows == 0) { logger.error("No rows affected during insert of form: " + form); @@ -57,7 +60,7 @@ public class StoreFormQuery { } } - public CompletableFuture storeFormForVerificationCode(String form, String eMail) { + public CompletableFuture storeFormForVerificationCode(String eMail, Form form) { Connection connection = DatabaseConnection.getConnection(); return CompletableFuture.supplyAsync(() -> { Optional optionalFormId = insertForm(connection, form); diff --git a/src/main/java/com/alttd/forms/database/Database.java b/src/main/java/com/alttd/forms/database/Database.java index 6188f0d..ebd8117 100644 --- a/src/main/java/com/alttd/forms/database/Database.java +++ b/src/main/java/com/alttd/forms/database/Database.java @@ -13,7 +13,7 @@ public class Database { public static void createTables() { String[] createTables = { "CREATE TABLE IF NOT EXISTS verify_form (e_mail VARCHAR(256), verification_code INT, formId INT, PRIMARY KEY(e_mail, verification_code))", - "CREATE TABLE IF NOT EXISTS form (formId INT AUTO_INCREMENT, creation_date BIGINT, form_json TEXT, PRIMARY KEY(formId))" + "CREATE TABLE IF NOT EXISTS form (formId INT AUTO_INCREMENT, creation_date BIGINT, form_json TEXT, form_class VARCHAR(64), PRIMARY KEY(formId))" }; Connection connection = DatabaseConnection.getConnection(); for (String query : createTables) { diff --git a/src/main/java/com/alttd/forms/mail/mail_forms/MailForm.java b/src/main/java/com/alttd/forms/mail/mail_forms/MailForm.java index 0263ae4..3d81d03 100644 --- a/src/main/java/com/alttd/forms/mail/mail_forms/MailForm.java +++ b/src/main/java/com/alttd/forms/mail/mail_forms/MailForm.java @@ -1,9 +1,7 @@ package com.alttd.forms.mail.mail_forms; -import com.alttd.forms.contact.ContactFormData; import com.alttd.forms.form.Form; import com.alttd.forms.mail.MailSettings; -import com.google.gson.Gson; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -17,19 +15,16 @@ public class MailForm { private static final Logger logger = LoggerFactory.getLogger(MailForm.class); - public static void sendForm(String receiver, String json) { //TODO something to convert json back to the right object, might need to store classname in db? - ContactFormData contactFormData = new Gson().fromJson(json, ContactFormData.class); + public static void sendForm(String receiver, Form form) { Properties mailProperties = MailSettings.getMailProperties(); Optional accountDetails = MailSettings.getAccountDetails(); if (accountDetails.isEmpty()) { - logger.error("No account details, can't send email to " + receiver + " with data " + contactFormData.toString()); + logger.error("No account details, can't send email to " + receiver + " with data " + form.toString()); return; } PasswordAuthentication passwordAuthentication = accountDetails.get(); Session session = MailSettings.getSession(mailProperties, passwordAuthentication); - //TODO rate limiting should be handled before anything ever gets here - try { Message message = new MimeMessage(session); message.setFrom(new InternetAddress(passwordAuthentication.getUserName())); @@ -39,12 +34,12 @@ public class MailForm { ); message.setSubject("Altitude Form"); //TODO add something above the html form probably - message.setContent(contactFormData.toHtml(), "text/html"); + message.setContent(form.toHtml(), "text/html"); try { Transport.send(message); - logger.debug("Send mail to " + receiver + " containing " + contactFormData) ; + logger.debug("Send mail to " + receiver + " containing " + form) ; } catch (MessagingException e) { - logger.error("Unable to send mail to " + receiver + " with data " + contactFormData, e); + logger.error("Unable to send mail to " + receiver + " with data " + form, e); } } catch (MessagingException e) { logger.error("Failed to create MimeMessage", e); diff --git a/src/main/java/com/alttd/forms/verify_mail/FormQuery.java b/src/main/java/com/alttd/forms/verify_mail/FormQuery.java index 328ea1a..b5643f4 100644 --- a/src/main/java/com/alttd/forms/verify_mail/FormQuery.java +++ b/src/main/java/com/alttd/forms/verify_mail/FormQuery.java @@ -1,6 +1,10 @@ package com.alttd.forms.verify_mail; +import com.alttd.forms.contact.ContactFormData; +import com.alttd.forms.contact.StoreFormQuery; import com.alttd.forms.database.DatabaseConnection; +import com.alttd.forms.form.Form; +import com.google.gson.Gson; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,7 +19,7 @@ public class FormQuery { private static final Logger logger = LoggerFactory.getLogger(FormQuery.class); - public static Optional getFormId(Connection connection, int verificationCode, String eMail) throws SQLException { + public Optional getFormId(Connection connection, int verificationCode, String eMail) throws SQLException { String sql = "SELECT formId FROM verify_form WHERE verification_code = ? AND e_mail = ?"; try (PreparedStatement stmt = connection.prepareStatement(sql)) { @@ -33,7 +37,7 @@ public class FormQuery { } } - private static Optional getFormForId(Connection connection, int formId) throws SQLException { + private Optional getFormForId(Connection connection, int formId) throws SQLException { String sql = "SELECT form_json FROM form WHERE formId = ?"; try (PreparedStatement stmt = connection.prepareStatement(sql)) { @@ -43,14 +47,30 @@ public class FormQuery { logger.warn("Could not find form with id: " + formId); return Optional.empty(); } - return Optional.of(resultSet.getString("form_json")); + String json = resultSet.getString("form_json"); + String formClass = resultSet.getString("form_class"); + try { + return Optional.of(getForm(formClass, json)); + } catch (IllegalArgumentException e) { + logger.error("Invalid form class in database", e); + return Optional.empty(); + } } catch (SQLException e) { logger.error("Failed select form query for form with id: " + formId, e); throw e; } } - public static CompletableFuture getFormForCode(String verificationCode, String eMail) { + private Form getForm(String className, String json) throws IllegalArgumentException { + switch (className) { + case "ContactFormData" -> { + return new Gson().fromJson(json, ContactFormData.class); + } + default -> throw new IllegalArgumentException("Invalid form class name: " + className); + } + } + + public CompletableFuture getFormForCode(String verificationCode, String eMail) { Connection connection = DatabaseConnection.getConnection(); int code; try { @@ -73,7 +93,7 @@ public class FormQuery { try { return getFormForId(connection, formId.get()) - .map(formJson -> new FormQueryResult(Optional.of(formJson), "Success")) + .map(form -> new FormQueryResult(Optional.of(form), "Success")) .orElse(new FormQueryResult(Optional.empty(), "Unable to find your form")); } catch (SQLException e) { throw new RuntimeException(e); diff --git a/src/main/java/com/alttd/forms/verify_mail/FormQueryResult.java b/src/main/java/com/alttd/forms/verify_mail/FormQueryResult.java index 8d5e3df..83970ff 100644 --- a/src/main/java/com/alttd/forms/verify_mail/FormQueryResult.java +++ b/src/main/java/com/alttd/forms/verify_mail/FormQueryResult.java @@ -1,6 +1,8 @@ package com.alttd.forms.verify_mail; +import com.alttd.forms.form.Form; + import java.util.Optional; -public record FormQueryResult(Optional formJson, String failReason) { +public record FormQueryResult(Optional form, String failReason) { } diff --git a/src/main/java/com/alttd/forms/verify_mail/VerifyController.java b/src/main/java/com/alttd/forms/verify_mail/VerifyController.java index 86498fb..537aae5 100644 --- a/src/main/java/com/alttd/forms/verify_mail/VerifyController.java +++ b/src/main/java/com/alttd/forms/verify_mail/VerifyController.java @@ -1,5 +1,6 @@ package com.alttd.forms.verify_mail; +import com.alttd.forms.form.Form; import com.alttd.forms.mail.mail_forms.MailForm; import jakarta.validation.Valid; import org.slf4j.Logger; @@ -21,12 +22,12 @@ public class VerifyController { @PostMapping("/form") public CompletableFuture> validateEmailFromForm(@Valid @RequestBody VerificationData verificationData) { logger.debug(verificationData.toString()); - return FormQuery.getFormForCode(verificationData.code, verificationData.eMail).thenApply(form -> form.formJson() - .map(body -> { - MailForm.sendForm("akastijn@alttd.com", body); - return ResponseEntity.ok(body); + return new FormQuery().getFormForCode(verificationData.code, verificationData.eMail).thenApply(result -> result.form() + .map(form -> { + MailForm.sendForm("akastijn@alttd.com", form); + return ResponseEntity.ok(form.toJsonString()); }) - .orElse(ResponseEntity.ok(form.failReason())) + .orElse(ResponseEntity.ok(result.failReason())) ).exceptionally(throwable -> ResponseEntity.internalServerError() .body("The server was unable to process your request, if this issue persists please contact admin@alttd.com")); }