Skip to content

Commit

Permalink
Merge pull request #1439 from betagouv/master
Browse files Browse the repository at this point in the history
MEP
  • Loading branch information
charlescd authored Sep 12, 2023
2 parents f5fdc76 + d765db8 commit ac92a8b
Show file tree
Hide file tree
Showing 11 changed files with 183 additions and 28 deletions.
6 changes: 1 addition & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@ name: Build, format & test workflow

on: [push]
env:
POSTGRESQL_ADDON_USER: signalconso
POSTGRESQL_ADDON_HOST: localhost
POSTGRESQL_ADDON_PORT: 5432
POSTGRESQL_ADDON_DB: test_signalconso
POSTGRESQL_ADDON_PASSWORD :
USER: signalconso
USE_TEXT_LOGS: true

jobs:
Expand Down
3 changes: 2 additions & 1 deletion app/config/EmailConfiguration.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ case class EmailConfiguration(
contactAddress: EmailAddress,
skipReportEmailValidation: Boolean,
emailProvidersBlocklist: List[String],
outboundEmailFilterRegex: Regex
outboundEmailFilterRegex: Regex,
maxRecipientsPerEmail: Int
)
2 changes: 2 additions & 0 deletions app/models/report/ReportDraft.scala
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ case class ReportDraft(
if (employeeConsumer) ReportStatus.LanceurAlerte
else if (!shouldBeVisibleToPro()) ReportStatus.NA
else if (companySiret.isEmpty) ReportStatus.NA
else if (companySiret.nonEmpty && companyAddress.flatMap(_.country).isDefined)
ReportStatus.NA // Company has a french SIRET but a foreign address, we can't send any letter to it
else ReportStatus.TraitementEnCours
}

