Skip to content

Commit

Permalink
Merge pull request #1279 from navikt/innsyn
Browse files Browse the repository at this point in the history
Innsyn
  • Loading branch information
flexable777 authored Jan 16, 2025
2 parents 55ad261 + d9330db commit 7d81845
Show file tree
Hide file tree
Showing 24 changed files with 1,156 additions and 5 deletions.
4 changes: 4 additions & 0 deletions deploy/dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ inboundApplications:
- application: klage-arkiver-journalpost
- application: kabin-api
- application: kabin
- application: mine-klager
- application: k9-klage
namespace: k9saksbehandling
cluster: dev-fss
Expand All @@ -46,6 +47,9 @@ inboundApplications:
- application: tilleggsstonader-klage
namespace: tilleggsstonader
cluster: dev-gcp
- application: tokenx-token-generator
namespace: aura
cluster: dev-gcp
springProfile: dev-gcp
tenant: trygdeetaten.no
db_tier: db-f1-micro
Expand Down
2 changes: 2 additions & 0 deletions deploy/nais.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ spec:
path: /internal/prometheus
secureLogs:
enabled: true
tokenx:
enabled: true
accessPolicy:
inbound:
rules:
Expand Down
1 change: 1 addition & 0 deletions deploy/prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ inboundApplications:
- application: klage-arkiver-journalpost
- application: kabin-api
- application: kabin
- application: mine-klager
- application: k9-klage
namespace: k9saksbehandling
cluster: prod-fss
Expand Down
3 changes: 3 additions & 0 deletions src/main/kotlin/no/nav/klage/innsyn/Exceptions.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package no.nav.klage.innsyn

class FileNotFoundInSafException(override val message: String = "Could not find document info in SAF"): RuntimeException()
102 changes: 102 additions & 0 deletions src/main/kotlin/no/nav/klage/innsyn/api/controller/InnsynController.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package no.nav.klage.innsyn.api.controller

import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.security.SecurityRequirement
import io.swagger.v3.oas.annotations.tags.Tag
import no.nav.klage.innsyn.api.view.InnsynResponse
import no.nav.klage.innsyn.service.InnsynService
import no.nav.klage.oppgave.config.SecurityConfiguration
import no.nav.klage.oppgave.util.TokenUtil
import no.nav.klage.oppgave.util.getLogger
import no.nav.klage.oppgave.util.getSecureLogger
import no.nav.klage.oppgave.util.logMethodDetails
import no.nav.security.token.support.core.api.ProtectedWithClaims
import org.springframework.core.io.FileSystemResource
import org.springframework.core.io.Resource
import org.springframework.http.HttpHeaders
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import java.io.FileInputStream
import java.io.InputStream
import java.nio.file.Files

@RestController
@Tag(
name = "kabal-innsyn",
description = "api for innsyn i brukeres saker"
)

@ProtectedWithClaims(issuer = SecurityConfiguration.TOKEN_X, claimMap = ["acr=Level4"])
@RequestMapping("api/innsyn")
@SecurityRequirement(name = "bearerAuth")
class InnsynController(
private val innsynService: InnsynService,
private val tokenUtil: TokenUtil,
) {

companion object {
@Suppress("JAVA_CLASS_ON_COMPANION")
private val logger = getLogger(javaClass.enclosingClass)
private val secureLogger = getSecureLogger()
}

@Operation(
summary = "Hent en brukers saker",
description = "Hent en brukers saker, basert på brukerens ident hentet fra token"
)
@GetMapping("/saker")
fun getSaker(): InnsynResponse {
val identFromToken = tokenUtil.getSubjectFromTokenXToken()
logMethodDetails(
methodName = ::getSaker.name,
innloggetIdent = identFromToken,
logger = secureLogger,
)

return innsynService.getSakerForBruker(fnr = identFromToken)
}

@Operation(
summary = "Hent et gitt dokument fra arkivet",
description = "Henter alle dokumenter på en journalpost. Må være ført på innlogget bruker."
)
@GetMapping("/documents/{journalpostId}")
fun getDocument(
@PathVariable("journalpostId") journalpostId: String,
): ResponseEntity<Resource> {
val identFromToken = tokenUtil.getSubjectFromTokenXToken()
logMethodDetails(
methodName = ::getDocument.name,
innloggetIdent = identFromToken,
logger = secureLogger,
)

//TODO: Samkjør dette med metoden som brukes for merging av dokument inne i Kabal
val (pathToMergedDocument, title) = innsynService.getJournalpostPdf(journalpostId = journalpostId)
val responseHeaders = HttpHeaders()
responseHeaders.contentType = MediaType.APPLICATION_PDF
responseHeaders.add(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"$title.pdf\"")

return ResponseEntity.ok()
.headers(responseHeaders)
.contentLength(pathToMergedDocument.toFile().length())
.body(
object : FileSystemResource(pathToMergedDocument) {
override fun getInputStream(): InputStream {
return object : FileInputStream(pathToMergedDocument.toFile()) {
override fun close() {
super.close()
//Override to do this after client has downloaded file
Files.delete(file.toPath())
}
}
}
})


}
}
44 changes: 44 additions & 0 deletions src/main/kotlin/no/nav/klage/innsyn/api/view/SakView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package no.nav.klage.innsyn.api.view

import java.time.LocalDate
import java.time.LocalDateTime


data class InnsynResponse(
val saker: List<SakView>,
// val active: List<SakView>,
// val finished: List<SakView>,
)

