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<Form> instead of an Optional<String>, 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.
This commit is contained in:
Teriuihi 2024-01-14 11:09:22 +01:00
parent 2b908b4e94
commit 0464281a38
7 changed files with 48 additions and 27 deletions

View File

@ -21,7 +21,7 @@ public class ContactController {
public CompletableFuture<ResponseEntity<String>> submitForm(@Valid @RequestBody ContactFormData formData) {
logger.debug(formData.toString());
CompletableFuture<Integer> storeFormForVerificationCode = new StoreFormQuery().storeFormForVerificationCode(formData.toJsonString(), formData.email);
CompletableFuture<Integer> 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

View File

@ -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<Long> insertForm(Connection connection, String form) {
String insertForm = "INSERT INTO form (creation_date, form_json) VALUES (?, ?)";
private Optional<Long> 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<Integer> storeFormForVerificationCode(String form, String eMail) {
public CompletableFuture<Integer> storeFormForVerificationCode(String eMail, Form form) {
Connection connection = DatabaseConnection.getConnection();
return CompletableFuture.supplyAsync(() -> {
Optional<Long> optionalFormId = insertForm(connection, form);

View File

@ -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) {

View File

@ -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<PasswordAuthentication> 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);

View File

@ -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<Integer> getFormId(Connection connection, int verificationCode, String eMail) throws SQLException {
public Optional<Integer> 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<String> getFormForId(Connection connection, int formId) throws SQLException {
private Optional<Form> 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<FormQueryResult> 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<FormQueryResult> 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);

View File

@ -1,6 +1,8 @@
package com.alttd.forms.verify_mail;
import com.alttd.forms.form.Form;
import java.util.Optional;
public record FormQueryResult(Optional<String> formJson, String failReason) {
public record FormQueryResult(Optional<Form> form, String failReason) {
}

View File

@ -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<ResponseEntity<String>> 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"));
}