Expand Down
22 changes: 12 additions & 10 deletions app/services/MailService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -82,17 +82,19 @@ class MailService(
): Future[Unit] = {
val filteredEmptyEmail: Seq[EmailAddress] = filterEmail(recipients)
NonEmptyList.fromList(filteredEmptyEmail.toList) match {
case None =>
case None => ()
case Some(filteredRecipients) =>
val emailRequest = EmailRequest(
from = mailFrom,
recipients = filteredRecipients,
subject = subject,
bodyHtml = bodyHtml,
attachments = attachments
)
// we launch this but don't wait for its completion
mailRetriesService.sendEmailWithRetries(emailRequest)
filteredRecipients.grouped(emailConfiguration.maxRecipientsPerEmail).foreach { groupedRecipients =>
val emailRequest = EmailRequest(
from = mailFrom,
recipients = groupedRecipients,
subject = subject,
bodyHtml = bodyHtml,
attachments = attachments
)
// we launch this but don't wait for its completion
mailRetriesService.sendEmailWithRetries(emailRequest)
}
}
Future.unit
}
Expand Down
89 changes: 85 additions & 4 deletions app/utils/Country.scala
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ object Country {
val Seychelles = Country("SC", "Seychelles", "Seychelles")
val SierraLeone = Country("SL", "Sierra Leone", "Sierra Leone")
val Singapour = Country("SG", "Singapour", "Singapore")
val Slovaquie = Country("SL", "Slovaquie", "Slovakia", european = true)
val Slovaquie = Country("SK", "Slovaquie", "Slovakia", european = true)
val Slovenie = Country("SI", "Slovénie", "Slovenia", european = true)
val Somalie = Country("SO", "Somalie", "Somalia")
val Soudan = Country("SD", "Soudan", "Sudan")
Expand All @@ -205,12 +205,54 @@ object Country {
val Ukraine = Country("UA", "Ukraine", "Ukraine")
val Uruguay = Country("UY", "Uruguay", "Uruguay")
val Vanuatu = Country("VU", "Vanuatu", "Vanuatu")
val Vatican = Country("VAT", "Vatican", "Vatican City")
val Vatican = Country("VA", "Vatican", "Vatican City")
val Venezuela = Country("VE", "Vénézuéla", "Venezuela")
val Vietnam = Country("VN", "Vietnam", "Vietnam")
val Yemen = Country("YE", "Yémen", "Yemen")
val Zambie = Country("ZM", "Zambie", "Zambia")
val Zimbabwe = Country("ZW", "Zimbabwé", "Zimbabwe")
val IlesFeroe = Country("FO", "Îles Féroé", "Faroe Islands")
val Svalbard = Country("SJ", "Svalbard et Île Jan Mayen", "Svalbard and Jan Mayen")
val IleBouvet = Country("BV", "Île Bouvet", "Bouvet Island")
val Jersey = Country("JE", "Jersey", "Jersey")
val IleMan = Country("IM", "Île Man", "Isle of Man")
val Guernsey = Country("GG", "Guernsey", "Bailiwick of Guernsey")
val Gibraltar = Country("GI", "Gibraltar", "Gibraltar")
val Aruba = Country("AW", "Aruba", "Aruba")
val HongKong = Country("HK", "Hong-Kong", "Hong Kong")
val Macao = Country("MO", "Macao", "Macau")
val Taiwan = Country("TW", "Taiwan", "Taiwan")
val Palestine = Country("PS", "État de Palestine", "State of Palestine")
val SainteHelene =
Country("SH", "Sainte-Hélène, Ascension et Tristan da Cunha", "Saint Helena, Ascension and Tristan da Cunha")
val TerritoireBritannique =
Country("IO", "Territoire britannique de l'océan Indien", "British Indian Ocean Territory")
val SaharaOccidental = Country("EH", "Sahara occidental", "Western Sahara")
val IlesViergesBritanniques = Country("VG", "Îles Vierges britanniques", "British Virgin Islands")
val IlesTurques = Country("TC", "Îles Turques-et-Caïques", "Turks and Caicos Islands")
val Montserrat = Country("MS", "Montserrat", "Montserrat")
val IlesCaimans = Country("KY", "Îles Caïmans", "Cayman Islands")
val Bermudes = Country("BM", "Bermudes", "Bermuda")
val Anguilla = Country("AI", "Anguilla", "Anguilla")
val GeorgieDuSud =
Country("GS", "Géorgie du Sud-et-les îles Sandwich du Sud", "South Georgia and the South Sandwich Islands")
val Falkland = Country("FK", "Îles Malouines ou Falkland", "Falkland Islands")
val Groenland = Country("GL", "Groenland", "Greenland")
val AntillesNeerlandaises = Country("AN", "Antilles néerlandaises", "Netherlands Antilles")
val IlesVierges = Country("VI", "Îles Vierges des États-Unis", "United States Virgin Islands")
val PortoRico = Country("PR", "Porto Rico", "Puerto Rico")
val Bonaire = Country("BQ", "Bonaire, Saint Eustache et Saba", "Bonaire, Sint Eustatius and Saba")
val Curacao = Country("CW", "Curaçao", "Curaçao")
val SaintMartinNL = Country("SX", "Saint-Martin (royaume des Pays-Bas)", "Sint Maarten (Dutch part)")
val Norfolk = Country("NF", "Île Norfolk", "Norfolk Island")
val MacDo = Country("HM", "Îles Heard-et-MacDonald", "Heard Island and McDonald Islands")
val Christmas = Country("CX", "Île Christmas", "Christmas Island")
val IlesCocos = Country("CC", "Îles Cocos", "Cocos (Keeling) Islands")
val Tokelau = Country("TK", "Tokelau", "Tokelau")
val Pitcairn = Country("PN", "Îles Pitcairn", "Pitcairn Islands")
val IlesMariannes = Country("MP", "Îles Mariannes du Nord", "Northern Mariana Islands")
val Guam = Country("GU", "Guam", "Guam")
val SamoaAmericaines = Country("AS", "Samoa américaines", "American Samoa")

val countries = List(
Afghanistan,
Expand Down Expand Up @@ -409,7 +451,46 @@ object Country {
Vietnam,
Yemen,
Zambie,
Zimbabwe
Zimbabwe,
IlesFeroe,
Svalbard,
IleBouvet,
Jersey,
IleMan,
Guernsey,
Gibraltar,
Aruba,
HongKong,
Macao,
Taiwan,
Palestine,
SainteHelene,
TerritoireBritannique,
SaharaOccidental,
IlesViergesBritanniques,
IlesTurques,
Montserrat,
IlesCaimans,
Bermudes,
Anguilla,
GeorgieDuSud,
Falkland,
Groenland,
AntillesNeerlandaises,
IlesVierges,
PortoRico,
Bonaire,
Curacao,
SaintMartinNL,
Norfolk,
MacDo,
Christmas,
IlesCocos,
Tokelau,
Pitcairn,
IlesMariannes,
Guam,
SamoaAmericaines
)

def fromCode(code: String) =
Expand All @@ -419,7 +500,7 @@ object Country {
countries.find(_.name == name).head

implicit val reads = new Reads[Country] {
def reads(json: JsValue): JsResult[Country] = json.validate[String].map(fromName(_))
def reads(json: JsValue): JsResult[Country] = json.validate[String].map(fromCode)
}
implicit val writes = Json.writes[Country]

Expand Down
10 changes: 5 additions & 5 deletions conf/common/database.conf
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ slick.dbs.default.db {
// Used for flyway

flyway {
host = ${POSTGRESQL_ADDON_HOST}
port = ${POSTGRESQL_ADDON_PORT}
database = ${POSTGRESQL_ADDON_DB},
user = ${POSTGRESQL_ADDON_USER}
password = ${POSTGRESQL_ADDON_PASSWORD}
host = ${?POSTGRESQL_ADDON_HOST}
port = ${?POSTGRESQL_ADDON_PORT}
database = ${?POSTGRESQL_ADDON_DB},
user = ${?POSTGRESQL_ADDON_USER}
password = ${?POSTGRESQL_ADDON_PASSWORD}
// DATA_LOSS / DESTRUCTIVE / BE AWARE ---- Keep to "false"
//Be careful when enabling this as it removes the safety net that ensures Flyway does not migrate the wrong database in case of a configuration mistake!
//This is useful for initial Flyway production deployments on projects with an existing DB.
Expand Down
3 changes: 3 additions & 0 deletions conf/common/mail.conf
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,7 @@ mail {
# Filter outbound emails on test / dev / local env
outbound-email-filter-regex = ".*"
outbound-email-filter-regex = ${?OUTBOUND_EMAIL_FILTER_REGEX}

// To prevent SMTPAddressFailedException 552 5.5.3 Maximum limit of 50 recipients reached
max-recipients-per-email = 40
}
15 changes: 14 additions & 1 deletion conf/test.application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,24 @@ test.db {
}


test.db.user = ${?POSTGRESQL_ADDON_USER}
test.db.user =${?USER}
test.db.host = "localhost"
test.db.port = 5432
test.db.name = "test_signalconso"

flyway {
host = "localhost"
port = 5432
database = "test_signalconso"
user = ${?USER}
password =password
// DATA_LOSS / DESTRUCTIVE / BE AWARE ---- Keep to "false"
//Be careful when enabling this as it removes the safety net that ensures Flyway does not migrate the wrong database in case of a configuration mistake!
//This is useful for initial Flyway production deployments on projects with an existing DB.
//See https://flywaydb.org/documentation/configuration/parameters/baselineOnMigrate for more information
baseline-on-migrate = false
}


slick.dbs.default.db.properties.url = "postgres://"${test.db.user}"@"${test.db.host}":"${test.db.port}"/"${test.db.name}
slick.dbs.default.db.connectionPool = "disabled"
Expand Down
3 changes: 2 additions & 1 deletion test/controllers/EmailValidationControllerSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ class EmailValidationControllerSpec(implicit ee: ExecutionEnv)
contactAddress = EmptyEmailAddress,
skipReportEmailValidation = skipValidation,
emailProvidersBlocklist = emailProviderBlocklist,
outboundEmailFilterRegex = ".*".r
outboundEmailFilterRegex = ".*".r,
maxRecipientsPerEmail = 40
)

}
Expand Down
3 changes: 2 additions & 1 deletion test/controllers/ReportControllerSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,8 @@ class ReportControllerSpec(implicit ee: ExecutionEnv) extends Specification with
EmailAddress("[email protected]"),
skipValidation,
List(""),
".*".r
".*".r,
40
)

}
Expand Down
55 changes: 55 additions & 0 deletions test/services/MailServiceSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,61 @@ class MailServiceSpecFilteredEmail(implicit ee: ExecutionEnv) extends BaseMailSe
}
}