data class SakView(
val id: String, //created using fagsystemId and saksnummer
val saksnummer: String,
val ytelseId: String,
val innsendingsytelseId: String,
val events: List<Event>,
val varsletBehandlingstid: VarsletBehandlingstid?,
val mottattKlageinstans: LocalDate,
) {
data class Event(
val type: EventType,
val date: LocalDateTime,
val relevantJournalpostId: String?,
) {
enum class EventType {
KLAGE_MOTTATT_VEDTAKSINSTANS,
KLAGE_MOTTATT_KLAGEINSTANS,
KLAGE_AVSLUTTET_I_KLAGEINSTANS,
ANKE_MOTTATT_KLAGEINSTANS,
ANKE_SENDT_TRYGDERETTEN,
ANKE_KJENNELSE_MOTTATT_FRA_TRYGDERETTEN,
ANKE_AVSLUTTET_I_TRYGDERETTEN, //Do we need this, when we have ANKE_KJENNELSE_MOTTATT_FRA_TRYGDERETTEN?
ANKE_AVSLUTTET_I_KLAGEINSTANS,
}
}

data class VarsletBehandlingstid(
val varsletBehandlingstidUnits: Int,
val varsletBehandlingstidUnitTypeId: String,
val varsletFrist: LocalDate,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package no.nav.klage.innsyn.client.safselvbetjening


data class GetJournalpostByIdGraphqlQuery(
val query: String,
val variables: GetJournalpostByIdVariables
)

data class GetJournalpostByIdVariables(
val journalpostId: String
)

fun getJournalpostByIdQuery(journalpostId: String): GetJournalpostByIdGraphqlQuery {
val query = GraphqlQuery::class.java.getResource("/safselvbetjening/getJournalpostById.graphql").readText()
.replace("[\n\r]", "")
return GetJournalpostByIdGraphqlQuery(
query = query,
variables = GetJournalpostByIdVariables(journalpostId = journalpostId)
)
}

data class GraphqlQuery(
val query: String,
val variables: Variables
)

data class Variables(
val ident: String,
val navnHistorikk: Boolean,
val grupper: List<IdentGruppe> = listOf(IdentGruppe.AKTORID, IdentGruppe.FOLKEREGISTERIDENT, IdentGruppe.NPID)
)

enum class IdentGruppe {
FOLKEREGISTERIDENT, NPID, AKTORID
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package no.nav.klage.innsyn.client.safselvbetjening

data class GetJournalpostByIdResponse(val data: GetJournalpostById?, val errors: List<PdlError>?)

data class GetJournalpostById(val journalpostById: JournalpostById?)

data class JournalpostById(
val journalpostId: String,
val tittel: String,
val dokumenter: List<Dokument>,
)

data class Dokument(
val dokumentInfoId: String,
)

data class PdlError(
val message: String,
val locations: List<PdlErrorLocation>,
val path: List<String>?,
val extensions: PdlErrorExtension
)

data class PdlErrorLocation(
val line: Int?,
val column: Int?
)

data class PdlErrorExtension(
val code: String?,
val classification: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package no.nav.klage.innsyn.client.safselvbetjening

import no.nav.klage.oppgave.util.TokenUtil
import no.nav.klage.oppgave.util.getLogger
import no.nav.klage.oppgave.util.getSecureLogger
import org.slf4j.Logger
import org.springframework.http.HttpHeaders
import org.springframework.http.HttpStatusCode
import org.springframework.retry.annotation.Retryable
import org.springframework.stereotype.Component
import org.springframework.web.reactive.function.client.ClientResponse
import org.springframework.web.reactive.function.client.WebClient
import org.springframework.web.reactive.function.client.bodyToMono
import reactor.core.publisher.Mono

@Component
class SafSelvbetjeningGraphQlClient(
private val safSelvbetjeningWebClient: WebClient,
private val tokenUtil: TokenUtil
) {

companion object {
@Suppress("JAVA_CLASS_ON_COMPANION")
private val logger = getLogger(javaClass.enclosingClass)
private val secureLogger = getSecureLogger()
}

@Retryable
fun getJournalpostById(
journalpostId: String,
): GetJournalpostByIdResponse {
val response = runWithTimingAndLogging {
safSelvbetjeningWebClient.post()
.uri("graphql")
.header(
HttpHeaders.AUTHORIZATION,
"Bearer ${tokenUtil.getOnBehalfOfTokenWithSafSelvbetjeningScope()}"
)
.bodyValue(getJournalpostByIdQuery(journalpostId = journalpostId))
.retrieve()
.onStatus(HttpStatusCode::isError) { response ->
logErrorResponse(response, ::getJournalpostById.name, secureLogger)
}
.bodyToMono<GetJournalpostByIdResponse>()
.block() ?: throw RuntimeException("No connection to safselvbetjening")
}

return response
}

fun <T> runWithTimingAndLogging(block: () -> T): T {
val start = System.currentTimeMillis()
try {
return block.invoke()
} finally {
val end = System.currentTimeMillis()
logger.debug("Time it took to call saf: ${end - start} millis")
}
}

fun logErrorResponse(response: ClientResponse, functionName: String, logger: Logger): Mono<RuntimeException> {
return response.bodyToMono(String::class.java).map {
val errorString =
"Got ${response.statusCode()} when requesting $functionName - response body: '$it'"
logger.error(errorString)
RuntimeException(errorString)
}
}

}
Loading

0 comments on commit 7d81845

Please sign in to comment.