Skip to content

Commit

Permalink
OK-439 lähetysten haku aikavälillä (#69)
Browse files Browse the repository at this point in the history
* Mui DateTimePicker aikavälin rajaukseen kälikomponentit ja vähän tyylejä
* aikarajaus kantahakuun, parametrien välitys frontista bäkkärille ja validointi
* päivitetty kirjastoriippuvuudet
* korvattu date-fns-tz dayjs:lla
  • Loading branch information
marjakari authored Oct 31, 2024
1 parent 5286537 commit 86cc71e
Show file tree
Hide file tree
Showing 27 changed files with 1,876 additions and 2,666 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import fi.oph.viestinvalitys.raportointi.resource.RaportointiAPIConstants.*
import fi.oph.viestinvalitys.raportointi.resource.{ParametriUtil, RaportointiAPIConstants}
import fi.oph.viestinvalitys.raportointi.integration.OrganisaatioOid

import java.time.Instant
import java.util.Optional
import scala.util.matching.Regex

Expand All @@ -20,7 +21,9 @@ case class LahetyksetParams(alkaen: Optional[String],
organisaatio: Optional[String],
viesti: Optional[String],
palvelu: Optional[String],
lahettaja: Optional[String])
lahettaja: Optional[String],
hakuAlkaen: Optional[String],
hakuPaattyen: Optional[String])
object LahetyksetParamValidator {

def validateAlkaenUUID(alkaen: Optional[String]): Set[String] =
Expand All @@ -44,6 +47,16 @@ object LahetyksetParamValidator {
if (emailParam.isPresent && validatedEmail.isEmpty) Left(virheet.incl(errorMessage)) else Right(virheet))
.fold(l => l, r => r)

def validateHakuAikavaliParams(hakuAlkaenParam: Optional[String], hakuPaattyenParam: Optional[String]): Set[String] =
val validatedHakuAlkaen = ParametriUtil.asInstant(hakuAlkaenParam)
val validatedHakuPaattyen = ParametriUtil.asInstant(hakuPaattyenParam)
Right(Set.empty.asInstanceOf[Set[String]])
.flatMap(virheet =>
if (hakuAlkaenParam.isPresent && (validatedHakuAlkaen.isEmpty || validatedHakuAlkaen.get.isAfter(Instant.now))) Left(virheet.incl(HAKU_ALKAEN_INVALID)) else Right(virheet))
.flatMap(virheet =>
if (hakuPaattyenParam.isPresent && (validatedHakuPaattyen.isEmpty || validatedHakuPaattyen.get.isAfter(Instant.now) || (hakuAlkaenParam.isPresent && !validatedHakuPaattyen.get.isAfter(validatedHakuAlkaen.get)))) Left(virheet.incl(HAKU_PAATTYEN_INVALID)) else Right(virheet))
.fold(l => l, r => r)

def validateEnintaan(enintaan: Optional[String], min: Int, max: Int, errorMessage: String): Set[String] =
val enintaanInt = ParametriUtil.asInt(enintaan)
Right(Set.empty.asInstanceOf[Set[String]])
Expand Down Expand Up @@ -98,6 +111,7 @@ object LahetyksetParamValidator {
validateOrganisaatio(params.organisaatio),
validateHakusanaParam(params.viesti),
validateHakusanaParam(params.palvelu),
validateLahettajaParam(params.lahettaja)
validateLahettajaParam(params.lahettaja),
validateHakuAikavaliParams(params.hakuAlkaen, params.hakuPaattyen)
).flatten
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ class LahetysResource {
@RequestParam(name = VIESTI_SISALTO_PARAM_NAME, required = false) viesti: Optional[String],
@RequestParam(name = PALVELU_PARAM_NAME, required = false) palvelu: Optional[String],
@RequestParam(name = LAHETTAJA_PARAM_NAME, required = false) lahettaja: Optional[String],
@RequestParam(name = HAKU_ALKAEN_PARAM_NAME, required = false) hakuAlkaen: Optional[String],
@RequestParam(name = HAKU_PAATTYEN_PARAM_NAME, required = false) hakuPaattyen: Optional[String],
request: HttpServletRequest): ResponseEntity[PalautaLahetyksetResponse] =
val securityOperaatiot = new SecurityOperaatiot
val kantaOperaatiot = new KantaOperaatiot(DbUtil.database)
Expand All @@ -65,7 +67,7 @@ class LahetysResource {
else
Right(None))
.flatMap(_ =>
val virheet = LahetyksetParamValidator.validateLahetyksetParams(LahetyksetParams(alkaen, enintaan, vastaanottajanEmail, organisaatio, viesti, palvelu, lahettaja))
val virheet = LahetyksetParamValidator.validateLahetyksetParams(LahetyksetParams(alkaen, enintaan, vastaanottajanEmail, organisaatio, viesti, palvelu, lahettaja, hakuAlkaen, hakuPaattyen))
if (!virheet.isEmpty)
Left(ResponseEntity.status(HttpStatus.BAD_REQUEST).body(PalautaLahetyksetFailureResponse(virheet.asJava)))
else
Expand All @@ -82,7 +84,9 @@ class LahetysResource {
vastaanottajaHakuLauseke = vastaanottajanEmail.toScala,
sisaltoHakuLauseke = viesti.toScala,
lahettavaPalveluHakuLauseke = palvelu.toScala,
lahettajaHakuLauseke = lahettaja.toScala)
lahettajaHakuLauseke = lahettaja.toScala,
hakuAlkaen = ParametriUtil.asInstant(hakuAlkaen),
hakuPaattyen = ParametriUtil.asInstant(hakuPaattyen))
if (lahetykset.isEmpty)
// on ok tilanne että haku ei palauta tuloksia
Left(ResponseEntity.status(HttpStatus.OK).body(PalautaLahetyksetSuccessResponse(Seq.empty.asJava, Optional.empty)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ object RaportointiAPIConstants {
final val VIESTI_SISALTO_PARAM_NAME = "viesti"
final val PALVELU_PARAM_NAME = "palvelu"
final val LAHETTAJA_PARAM_NAME = "lahettaja"
final val HAKU_ALKAEN_PARAM_NAME = "hakuAlkaen"
final val HAKU_PAATTYEN_PARAM_NAME = "hakuPaattyen"

final val VIESTI_PATH = VERSIONED_RAPORTOINTI_API_PREFIX + "/viesti"
final val VIESTITUNNISTE_PARAM_NAME = "viestiTunniste"
Expand Down Expand Up @@ -97,4 +99,6 @@ object RaportointiAPIConstants {
final val LAHETYKSET_ENINTAAN_INVALID = ENINTAAN_PARAM_NAME + "-parametri: Arvon pitää olla numero väliltä " + LAHETYKSET_ENINTAAN_MIN_STR + "-" + LAHETYKSET_ENINTAAN_MAX_STR
final val VASTAANOTTAJA_INVALID = VASTAANOTTAJA_PARAM_NAME + "-parametri: Tunniste ei ole validi sähköpostiosoite"
final val TILA_INVALID = TILA_PARAM_NAME + "-parametri: Tunniste ei ole validi vastaanoton tila"
final val HAKU_ALKAEN_INVALID = HAKU_ALKAEN_PARAM_NAME + "-parametri: Arvon pitää olla päivämäärä ja kellonaika ennen nykyhetkeä"
final val HAKU_PAATTYEN_INVALID = HAKU_PAATTYEN_PARAM_NAME + "-parametri: Arvon pitää olla päivämäärä ja kellonaika ennen hakuehtona olevaa alkuaikaa"
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import fi.oph.viestinvalitys.raportointi.resource.RaportointiAPIConstants
import fi.oph.viestinvalitys.raportointi.resource.RaportointiAPIConstants.*
import org.junit.jupiter.api.{Assertions, Test}

import java.time.Instant
import java.time.temporal.ChronoUnit
import java.util.{Optional, UUID}
import scala.util.Random

@Test
class LahetyksetValidatorTest {
Expand Down Expand Up @@ -37,7 +40,7 @@ class LahetyksetValidatorTest {
Assertions.assertEquals(Set(TILA_INVALID), LahetyksetParamValidator.validateRaportointiTila(Optional.of(VastaanottajanTila.ODOTTAA.toString), TILA_INVALID))
}

@Test def testValidateAlkaeUUIDParam(): Unit = {
@Test def testValidateAlkaenUUIDParam(): Unit = {
Assertions.assertEquals(Set.empty, LahetyksetParamValidator.validateAlkaenUUID(Optional.of(UUID.randomUUID().toString)))
Assertions.assertEquals(Set.empty, LahetyksetParamValidator.validateAlkaenUUID(Optional.empty()))
Assertions.assertEquals(Set(ALKAEN_UUID_TUNNISTE_INVALID), LahetyksetParamValidator.validateAlkaenUUID(Optional.of("foo")))
Expand All @@ -50,4 +53,27 @@ class LahetyksetValidatorTest {
Assertions.assertEquals(Set(ORGANISAATIO_INVALID), LahetyksetParamValidator.validateOrganisaatio(Optional.of("1.2.246.562.29.00000000000000032799")))
Assertions.assertEquals(Set(ORGANISAATIO_INVALID), LahetyksetParamValidator.validateOrganisaatio(Optional.of("foo")))
}

@Test def testValidateHakusanaParam(): Unit = {
Assertions.assertEquals(Set.empty, LahetyksetParamValidator.validateHakusanaParam(Optional.of("opintopolku")))
Assertions.assertEquals(Set.empty, LahetyksetParamValidator.validateHakusanaParam(Optional.empty()))
Assertions.assertEquals(Set(HAKUSANA_INVALID), LahetyksetParamValidator.validateHakusanaParam(Optional.of("haku")))
Assertions.assertEquals(Set(HAKUSANA_INVALID), LahetyksetParamValidator.validateHakusanaParam(Optional.of(Random.nextString(HAKUSANA_MAX_LENGTH+1))))
}

@Test def testValidateHakuAikavaliParams(): Unit = {
val future = Instant.now.plus(1, ChronoUnit.HOURS).toString
val validAlku = Instant.now.minus(1, ChronoUnit.HOURS).toString
val validLoppu = Instant.now.minus(30, ChronoUnit.MINUTES).toString
Assertions.assertEquals(Set.empty, LahetyksetParamValidator.validateHakuAikavaliParams(Optional.of(validAlku), Optional.of(validLoppu)))
Assertions.assertEquals(Set.empty, LahetyksetParamValidator.validateHakuAikavaliParams(Optional.of(validAlku), Optional.empty()))
Assertions.assertEquals(Set.empty, LahetyksetParamValidator.validateHakuAikavaliParams(Optional.empty(), Optional.empty()))
Assertions.assertEquals(Set.empty, LahetyksetParamValidator.validateHakuAikavaliParams(Optional.empty(), Optional.of(validLoppu)))
Assertions.assertEquals(Set(HAKU_ALKAEN_INVALID), LahetyksetParamValidator.validateHakuAikavaliParams(Optional.of(future), Optional.empty()))
Assertions.assertEquals(Set(HAKU_ALKAEN_INVALID), LahetyksetParamValidator.validateHakuAikavaliParams(Optional.of("Tue, 01 Oct 2024 05:00:00 GMT"), Optional.empty()))
Assertions.assertEquals(Set(HAKU_ALKAEN_INVALID), LahetyksetParamValidator.validateHakuAikavaliParams(Optional.of("Tue, 01 Oct 2024 05:00:00 GMT"), Optional.of("foo")))
Assertions.assertEquals(Set(HAKU_PAATTYEN_INVALID), LahetyksetParamValidator.validateHakuAikavaliParams(Optional.of(validAlku), Optional.of("foo")))
Assertions.assertEquals(Set(HAKU_PAATTYEN_INVALID), LahetyksetParamValidator.validateHakuAikavaliParams(Optional.empty, Optional.of(future)))
Assertions.assertEquals(Set(HAKU_PAATTYEN_INVALID), LahetyksetParamValidator.validateHakuAikavaliParams(Optional.of(Instant.now.minus(1, ChronoUnit.HOURS).toString), Optional.of(Instant.now.minus(2, ChronoUnit.HOURS).toString)))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ class KantaOperaatiot(db: JdbcBackend.JdbcDatabaseDef) {
maskit.foreach(maski => o = o.replace(maski._1, ""))
o
}

// poistetaan sisällöstä salaisuudet
val sisalto_sanitized = {
var s = sisalto
Expand Down Expand Up @@ -812,6 +812,14 @@ class KantaOperaatiot(db: JdbcBackend.JdbcDatabaseDef) {
lahetys_tunniste = ${lahetysTunniste.map(t => t.toString).getOrElse("")}::uuid))
"""

def getLahetysHakuLausekkeet(hakuAlkaen: Option[Instant], hakuPaattyen: Option[Instant]) =
sql"""
AND (${hakuAlkaen.isEmpty} OR
lahetykset.luotu::timestamptz >= ${hakuAlkaen.map(a => a.toString)}::timestamptz)
AND
(${hakuPaattyen.isEmpty} OR lahetykset.luotu::timestamptz <= ${hakuPaattyen.map(p => p.toString)}::timestamptz)
"""

/**
* Hakee lähetykset järjestettynä luontipäivämäärän mukaan (uusin ensin) perustuen annettuihin hakukriteereihin.
*
Expand Down Expand Up @@ -846,7 +854,9 @@ class KantaOperaatiot(db: JdbcBackend.JdbcDatabaseDef) {
vastaanottajaHakuLauseke: Option[String] = Option.empty,
lahettajaHakuLauseke: Option[String] = Option.empty,
metadataHakuLausekkeet: Option[Map[String, Seq[String]]] = Option.empty,
lahettavaPalveluHakuLauseke: Option[String] = Option.empty): (Seq[Lahetys], Boolean) =
lahettavaPalveluHakuLauseke: Option[String] = Option.empty,
hakuAlkaen: Option[Instant] = Option.empty,
hakuPaattyen: Option[Instant] = Option.empty): (Seq[Lahetys], Boolean) =
val lahetykset = Await.result(db.run(
(sql"""
SELECT DISTINCT
Expand All @@ -866,6 +876,8 @@ class KantaOperaatiot(db: JdbcBackend.JdbcDatabaseDef) {
concat
getViestienHakuLausekkeet(Option.empty, kayttooikeusTunnisteet, organisaatiot, sisaltoHakuLauseke,
vastaanottajaHakuLauseke, lahettajaHakuLauseke, metadataHakuLausekkeet, lahettavaPalveluHakuLauseke)
concat
getLahetysHakuLausekkeet(hakuAlkaen, hakuPaattyen)
concat
sql"""
AND (${alkaen.isEmpty} OR lahetys_tunniste<${alkaen.map(a => a.toString).getOrElse(null)}::uuid)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import slick.jdbc.JdbcBackend.Database
import slick.jdbc.PostgresProfile.api.*

import java.time.Instant
import java.time.temporal.ChronoUnit
import java.util.UUID
import java.util.concurrent.Executors
import scala.concurrent.duration.DurationInt
Expand Down Expand Up @@ -852,6 +853,9 @@ class KantaOperaatiotTest {
// haku lähetyksen 1 lähettävällä palvelulla palauttaa lähetyksen 1
Assertions.assertEquals(Seq(lahetys1), kantaOperaatiot.searchLahetykset(lahettavaPalveluHakuLauseke = Option.apply("lahettavaPalvelu1"))._1)

// haku aikarajauksella palauttaa lähetykset aikavälillä
Assertions.assertEquals(Seq(lahetys2, lahetys1), kantaOperaatiot.searchLahetykset(hakuAlkaen = Option.apply(Instant.now.minus(1, ChronoUnit.HOURS)))._1)

@Test def testHaeLahetyksetIlmanOikeuksia(): Unit =
val lahetys1 = tallennaLahetys(lahettavaPalvelu = "lahettavaPalvelu1");
val lahetys2 = tallennaLahetys(lahettavaPalvelu = "lahettavaPalvelu2");
Expand Down Expand Up @@ -901,6 +905,11 @@ class KantaOperaatiotTest {
Assertions.assertEquals(Seq.empty, kantaOperaatiot.searchLahetykset(kayttooikeusTunnisteet = Option.apply(Set.empty),
lahettavaPalveluHakuLauseke = Option.apply("lahettavaPalvelu1"))._1)

// haku aikarajauksella ei palauta mitään
Assertions.assertEquals(Seq.empty, kantaOperaatiot.searchLahetykset(kayttooikeusTunnisteet = Option.apply(Set.empty),
hakuAlkaen = Option.apply(Instant.now.minus(1, ChronoUnit.HOURS)))._1)


@Test def testHaeLahetyksetOikeuksilla(): Unit =
val lahetysOikeudetMatch = tallennaLahetys(lahettavaPalvelu = "lahettavaPalvelu1", lahettavanVirkailijanOID = Some(LAHETTAJA_OID1));
val lahetysOikeudetEiMatch = tallennaLahetys(lahettavaPalvelu = "lahettavaPalvelu2", lahettavanVirkailijanOID = Some(LAHETTAJA_OID2));
Expand Down Expand Up @@ -986,6 +995,27 @@ class KantaOperaatiotTest {
Assertions.assertEquals(Seq.empty, kantaOperaatiot.searchLahetykset(
kayttooikeusTunnisteet = Option.apply(kayttooikeusTunnisteet), lahettavaPalveluHakuLauseke = Option.apply("lahettavaPalvelu33"))._1)

// haku aikavälillä palauttaa lähetykset joihin oikeudet ja jotka osuvat aikavälille
Assertions.assertEquals(Seq(lahetysOikeudetEiMatch, lahetysOikeudetMatch), kantaOperaatiot.searchLahetykset(
kayttooikeusTunnisteet = Option.apply(kayttooikeusTunnisteet),
hakuAlkaen = Option.apply(Instant.now.minus(1, ChronoUnit.HOURS)), hakuPaattyen = Option.apply(Instant.now))._1)

// haku aikavälillä jossa ei ole lähetyksiä ei palauta mitään - lasketaan sen varaan että data alustettu yli nanosekuntia aiemmin
Assertions.assertEquals(Seq.empty, kantaOperaatiot.searchLahetykset(
kayttooikeusTunnisteet = Option.apply(kayttooikeusTunnisteet),
hakuAlkaen = Option.apply(Instant.now.minus(1, ChronoUnit.NANOS)), hakuPaattyen = Option.apply(Instant.now))._1)

// haku pelkällä alkamissajalla
Assertions.assertEquals(Seq(lahetysOikeudetEiMatch, lahetysOikeudetMatch), kantaOperaatiot.searchLahetykset(
kayttooikeusTunnisteet = Option.apply(kayttooikeusTunnisteet),
hakuAlkaen = Option.apply(Instant.now.minus(1, ChronoUnit.HOURS)), hakuPaattyen = Option.empty)._1)

// haku pelkällä päättymisajalla
Assertions.assertEquals(Seq.empty, kantaOperaatiot.searchLahetykset(
kayttooikeusTunnisteet = Option.apply(kayttooikeusTunnisteet),
hakuAlkaen = Option.empty, hakuPaattyen = Option.apply(Instant.now.minus(1, ChronoUnit.HOURS)))._1)


/**
* Tämä testi dokumentoi tekstihaun vaatimukset, päivitetään jos speksi muuttuu
*/
Expand Down
Loading

0 comments on commit 86cc71e

Please sign in to comment.