class MailServiceSpecGroupForMaxRecipients(implicit ee: ExecutionEnv) extends BaseMailServiceSpec {

override def is = s2"""email must be grouped to prevent exceeding recipients limit $e1"""

def e1 = {

val recipients = List(
EmailAddress(s"[email protected]"),
EmailAddress(s"[email protected]"),
EmailAddress(s"[email protected]")
)

val mailService = new MailService(
mockMailRetriesService,
emailConfiguration = emailConfiguration.copy(maxRecipientsPerEmail = 2),
reportNotificationBlocklistRepo = components.reportNotificationBlockedRepository,
pdfService = components.pdfService,
attachmentService = components.attachmentService
)(
components.frontRoute,
executionContext
)

ProNewReportNotification(
NonEmptyList.of(EmailAddress(s"[email protected]"), EmailAddress(s"[email protected]")),
reportForSubsidiary
)

Await.result(
mailService.send(
ProNewReportNotification(
NonEmptyList.fromListUnsafe(recipients),
reportForSubsidiary
)
),
Duration.Inf
)

there was one(mockMailRetriesService).sendEmailWithRetries(
argThat((emailRequest: EmailRequest) =>
emailRequest.recipients.size == 2 && emailRequest.recipients.toList.containsSlice(
List(EmailAddress(s"[email protected]"), EmailAddress(s"[email protected]"))
)
)
)

there was one(mockMailRetriesService).sendEmailWithRetries(
argThat((emailRequest: EmailRequest) =>
emailRequest.recipients.size == 1 && emailRequest.recipients.head == EmailAddress(s"[email protected]")
)
)

}
}

//MailServiceSpecNoBlock
//MailServiceSpecSomeBlock
//MailServiceSpecAllBlock

0 comments on commit ac92a8b

Please sign in to comment.