From bbd2d3e62cdbb7447fc267a8c68fb61b527965ae Mon Sep 17 00:00:00 2001
From: Pierre Malarme
Date: Fri, 23 Dec 2022 13:55:07 +0100
Subject: [PATCH 1/3] Add smtp output binding
---
FineCollectionService/pom.xml | 5 ++
.../fines/FineCollectionConfiguration.java | 15 ++++
.../violation/DaprViolationProcessor.java | 81 +++++++++++++++++++
.../violation/DefaultViolationProcessor.java | 56 +++++++++++++
.../violation/KafkaViolationConsumer.java | 3 +-
.../fines/violation/ViolationProcessor.java | 56 +------------
.../main/resources/email/email-template.ftl | 74 +++++++++++++++++
dapr/bindings-smtp.yaml | 16 ++++
8 files changed, 250 insertions(+), 56 deletions(-)
create mode 100644 FineCollectionService/src/main/java/dapr/fines/violation/DaprViolationProcessor.java
create mode 100644 FineCollectionService/src/main/java/dapr/fines/violation/DefaultViolationProcessor.java
create mode 100644 FineCollectionService/src/main/resources/email/email-template.ftl
create mode 100644 dapr/bindings-smtp.yaml
diff --git a/FineCollectionService/pom.xml b/FineCollectionService/pom.xml
index d170be9..9c503c3 100644
--- a/FineCollectionService/pom.xml
+++ b/FineCollectionService/pom.xml
@@ -38,6 +38,11 @@
2.8.0
test
+
+ org.freemarker
+ freemarker
+ 2.3.31
+
diff --git a/FineCollectionService/src/main/java/dapr/fines/FineCollectionConfiguration.java b/FineCollectionService/src/main/java/dapr/fines/FineCollectionConfiguration.java
index f85f505..7764dd3 100644
--- a/FineCollectionService/src/main/java/dapr/fines/FineCollectionConfiguration.java
+++ b/FineCollectionService/src/main/java/dapr/fines/FineCollectionConfiguration.java
@@ -4,6 +4,10 @@
import dapr.fines.fines.FineCalculator;
import dapr.fines.vehicle.DefaultVehicleRegistrationClient;
import dapr.fines.vehicle.VehicleRegistrationClient;
+import dapr.fines.violation.DaprViolationProcessor;
+import dapr.fines.violation.DefaultViolationProcessor;
+import dapr.fines.violation.ViolationProcessor;
+
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -43,6 +47,17 @@ public RestTemplate restTemplate() {
public VehicleRegistrationClient vehicleRegistrationClient(final RestTemplate restTemplate) {
return new DefaultVehicleRegistrationClient(restTemplate, vehicleInformationAddress);
}
+
+ @Bean
+ public ViolationProcessor violationProcessor(final FineCalculator fineCalculator, final VehicleRegistrationClient vehicleRegistrationClient) {
+ return new DefaultViolationProcessor(fineCalculator, vehicleRegistrationClient);
+ }
+
+
+// @Bean
+// public ViolationProcessor violationProcessor(final DaprClient daprClient, final FineCalculator fineCalculator, final VehicleRegistrationClient vehicleRegistrationClient) {
+// return new DaprViolationProcessor(daprClient, fineCalculator, vehicleRegistrationClient);
+// }
// @Bean
// public DaprClient daprClient() {
diff --git a/FineCollectionService/src/main/java/dapr/fines/violation/DaprViolationProcessor.java b/FineCollectionService/src/main/java/dapr/fines/violation/DaprViolationProcessor.java
new file mode 100644
index 0000000..5242050
--- /dev/null
+++ b/FineCollectionService/src/main/java/dapr/fines/violation/DaprViolationProcessor.java
@@ -0,0 +1,81 @@
+package dapr.fines.violation;
+
+import dapr.fines.fines.FineCalculator;
+import dapr.fines.vehicle.VehicleInfo;
+import dapr.fines.vehicle.VehicleRegistrationClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.StringWriter;
+import java.time.format.DateTimeFormatter;
+import java.util.Map;
+
+import dapr.traffic.violation.SpeedingViolation;
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import freemarker.template.TemplateExceptionHandler;
+import io.dapr.client.DaprClient;
+
+public class DaprViolationProcessor implements ViolationProcessor {
+ private static final Logger log = LoggerFactory.getLogger(DefaultViolationProcessor.class);
+
+ private static final String BINDING_NAME = "smtp";
+ private static final String BINDING_OPERATION = "create";
+
+ private final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("LLLL, dd y");
+ private final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss");
+
+ private final DaprClient daprClient;
+ private final FineCalculator fineCalculator;
+ private final VehicleRegistrationClient vehicleRegistrationClient;
+ private final Configuration templateConfiguration;
+
+ public DaprViolationProcessor(final DaprClient daprClient,
+ final FineCalculator fineCalculator,
+ final VehicleRegistrationClient vehicleRegistrationClient) {
+ this.daprClient = daprClient;
+ this.fineCalculator = fineCalculator;
+ this.vehicleRegistrationClient = vehicleRegistrationClient;
+ this.templateConfiguration = new Configuration(Configuration.VERSION_2_3_30);
+ this.templateConfiguration.setClassForTemplateLoading(DefaultViolationProcessor.class, "/email");
+ this.templateConfiguration.setDefaultEncoding("UTF-8");
+ this.templateConfiguration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
+ }
+
+ public void processSpeedingViolation(final SpeedingViolation violation) {
+ int fine = fineCalculator.calculateFine(violation.excessSpeed());
+ final VehicleInfo vehicleInfo = vehicleRegistrationClient.getVehicleInfo(violation.licenseNumber());
+
+ final String nowDate = DATE_FORMAT.format(violation.timestamp());
+ final String dateSpeedingViolation = DATE_FORMAT.format(violation.timestamp());
+ final String timeSpeedingViolation = TIME_FORMAT.format(violation.timestamp());
+ final Map metadataEmailTemplate = Map.of(
+ "customerName", vehicleInfo.ownerName(),
+ "fineDate", nowDate,
+ "vehicleBrand", vehicleInfo.make(),
+ "vehicleModel", vehicleInfo.model(),
+ "vehicleLicenseNumber", violation.licenseNumber(),
+ "road", violation.roadId(),
+ "timeOfDay", timeSpeedingViolation,
+ "violationDate", dateSpeedingViolation,
+ "excessSpeed", violation.excessSpeed(),
+ "fineAmount", fine);
+ Template template;
+ try {
+ template = templateConfiguration.getTemplate("email-template.ftl");
+ final StringWriter writer = new StringWriter();
+ template.process(metadataEmailTemplate, writer);
+ writer.flush();
+ final String emailBody = writer.toString();
+
+ final Map metadataBinding = Map.of(
+ "emailFrom", "donotreply@roadtothe.cloud",
+ "emailTo", "pmalarme@roadtothe.cloud",
+ "subject", "Speeding violation on the " + violation.roadId() + " for vehicle " + violation.licenseNumber(),
+ "priority", "1");
+ daprClient.invokeBinding(BINDING_NAME, BINDING_OPERATION, emailBody, metadataBinding, String.class).block();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/FineCollectionService/src/main/java/dapr/fines/violation/DefaultViolationProcessor.java b/FineCollectionService/src/main/java/dapr/fines/violation/DefaultViolationProcessor.java
new file mode 100644
index 0000000..0e9ba8f
--- /dev/null
+++ b/FineCollectionService/src/main/java/dapr/fines/violation/DefaultViolationProcessor.java
@@ -0,0 +1,56 @@
+package dapr.fines.violation;
+
+import dapr.fines.fines.FineCalculator;
+import dapr.fines.vehicle.VehicleInfo;
+import dapr.fines.vehicle.VehicleRegistrationClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.time.format.DateTimeFormatter;
+
+import dapr.traffic.violation.SpeedingViolation;
+
+public class DefaultViolationProcessor implements ViolationProcessor {
+ private static final Logger log = LoggerFactory.getLogger(DefaultViolationProcessor.class);
+
+ private final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("LLLL, dd y");
+ private final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss");
+
+ private final FineCalculator fineCalculator;
+ private final VehicleRegistrationClient vehicleRegistrationClient;
+
+ public DefaultViolationProcessor(final FineCalculator fineCalculator,
+ final VehicleRegistrationClient vehicleRegistrationClient) {
+ this.fineCalculator = fineCalculator;
+ this.vehicleRegistrationClient = vehicleRegistrationClient;
+ }
+
+ public void processSpeedingViolation(final SpeedingViolation violation) {
+ int fine = fineCalculator.calculateFine(violation.excessSpeed());
+ String fineText = fine == -1 ? "to be decided by the prosecutor" : String.format("EUR %.2f", (float) fine);
+ final VehicleInfo vehicleInfo = vehicleRegistrationClient.getVehicleInfo(violation.licenseNumber());
+
+ final String fineMessage = constructLogMessage(violation, vehicleInfo, fineText);
+ log.info(fineMessage);
+ }
+
+ private String constructLogMessage(final SpeedingViolation violation, final VehicleInfo vehicleInfo, final String fineText) {
+ final String date = DATE_FORMAT.format(violation.timestamp());
+ final String time = TIME_FORMAT.format(violation.timestamp());
+
+ return String.format("""
+ Sent fine notification
+ \t\t\tTo %s, registered owner of license number %s.
+ \t\t\tViolation of %d km/h detected on the %s road on %s at %s.
+ \t\t\tFine: %s.%n
+ """,
+ vehicleInfo.ownerName(),
+ violation.licenseNumber(),
+ violation.excessSpeed(),
+ violation.roadId(),
+ date,
+ time,
+ fineText
+ );
+ }
+}
diff --git a/FineCollectionService/src/main/java/dapr/fines/violation/KafkaViolationConsumer.java b/FineCollectionService/src/main/java/dapr/fines/violation/KafkaViolationConsumer.java
index 2e804c1..e2b534b 100644
--- a/FineCollectionService/src/main/java/dapr/fines/violation/KafkaViolationConsumer.java
+++ b/FineCollectionService/src/main/java/dapr/fines/violation/KafkaViolationConsumer.java
@@ -14,8 +14,7 @@ public KafkaViolationConsumer(final ViolationProcessor violationProcessor) {
@KafkaListener(topics = "test", groupId = "test", containerFactory = "kafkaListenerContainerFactory")
public void listen(SpeedingViolation violation) {
-
- violationProcessor.processSpeedingViolation(violation);
+ violationProcessor.processSpeedingViolation(violation);
}
diff --git a/FineCollectionService/src/main/java/dapr/fines/violation/ViolationProcessor.java b/FineCollectionService/src/main/java/dapr/fines/violation/ViolationProcessor.java
index 69b242a..877537d 100644
--- a/FineCollectionService/src/main/java/dapr/fines/violation/ViolationProcessor.java
+++ b/FineCollectionService/src/main/java/dapr/fines/violation/ViolationProcessor.java
@@ -1,59 +1,7 @@
package dapr.fines.violation;
-import dapr.fines.fines.FineCalculator;
-import dapr.fines.vehicle.VehicleInfo;
-import dapr.fines.vehicle.VehicleRegistrationClient;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.stereotype.Component;
-
-import java.time.format.DateTimeFormatter;
import dapr.traffic.violation.SpeedingViolation;
-@Component
-public class ViolationProcessor {
- private static final Logger log = LoggerFactory.getLogger(ViolationProcessor.class);
-
- private final FineCalculator fineCalculator;
- private final VehicleRegistrationClient vehicleRegistrationClient;
-
- public ViolationProcessor(final FineCalculator fineCalculator,
- final VehicleRegistrationClient vehicleRegistrationClient) {
- this.fineCalculator = fineCalculator;
- this.vehicleRegistrationClient = vehicleRegistrationClient;
- }
-
- public void processSpeedingViolation(final SpeedingViolation violation) {
- var fine = fineCalculator.calculateFine(violation.excessSpeed());
- var fineText = fine == -1 ? "to be decided by the prosecutor" : String.format("EUR %.2f", (float) fine);
- var vehicleInfo = vehicleRegistrationClient.getVehicleInfo(violation.licenseNumber());
-
- // Send notification of fine by email
- // TODO
-
- log.info(constructLogMessage(violation, vehicleInfo, fineText));
- }
-
- private final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("LLLL, dd y");
- private final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss");
- private String constructLogMessage(final SpeedingViolation violation, final VehicleInfo vehicleInfo, final String fineText) {
- var date = DATE_FORMAT.format(violation.timestamp());
- var time = TIME_FORMAT.format(violation.timestamp());
-
- return String.format("""
- Sent fine notification
- \t\t\tTo %s, registered owner of license number %s.
- \t\t\tViolation of %d km/h detected on the %s road on %s at %s.
- \t\t\tFine: %s.%n
- """,
- vehicleInfo.ownerName(),
- violation.licenseNumber(),
- violation.excessSpeed(),
- violation.roadId(),
- date,
- time,
- fineText
- );
- }
-
+public interface ViolationProcessor {
+ void processSpeedingViolation(final SpeedingViolation violation);
}
diff --git a/FineCollectionService/src/main/resources/email/email-template.ftl b/FineCollectionService/src/main/resources/email/email-template.ftl
new file mode 100644
index 0000000..abcb2e2
--- /dev/null
+++ b/FineCollectionService/src/main/resources/email/email-template.ftl
@@ -0,0 +1,74 @@
+
+
+ Fine Collection Agency
+
+
+
+
+
Fine Collection Department
+
+
+
+
Dear ${customerName},
+
We are writing to inform you that you have an outstanding fine for speeding violation. This fine was incurred on ${fineDate} for the following reason:
+
You were driving a ${vehicleBrand} ${vehicleModel} with license number ${vehicleLicenseNumber} on ${road} at ${timeOfDay} on ${violationDate} and exceeded the maximum speed by ${excessSpeed} km/h.
+ <#if fineAmount != -1>
+
The fine amount is EUR ${fineAmount}.
+
Please pay this fine as soon as possible to avoid additional fees and legal action. You can pay online at our website or by visiting our office in The Hague.
+ <#else>
+
The fine amount has not yet been determined and will be decided by the prosecutor. A notice to appear in court will be sent shortly at your home address.
+ #if>
+
Thank you for your cooperation.
+
Sincerely,
+
Fine Collection Agency
+
+
+
+ Fine Collection Department
+ Phone: +31 123 456 789
+ Address: Some Street 123, 2511 CD The Hague
+ Email: fine@random-email.com
+ fine.random-domain.com
+
+
diff --git a/dapr/bindings-smtp.yaml b/dapr/bindings-smtp.yaml
new file mode 100644
index 0000000..d12cf72
--- /dev/null
+++ b/dapr/bindings-smtp.yaml
@@ -0,0 +1,16 @@
+apiVersion: daprio.io/v1alpha1
+kind: Component
+metadata:
+ name: smtp
+spec:
+ type: bindings.smtp
+ metadata:
+ - name: host
+ value: smtp.office365.com
+ - name: port
+ value: "587"
+ - name: username
+ value: ""
+ - name: password
+ value: ""
+ - emailFrom: ""
\ No newline at end of file
From 77909d84a7352c41801960246229049e45c53d6b Mon Sep 17 00:00:00 2001
From: Pierre Malarme
Date: Wed, 18 Jan 2023 18:07:30 +0100
Subject: [PATCH 2/3] Update email template
---
.../src/main/resources/email/email-template.ftl | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/FineCollectionService/src/main/resources/email/email-template.ftl b/FineCollectionService/src/main/resources/email/email-template.ftl
index abcb2e2..571be20 100644
--- a/FineCollectionService/src/main/resources/email/email-template.ftl
+++ b/FineCollectionService/src/main/resources/email/email-template.ftl
@@ -46,7 +46,7 @@
-
Fine Collection Department
+ Fine Collection Agency
@@ -65,7 +65,7 @@
- Fine Collection Department
+ Fine Collection Agency
Phone: +31 123 456 789
Address: Some Street 123, 2511 CD The Hague
Email: fine@random-email.com
From ad13a7f4758c76e14a8a311749c96866cbb708c1 Mon Sep 17 00:00:00 2001
From: Pierre Malarme
Date: Mon, 23 Jan 2023 11:12:35 +0100
Subject: [PATCH 3/3] Remove the old Email Generator that is not needed anymore
---
.../java/dapr/fines/fines/EmailGenerator.java | 172 ------------------
1 file changed, 172 deletions(-)
delete mode 100644 FineCollectionService/src/main/java/dapr/fines/fines/EmailGenerator.java
diff --git a/FineCollectionService/src/main/java/dapr/fines/fines/EmailGenerator.java b/FineCollectionService/src/main/java/dapr/fines/fines/EmailGenerator.java
deleted file mode 100644
index 2f6e0a6..0000000
--- a/FineCollectionService/src/main/java/dapr/fines/fines/EmailGenerator.java
+++ /dev/null
@@ -1,172 +0,0 @@
-package dapr.fines.fines;
-
-import dapr.fines.vehicle.VehicleInfo;
-import dapr.fines.violation.SpeedingViolation;
-import org.springframework.stereotype.Component;
-
-import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
-
-@Component
-public class EmailGenerator {
- private static final DateTimeFormatter LONG_DATE_FORMAT = DateTimeFormatter.ofPattern("EEE, MMMM dd, yyyy");
- private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("dd-MM-yyyy");
- private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss");
-
- private static final String EMAIL_TEMPLATE = """
-
-
-
-
-
-
-
-
Central Fine Collection Agency
-
-
- The Hague, %s
-
- Dear Mr. / Miss / Mrs. %s,
-
- We hereby inform you of the fact that a speeding violation was detected with a
- vehicle that is registered to you.
-
- The violation was detected by a speeding camera. We have a digital image of your
- vehicle committing the violation on record in our system. If requested by your
- solicitor, we will provide this image to you.
-
-
-
- Below you can find all the details of the violation.
-
-
- Vehicle information:
-
- | License number | %s |
- | Brand | %s |
- | Model | %s |
-
-
-
-
- Conditions during the violation:
-
- | Road | %s |
- | Date | %s |
- | Time of day | %s |
-
-
-
-
- Sanction:
-
- | Maximum speed violation | %s km/h |
- | Sanction amount | %s |
-
-
-
-
-
- Sanction handling:
-
- If the amount of the fine is to be determined by the prosecutor, you will receive a notice\s
- to appear in court shortly.
-
- Otherwise, you must pay the sanctioned fine within 8 weeks after the date of this\s
- email. If you fail to pay within 8 weeks, you will receive a first reminder email and the\s
- fine will be increased to 1.5x the original fine amount. If you fail to pay within 8 weeks\s
- after the first reminder, you will receive a second and last reminder email and the fine\s
- will be increased to 3x the original fine amount. If you fail to pay within 8 weeks\s
- after the second reminder, the case is turned over to the prosecutor and you will receive a\s
- notice to appear in court.
-
-
-
-
- Yours sincerely,
- The Central Fine Collection Agency
-
-
- """;
-
- public String createEmailBody(SpeedingViolation speedingViolation,
- VehicleInfo vehicleInfo,
- String fine) {
- var timestamp = LONG_DATE_FORMAT.format(LocalDateTime.now());
- var violationDate = DATE_FORMAT.format(speedingViolation.timestamp());
- var violationTime = DATE_FORMAT.format(speedingViolation.timestamp());
-
- return EMAIL_TEMPLATE.formatted(
- timestamp,
- vehicleInfo.ownerName(),
- speedingViolation.licenseNumber(),
- vehicleInfo.make(),
- vehicleInfo.model(),
- speedingViolation.roadId(),
- violationDate,
- violationTime,
- speedingViolation.excessSpeed(),
- fine
- );
- }
-}