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 + + + + + +
+

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.

+ +

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