Skip to content

Commit

Permalink
IS-2871: Add possibility to search on name
Browse files Browse the repository at this point in the history
  • Loading branch information
vetlesolgaard committed Dec 16, 2024
1 parent ba3e09d commit 32a51d9
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ fun Route.registerPersonoversiktApiV2(
?: throw java.lang.IllegalArgumentException("No Authorization header supplied")
val searchQuery = call.receive<SearchQueryDTO>().toSearchQuery()

val searchResult = personoversiktSearchService.searchSykmeldt(searchQuery = searchQuery)
val searchResult = personoversiktSearchService.searchSykmeldt(search = searchQuery)
val fnrWithVeilederAccess = veilederTilgangskontrollClient.veilederPersonAccessListMedOBO(
personIdentNumberList = searchResult.map { it.fnr },
personidenter = searchResult.map { it.fnr },
token = token,
callId = callId,
) ?: emptyList()
Expand Down Expand Up @@ -94,7 +94,7 @@ fun Route.registerPersonoversiktApiV2(

val personFnrListWithVeilederAccess: List<String> =
veilederTilgangskontrollClient.veilederPersonAccessListMedOBO(
personIdentNumberList = personOversiktStatusList.map { it.fnr },
personidenter = personOversiktStatusList.map { it.fnr },
token = token,
callId = callId,
) ?: emptyList()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
package no.nav.syfo.personstatus.api.v2.model

import no.nav.syfo.personstatus.domain.Initials
import no.nav.syfo.personstatus.domain.SearchQuery
import no.nav.syfo.personstatus.domain.Name
import no.nav.syfo.personstatus.domain.Search
import java.time.LocalDate

data class SearchQueryDTO(
val initials: String?,
val birthdate: LocalDate,
val initials: String? = null,
val name: String? = null,
val birthdate: LocalDate?,
) {
fun toSearchQuery(): SearchQuery = SearchQuery(
birthdate = birthdate,
initials = Initials(
initials
)
)
fun toSearchQuery(): Search =
if (name != null && birthdate != null) {
Search.ByNameAndDate(
name = Name(name),
birthdate = birthdate
)
} else if (initials != null && birthdate != null) {
Search.ByInitialsAndDate(initials = Initials(initials), birthdate = birthdate)
} else if (name != null) {
Search.ByName(name = Name(name))
} else if (birthdate != null) {
Search.ByDate(birthdate = birthdate)
} else {
throw IllegalArgumentException("SearchQueryDTO values did not conform to allowed search rules")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import no.nav.syfo.oppfolgingstilfelle.domain.Oppfolgingstilfelle
import no.nav.syfo.personstatus.api.v2.model.VeilederTildelingHistorikkDTO
import no.nav.syfo.personstatus.domain.PersonIdent
import no.nav.syfo.personstatus.domain.PersonOversiktStatus
import no.nav.syfo.personstatus.domain.SearchQuery
import no.nav.syfo.personstatus.domain.Search
import no.nav.syfo.personstatus.domain.VeilederBrukerKnytning

interface IPersonOversiktStatusRepository {
Expand Down Expand Up @@ -38,7 +38,7 @@ interface IPersonOversiktStatusRepository {

fun updatePersonstatusesWithNavnAndFodselsdato(personer: List<PersonOversiktStatus>): List<Result<PersonOversiktStatus>>

fun searchPerson(searchQuery: SearchQuery): List<PersonOversiktStatus>
fun searchPerson(search: Search): List<PersonOversiktStatus>

fun updatePersonOversiktStatusOppfolgingstilfelle(
personstatus: PersonOversiktStatus,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package no.nav.syfo.personstatus.application

import no.nav.syfo.personstatus.domain.PersonOversiktStatus
import no.nav.syfo.personstatus.domain.SearchQuery
import no.nav.syfo.personstatus.domain.Search
import org.slf4j.LoggerFactory
import kotlin.jvm.java
import kotlin.time.measureTimedValue

class PersonoversiktSearchService(
private val personoversiktStatusRepository: IPersonOversiktStatusRepository,
) {
fun searchSykmeldt(searchQuery: SearchQuery): List<PersonOversiktStatus> {
fun searchSykmeldt(search: Search): List<PersonOversiktStatus> {
val (searchResult, duration) = measureTimedValue {
personoversiktStatusRepository.searchPerson(searchQuery = searchQuery)
personoversiktStatusRepository.searchPerson(search = search)
}

log.info("Completed search for sykmeldt in repository, got ${searchResult.size} results in ${duration.inWholeMilliseconds} ms")
Expand Down
24 changes: 24 additions & 0 deletions src/main/kotlin/no/nav/syfo/personstatus/domain/Search.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package no.nav.syfo.personstatus.domain

import java.time.LocalDate

sealed class Search {
data class ByName(val name: Name) : Search()
data class ByNameAndDate(val name: Name, val birthdate: LocalDate) : Search()
data class ByDate(val birthdate: LocalDate) : Search()
data class ByInitialsAndDate(val initials: Initials, val birthdate: LocalDate) : Search()
}

@JvmInline
value class Initials(val value: String) {
init {
require(value.isEmpty() || value.length > 1) { "Initials must be null or more than one characters long" }
}
}

@JvmInline
value class Name(val value: String) {
init {
require(value.split(" ").size > 1) { "Name must consist of two continuous strings or more" }
}
}
15 changes: 0 additions & 15 deletions src/main/kotlin/no/nav/syfo/personstatus/domain/SearchQuery.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class VeilederTilgangskontrollClient(
}

suspend fun veilederPersonAccessListMedOBO(
personIdentNumberList: List<String>,
personidenter: List<String>,
token: String,
callId: String,
): List<String>? {
Expand All @@ -85,7 +85,7 @@ class VeilederTilgangskontrollClient(
header(NAV_CALL_ID_HEADER, callId)
accept(ContentType.Application.Json)
contentType(ContentType.Application.Json)
setBody(personIdentNumberList)
setBody(personidenter)
}

requestTimer.stop(HISTOGRAM_ISTILGANGSKONTROLL_PERSONER)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,23 +303,62 @@ class PersonOversiktStatusRepository(private val database: DatabaseInterface) :
Result.failure(e)
}

override fun searchPerson(searchQuery: SearchQuery): List<PersonOversiktStatus> {
val initials = searchQuery.initials?.value?.toList() ?: emptyList()
val baseQuery =
"SELECT * FROM PERSON_OVERSIKT_STATUS p WHERE (p.oppfolgingstilfelle_end + INTERVAL '16 DAY' >= now() OR $AKTIV_OPPGAVE_WHERE_CLAUSE) AND p.fodselsdato = ? "
val nameQuery = if (initials.isNotEmpty()) {
"AND p.name ILIKE ? "
} else ""
val orderBy = "ORDER BY name ASC"
val initialsSearchString = initials.joinToString(separator = "% ", postfix = "%")
override fun searchPerson(search: Search): List<PersonOversiktStatus> {
val results = when (search) {
is Search.ByName -> searchByName(search)
is Search.ByNameAndDate -> searchByDateAndName(search)
is Search.ByDate -> searchByDate(search)
is Search.ByInitialsAndDate -> searchByInitialsAndDate(search)
}
return results.map { it.toPersonOversiktStatus() }
}

private fun searchByName(search: Search.ByName): List<PPersonOversiktStatus> {
val name = search.name.value.split(" ").joinToString(separator = "% ", postfix = "%")
val nameQuery = "AND p.name ILIKE ?"
val query = "$SEARCH_PERSON_BASE_QUERY $nameQuery $ORDER_BY_ASC"
return database.connection.use { connection ->
connection.prepareStatement(baseQuery + nameQuery + orderBy).use {
it.setDate(1, Date.valueOf(searchQuery.birthdate))
if (initials.isNotEmpty()) {
it.setString(2, initialsSearchString)
}
connection.prepareStatement(query).use {
it.setString(1, name)
it.executeQuery().toList { toPPersonOversiktStatus() }
}.map { it.toPersonOversiktStatus() }
}
}
}

private fun searchByDateAndName(search: Search.ByNameAndDate): List<PPersonOversiktStatus> {
val name = search.name.value.split(" ").joinToString(separator = "% ", postfix = "%")
val nameAndDateQuery = "AND p.name ILIKE ? AND p.fodselsdato = ?"
val query = "$SEARCH_PERSON_BASE_QUERY $nameAndDateQuery $ORDER_BY_ASC"
return database.connection.use { connection ->
connection.prepareStatement(query).use {
it.setString(1, name)
it.setDate(2, Date.valueOf(search.birthdate))
it.executeQuery().toList { toPPersonOversiktStatus() }
}
}
}

private fun searchByDate(search: Search.ByDate): List<PPersonOversiktStatus> {
val dateQuery = "AND p.fodselsdato = ?"
val query = "$SEARCH_PERSON_BASE_QUERY $dateQuery $ORDER_BY_ASC"
return database.connection.use { connection ->
connection.prepareStatement(query).use {
it.setDate(1, Date.valueOf(search.birthdate))
it.executeQuery().toList { toPPersonOversiktStatus() }
}
}
}

private fun searchByInitialsAndDate(search: Search.ByInitialsAndDate): List<PPersonOversiktStatus> {
val initials = search.initials.value.toList().joinToString(separator = "% ", postfix = "%")
val initialsAndDateQuery = "AND p.name ILIKE ? AND p.fodselsdato = ?"
val query = "$SEARCH_PERSON_BASE_QUERY $initialsAndDateQuery $ORDER_BY_ASC"
return database.connection.use { connection ->
connection.prepareStatement(query).use {
it.setString(1, initials)
it.setDate(2, Date.valueOf(search.birthdate))
it.executeQuery().toList { toPPersonOversiktStatus() }
}
}
}

Expand Down Expand Up @@ -524,6 +563,11 @@ class PersonOversiktStatusRepository(private val database: DatabaseInterface) :
WHERE fnr = ?
RETURNING id
"""

private const val SEARCH_PERSON_BASE_QUERY =
"SELECT * FROM PERSON_OVERSIKT_STATUS p WHERE (p.oppfolgingstilfelle_end + INTERVAL '16 DAY' >= now() OR $AKTIV_OPPGAVE_WHERE_CLAUSE)"

private const val ORDER_BY_ASC = "ORDER BY name ASC"
}
}

Expand Down
Loading

0 comments on commit 32a51d9

Please sign in to comment.