Skip to content

Commit 594e63c

Browse files
authored
Merge pull request cfpb#1150 from nickgrippin/email-on-sign
Email on sign
2 parents 7ed9942 + 3de7d48 commit 594e63c

File tree

11 files changed

+152
-12
lines changed

11 files changed

+152
-12
lines changed

README.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,11 @@ docker-compose -f docker-dev.yml up
157157

158158
When finished, use `docker-compose down` to gracefully stop the running containers.
159159

160+
In order to not get Postgres connection error upon signing a submission, you may want to start up the keycloak image as well.
161+
```shell
162+
docker-compose up keycloak
163+
```
164+
Not having the keycloak image running will produce an error in the console, but will not cause the platform to crash.
160165

161166
#### Running the API
162167

@@ -214,13 +219,13 @@ The Filing API will run on `$(docker-machine ip):8080`
214219
The Public API will run on `$(docker-machine ip):8082`
215220

216221
By default, the `HDMA Platform` runs with a log level of `INFO`. This can be changed by establishing a different log level in the `HMDA_LOGLEVEL` environment variable.
217-
For the different logging options, see the [reference.conf](https://github.com/akka/akka/blob/master/akka-actor/src/main/resources/reference.conf#L38) default configuration file for `Akka`.
222+
For the different logging options, see the [reference.conf](https://github.com/akka/akka/blob/master/akka-actor/src/main/resources/reference.conf#L38) default configuration file for `Akka`.
218223

219224
#### To run the entire platform
220225

221226
1. Ensure you have a Docker Machine with sufficient resources, as described in the [Docker](#docker) section above.
222227

223-
1. Clone [hmda-platform-ui](https://github.com/cfpb/hmda-platform-ui) and
228+
1. Clone [hmda-platform-ui](https://github.com/cfpb/hmda-platform-ui) and
224229
[hmda-platform-auth](https://github.com/cfpb/hmda-platform-auth) into the same
225230
directory as hmda-platform.
226231

@@ -270,7 +275,7 @@ For the different logging options, see the [reference.conf](https://github.com/a
270275
**Note:** You must register with an email address from our whitelist of email domains.
271276
For convenience, `bank0.com` and `bank1.com` address should be available automatically.
272277

273-
1. Browse to the mock email server at https://192.168.99.100:8443/mail/, and select the
278+
1. Browse to the mock email server at https://192.168.99.100:8443/mail/, and select the
274279
verification link in the email found there. This should take you back to the HMDA
275280
filing web app, now logged in.
276281

@@ -281,7 +286,7 @@ For the different logging options, see the [reference.conf](https://github.com/a
281286

282287
##### Updating an existing system
283288

284-
If you've updated any of the hmda-platform services, and would like to see those
289+
If you've updated any of the hmda-platform services, and would like to see those
285290
changes reflected in the Docker Compose setup, the simplest way to do this is to
286291
rebuild everything from scratch. The following command should be executed from
287292
within the `hmda-platform` directory.

api/src/main/resources/application.conf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ hmda {
2727
timeout = 10
2828
timeout = ${?HMDA_HTTP_TIMEOUT}
2929
}
30+
mail {
31+
host = "mail_dev"
32+
host = ${?HMDA_MAIL_HOST}
33+
port = "25"
34+
port = ${?HMDA_MAIL_PORT}
35+
senderAddress = "no-reply@cfpb.gov"
36+
senderAddress = ${?HMDA_MAIL_SENDER_ADDRESS}
37+
}
3038
isDemo = false
3139
isDemo = ${?HMDA_IS_DEMO}
3240
panel {

api/src/main/scala/hmda/api/http/institutions/submissions/SubmissionSignPaths.scala

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package hmda.api.http.institutions.submissions
22

3+
import java.time.{ ZoneOffset, ZonedDateTime }
4+
35
import akka.actor.{ ActorRef, ActorSystem }
46
import akka.event.LoggingAdapter
57
import akka.pattern.ask
@@ -22,6 +24,15 @@ import spray.json.{ JsBoolean, JsFalse, JsObject, JsTrue }
2224

2325
import scala.util.{ Failure, Success }
2426
import scala.concurrent.{ ExecutionContext, Future }
27+
import javax.mail._
28+
import javax.mail.internet.{ InternetAddress, MimeMessage }
29+
30+
import com.typesafe.config.ConfigFactory
31+
import hmda.model.institution.Institution
32+
import hmda.persistence.model.HmdaSupervisorActor.FindActorByName
33+
import hmda.query.repository.KeyCloakRepository
34+
import hmda.query.view.institutions.InstitutionView
35+
import hmda.query.view.institutions.InstitutionView.GetInstitutionById
2536

2637
trait SubmissionSignPaths
2738
extends InstitutionProtocol
@@ -30,7 +41,8 @@ trait SubmissionSignPaths
3041
with EditResultsProtocol
3142
with HmdaCustomDirectives
3243
with RequestVerificationUtils
33-
with ValidationErrorConverter {
44+
with ValidationErrorConverter
45+
with KeyCloakRepository {
3446

3547
implicit val system: ActorSystem
3648
implicit val materializer: ActorMaterializer
@@ -44,7 +56,7 @@ trait SubmissionSignPaths
4456
val submissionId = SubmissionId(institutionId, period, id)
4557
timedGet { uri =>
4658
completeVerified(supervisor, querySupervisor, institutionId, period, id, uri) {
47-
completeWithSubmissionReceipt(supervisor, submissionId, uri)
59+
completeWithSubmissionReceipt(supervisor, submissionId, uri, signed = false)
4860
}
4961
} ~
5062
timedPost { uri =>
@@ -59,7 +71,7 @@ trait SubmissionSignPaths
5971
s <- actor ? hmda.persistence.processing.ProcessingMessages.Signed
6072
} yield s
6173
onComplete(fSign) {
62-
case Success(Some(_)) => completeWithSubmissionReceipt(supervisor, submissionId, uri)
74+
case Success(Some(_)) => completeWithSubmissionReceipt(supervisor, submissionId, uri, signed = true)
6375
case Success(_) =>
6476
val errorResponse = ErrorResponse(400, "Illegal State: Submission must be Validated or ValidatedWithErrors to sign", uri.path)
6577
complete(ToResponseMarshallable(StatusCodes.BadRequest -> errorResponse))
@@ -76,7 +88,7 @@ trait SubmissionSignPaths
7688
}
7789
}
7890

79-
private def completeWithSubmissionReceipt(supervisor: ActorRef, subId: SubmissionId, uri: Uri)(implicit ec: ExecutionContext) = {
91+
private def completeWithSubmissionReceipt(supervisor: ActorRef, subId: SubmissionId, uri: Uri, signed: Boolean)(implicit ec: ExecutionContext) = {
8092
val fSubmissionsActor = (supervisor ? FindSubmissions(SubmissionPersistence.name, subId.institutionId, subId.period)).mapTo[ActorRef]
8193
val fSubmission = for {
8294
a <- fSubmissionsActor
@@ -85,8 +97,69 @@ trait SubmissionSignPaths
8597

8698
onComplete(fSubmission) {
8799
case Success(sub) =>
100+
if (signed) {
101+
emailSignature(supervisor, sub)
102+
}
88103
complete(ToResponseMarshallable(Receipt(sub.end, sub.receipt, sub.status)))
89104
case Failure(error) => completeWithInternalError(uri, error)
90105
}
91106
}
107+
108+
private def emailSignature(supervisor: ActorRef, submission: Submission)(implicit ec: ExecutionContext) = {
109+
val emails = findEmailsById(submission.id.institutionId)
110+
val querySupervisor = system.actorSelection("/user/query-supervisor/singleton")
111+
val fInstitutionsActor = (querySupervisor ? FindActorByName(InstitutionView.name)).mapTo[ActorRef]
112+
val fName = for {
113+
a <- fInstitutionsActor
114+
i <- (a ? GetInstitutionById(submission.id.institutionId)).mapTo[Institution]
115+
e <- emails
116+
} yield (i.respondent.name, e)
117+
118+
fName.onComplete({
119+
case Success((instName, emailSeq)) =>
120+
emailSeq.foreach(t => {
121+
val username = t._1 + " " + t._2
122+
sendMail(t._3, username, submission, instName)
123+
})
124+
case Failure(error) => log.error(error, s"An error has occured retrieving the institution name for ID ${submission.id.institutionId}")
125+
})
126+
}
127+
128+
private def sendMail(address: String, username: String, submission: Submission, instName: String) = {
129+
val config = ConfigFactory.load()
130+
val host = config.getString("hmda.mail.host")
131+
val port = config.getString("hmda.mail.port")
132+
val senderAddress = config.getString("hmda.mail.senderAddress")
133+
134+
val properties = System.getProperties
135+
properties.put("mail.smtp.host", host)
136+
properties.put("mail.smtp.port", port)
137+
138+
val session = Session.getDefaultInstance(properties)
139+
val message = new MimeMessage(session)
140+
141+
val date = getFormattedDate
142+
143+
val text = s"$username,\n\nCongratulations, you've completed filing your HMDA data for $instName for filing period ${submission.id.period}.\n" +
144+
s"We received your filing on: $date\n" +
145+
s"Your receipt is: ${submission.receipt}"
146+
message.setFrom(new InternetAddress(senderAddress))
147+
message.setRecipients(Message.RecipientType.TO, address)
148+
message.setSubject("HMDA Filing Successful")
149+
message.setText(text)
150+
151+
log.info(s"Sending message to $address with the message \n$text")
152+
Transport.send(message)
153+
}
154+
155+
private def getFormattedDate: String = {
156+
val offset = ZoneOffset.ofHours(-5)
157+
val zonedTime = ZonedDateTime.now(offset)
158+
159+
val day = zonedTime.getDayOfMonth
160+
val month = zonedTime.getMonthValue
161+
val year = zonedTime.getYear
162+
163+
s"$month/$day/$year"
164+
}
92165
}

build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ lazy val api = (project in file("api"))
211211
oldStrategy(x)
212212
},
213213
parallelExecution in Test := false,
214-
libraryDependencies ++= httpDeps
214+
libraryDependencies ++= httpDeps ++ Seq(javaMail)
215215
)
216216
)
217217
.dependsOn(persistenceModel % "compile->compile;test->test")

docker-compose.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ services:
4545
APP_URL: https://192.168.99.100
4646
HMDA_API: https://192.168.99.100:4443/hmda
4747
KEYCLOAK_URL: https://192.168.99.100:8443/auth/realms/hmda
48-
# lb settings
48+
# lb settings
4949
VIRTUAL_HOST: 'http://*:80/*, https://*:443/*'
5050
EXCLUDE_PORTS: '443' # use lb's ssl instead of ui's nginx
5151
FORCE_SSL: 'true' # redirect 80 to 443
@@ -82,6 +82,8 @@ services:
8282

8383
keycloak_db:
8484
image: postgres:9.6.1
85+
ports:
86+
- '5433:5432'
8587
environment:
8688
POSTGRES_DB: keycloak
8789
POSTGRES_USER: keycloak

docker-dev.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ services:
1111
ports:
1212
- '9042:9042'
1313
- '7000:7000'
14-
- '7199:7199'
14+
- '7199:7199'

persistence-model/src/main/scala/hmda/persistence/model/HmdaPersistentActor.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ abstract class HmdaPersistentActor extends PersistentActor with HmdaActor {
2424
}
2525

2626
override def receiveCommand: Receive = {
27-
case ReceiveTimeout =>
27+
case ReceiveTimeout => {
28+
log.info("Received Timeout, sending Shutdown message. Thread: " + Thread.currentThread().getName)
2829
self ! Shutdown
30+
}
2931

3032
case Shutdown =>
3133
context stop self

project/Dependencies.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,5 @@ object Dependencies {
4040
val cassandraUnit = "org.cassandraunit" % "cassandra-unit" % Version.cassandraUnit
4141
val alpakkaCassandra = "com.lightbend.akka" %% "akka-stream-alpakka-cassandra" % Version.alpakka
4242
val cassandraDriver = "com.datastax.cassandra" % "cassandra-driver-core" % Version.cassandraDriver
43+
val javaMail = "javax.mail" % "mail" % Version.javaMail
4344
}

project/Version.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ object Version {
2222
val cassandraUnit = "3.1.3.2"
2323
val alpakka = "0.9"
2424
val cassandraDriver = "3.2.0"
25+
val javaMail = "1.4.7"
2526
}

query/src/main/resources/application.conf

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,26 @@ hmda {
3636
// }
3737
//}
3838

39+
db {
40+
driver = "slick.driver.PostgresDriver$"
41+
db {
42+
driver = org.postgresql.Driver
43+
host = "192.168.99.100"
44+
host = ${?KEYCLOAK_HOST}
45+
port = "5433"
46+
port = ${?KEYCLOAK_PORT}
47+
database = "keycloak"
48+
database = ${?KEYCLOAK_DATABASE}
49+
url = "jdbc:postgresql://"${db.db.host}":"${db.db.port}"/"${db.db.database}
50+
url = ${?KEYCLOAK_URL_OVERRIDE}
51+
user = "keycloak"
52+
user = ${?KEYCLOAK_USER}
53+
password = "password"
54+
password = ${?KEYCLOAK_PASSWORD}
55+
connectionPool = disabled
56+
}
57+
}
58+
3959
cassandra {
4060
host = "192.168.99.100"
4161
host = ${?CASSANDRA_CLUSTER_HOSTS}

0 commit comments

Comments
 (0)