Skip to content

Commit 7d81845

Browse files
authored
Merge pull request #1279 from navikt/innsyn
Innsyn
2 parents 55ad261 + d9330db commit 7d81845

24 files changed

+1156
-5
lines changed

deploy/dev.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ inboundApplications:
2222
- application: klage-arkiver-journalpost
2323
- application: kabin-api
2424
- application: kabin
25+
- application: mine-klager
2526
- application: k9-klage
2627
namespace: k9saksbehandling
2728
cluster: dev-fss
@@ -46,6 +47,9 @@ inboundApplications:
4647
- application: tilleggsstonader-klage
4748
namespace: tilleggsstonader
4849
cluster: dev-gcp
50+
- application: tokenx-token-generator
51+
namespace: aura
52+
cluster: dev-gcp
4953
springProfile: dev-gcp
5054
tenant: trygdeetaten.no
5155
db_tier: db-f1-micro

deploy/nais.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ spec:
7979
path: /internal/prometheus
8080
secureLogs:
8181
enabled: true
82+
tokenx:
83+
enabled: true
8284
accessPolicy:
8385
inbound:
8486
rules:

deploy/prod.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ inboundApplications:
2222
- application: klage-arkiver-journalpost
2323
- application: kabin-api
2424
- application: kabin
25+
- application: mine-klager
2526
- application: k9-klage
2627
namespace: k9saksbehandling
2728
cluster: prod-fss
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package no.nav.klage.innsyn
2+
3+
class FileNotFoundInSafException(override val message: String = "Could not find document info in SAF"): RuntimeException()
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package no.nav.klage.innsyn.api.controller
2+
3+
import io.swagger.v3.oas.annotations.Operation
4+
import io.swagger.v3.oas.annotations.security.SecurityRequirement
5+
import io.swagger.v3.oas.annotations.tags.Tag
6+
import no.nav.klage.innsyn.api.view.InnsynResponse
7+
import no.nav.klage.innsyn.service.InnsynService
8+
import no.nav.klage.oppgave.config.SecurityConfiguration
9+
import no.nav.klage.oppgave.util.TokenUtil
10+
import no.nav.klage.oppgave.util.getLogger
11+
import no.nav.klage.oppgave.util.getSecureLogger
12+
import no.nav.klage.oppgave.util.logMethodDetails
13+
import no.nav.security.token.support.core.api.ProtectedWithClaims
14+
import org.springframework.core.io.FileSystemResource
15+
import org.springframework.core.io.Resource
16+
import org.springframework.http.HttpHeaders
17+
import org.springframework.http.MediaType
18+
import org.springframework.http.ResponseEntity
19+
import org.springframework.web.bind.annotation.GetMapping
20+
import org.springframework.web.bind.annotation.PathVariable
21+
import org.springframework.web.bind.annotation.RequestMapping
22+
import org.springframework.web.bind.annotation.RestController
23+
import java.io.FileInputStream
24+
import java.io.InputStream
25+
import java.nio.file.Files
26+
27+
@RestController
28+
@Tag(
29+
name = "kabal-innsyn",
30+
description = "api for innsyn i brukeres saker"
31+
)
32+
33+
@ProtectedWithClaims(issuer = SecurityConfiguration.TOKEN_X, claimMap = ["acr=Level4"])
34+
@RequestMapping("api/innsyn")
35+
@SecurityRequirement(name = "bearerAuth")
36+
class InnsynController(
37+
private val innsynService: InnsynService,
38+
private val tokenUtil: TokenUtil,
39+
) {
40+
41+
companion object {
42+
@Suppress("JAVA_CLASS_ON_COMPANION")
43+
private val logger = getLogger(javaClass.enclosingClass)
44+
private val secureLogger = getSecureLogger()
45+
}
46+
47+
@Operation(
48+
summary = "Hent en brukers saker",
49+
description = "Hent en brukers saker, basert på brukerens ident hentet fra token"
50+
)
51+
@GetMapping("/saker")
52+
fun getSaker(): InnsynResponse {
53+
val identFromToken = tokenUtil.getSubjectFromTokenXToken()
54+
logMethodDetails(
55+
methodName = ::getSaker.name,
56+
innloggetIdent = identFromToken,
57+
logger = secureLogger,
58+
)
59+
60+
return innsynService.getSakerForBruker(fnr = identFromToken)
61+
}
62+
63+
@Operation(
64+
summary = "Hent et gitt dokument fra arkivet",
65+
description = "Henter alle dokumenter på en journalpost. Må være ført på innlogget bruker."
66+
)
67+
@GetMapping("/documents/{journalpostId}")
68+
fun getDocument(
69+
@PathVariable("journalpostId") journalpostId: String,
70+
): ResponseEntity<Resource> {
71+
val identFromToken = tokenUtil.getSubjectFromTokenXToken()
72+
logMethodDetails(
73+
methodName = ::getDocument.name,
74+
innloggetIdent = identFromToken,
75+
logger = secureLogger,
76+
)
77+
78+
//TODO: Samkjør dette med metoden som brukes for merging av dokument inne i Kabal
79+
val (pathToMergedDocument, title) = innsynService.getJournalpostPdf(journalpostId = journalpostId)
80+
val responseHeaders = HttpHeaders()
81+
responseHeaders.contentType = MediaType.APPLICATION_PDF
82+
responseHeaders.add(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"$title.pdf\"")
83+
84+
return ResponseEntity.ok()
85+
.headers(responseHeaders)
86+
.contentLength(pathToMergedDocument.toFile().length())
87+
.body(
88+
object : FileSystemResource(pathToMergedDocument) {
89+
override fun getInputStream(): InputStream {
90+
return object : FileInputStream(pathToMergedDocument.toFile()) {
91+
override fun close() {
92+
super.close()
93+
//Override to do this after client has downloaded file
94+
Files.delete(file.toPath())
95+
}
96+
}
97+
}
98+
})
99+
100+
101+
}
102+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package no.nav.klage.innsyn.api.view
2+
3+
import java.time.LocalDate
4+
import java.time.LocalDateTime
5+
6+
7+
data class InnsynResponse(
8+
val saker: List<SakView>,
9+
// val active: List<SakView>,
10+
// val finished: List<SakView>,
11+
)
12+
13+
data class SakView(
14+
val id: String, //created using fagsystemId and saksnummer
15+
val saksnummer: String,
16+
val ytelseId: String,
17+
val innsendingsytelseId: String,
18+
val events: List<Event>,
19+
val varsletBehandlingstid: VarsletBehandlingstid?,
20+
val mottattKlageinstans: LocalDate,
21+
) {
22+
data class Event(
23+
val type: EventType,
24+
val date: LocalDateTime,
25+
val relevantJournalpostId: String?,
26+
) {
27+
enum class EventType {
28+
KLAGE_MOTTATT_VEDTAKSINSTANS,
29+
KLAGE_MOTTATT_KLAGEINSTANS,
30+
KLAGE_AVSLUTTET_I_KLAGEINSTANS,
31+
ANKE_MOTTATT_KLAGEINSTANS,
32+
ANKE_SENDT_TRYGDERETTEN,
33+
ANKE_KJENNELSE_MOTTATT_FRA_TRYGDERETTEN,
34+
ANKE_AVSLUTTET_I_TRYGDERETTEN, //Do we need this, when we have ANKE_KJENNELSE_MOTTATT_FRA_TRYGDERETTEN?
35+
ANKE_AVSLUTTET_I_KLAGEINSTANS,
36+
}
37+
}
38+
39+
data class VarsletBehandlingstid(
40+
val varsletBehandlingstidUnits: Int,
41+
val varsletBehandlingstidUnitTypeId: String,
42+
val varsletFrist: LocalDate,
43+
)
44+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package no.nav.klage.innsyn.client.safselvbetjening
2+
3+
4+
data class GetJournalpostByIdGraphqlQuery(
5+
val query: String,
6+
val variables: GetJournalpostByIdVariables
7+
)
8+
9+
data class GetJournalpostByIdVariables(
10+
val journalpostId: String
11+
)
12+
13+
fun getJournalpostByIdQuery(journalpostId: String): GetJournalpostByIdGraphqlQuery {
14+
val query = GraphqlQuery::class.java.getResource("/safselvbetjening/getJournalpostById.graphql").readText()
15+
.replace("[\n\r]", "")
16+
return GetJournalpostByIdGraphqlQuery(
17+
query = query,
18+
variables = GetJournalpostByIdVariables(journalpostId = journalpostId)
19+
)
20+
}
21+
22+
data class GraphqlQuery(
23+
val query: String,
24+
val variables: Variables
25+
)
26+
27+
data class Variables(
28+
val ident: String,
29+
val navnHistorikk: Boolean,
30+
val grupper: List<IdentGruppe> = listOf(IdentGruppe.AKTORID, IdentGruppe.FOLKEREGISTERIDENT, IdentGruppe.NPID)
31+
)
32+
33+
enum class IdentGruppe {
34+
FOLKEREGISTERIDENT, NPID, AKTORID
35+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package no.nav.klage.innsyn.client.safselvbetjening
2+
3+
data class GetJournalpostByIdResponse(val data: GetJournalpostById?, val errors: List<PdlError>?)
4+
5+
data class GetJournalpostById(val journalpostById: JournalpostById?)
6+
7+
data class JournalpostById(
8+
val journalpostId: String,
9+
val tittel: String,
10+
val dokumenter: List<Dokument>,
11+
)
12+
13+
data class Dokument(
14+
val dokumentInfoId: String,
15+
)
16+
17+
data class PdlError(
18+
val message: String,
19+
val locations: List<PdlErrorLocation>,
20+
val path: List<String>?,
21+
val extensions: PdlErrorExtension
22+
)
23+
24+
data class PdlErrorLocation(
25+
val line: Int?,
26+
val column: Int?
27+
)
28+
29+
data class PdlErrorExtension(
30+
val code: String?,
31+
val classification: String
32+
)
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package no.nav.klage.innsyn.client.safselvbetjening
2+
3+
import no.nav.klage.oppgave.util.TokenUtil
4+
import no.nav.klage.oppgave.util.getLogger
5+
import no.nav.klage.oppgave.util.getSecureLogger
6+
import org.slf4j.Logger
7+
import org.springframework.http.HttpHeaders
8+
import org.springframework.http.HttpStatusCode
9+
import org.springframework.retry.annotation.Retryable
10+
import org.springframework.stereotype.Component
11+
import org.springframework.web.reactive.function.client.ClientResponse
12+
import org.springframework.web.reactive.function.client.WebClient
13+
import org.springframework.web.reactive.function.client.bodyToMono
14+
import reactor.core.publisher.Mono
15+
16+
@Component
17+
class SafSelvbetjeningGraphQlClient(
18+
private val safSelvbetjeningWebClient: WebClient,
19+
private val tokenUtil: TokenUtil
20+
) {
21+
22+
companion object {
23+
@Suppress("JAVA_CLASS_ON_COMPANION")
24+
private val logger = getLogger(javaClass.enclosingClass)
25+
private val secureLogger = getSecureLogger()
26+
}
27+
28+
@Retryable
29+
fun getJournalpostById(
30+
journalpostId: String,
31+
): GetJournalpostByIdResponse {
32+
val response = runWithTimingAndLogging {
33+
safSelvbetjeningWebClient.post()
34+
.uri("graphql")
35+
.header(
36+
HttpHeaders.AUTHORIZATION,
37+
"Bearer ${tokenUtil.getOnBehalfOfTokenWithSafSelvbetjeningScope()}"
38+
)
39+
.bodyValue(getJournalpostByIdQuery(journalpostId = journalpostId))
40+
.retrieve()
41+
.onStatus(HttpStatusCode::isError) { response ->
42+
logErrorResponse(response, ::getJournalpostById.name, secureLogger)
43+
}
44+
.bodyToMono<GetJournalpostByIdResponse>()
45+
.block() ?: throw RuntimeException("No connection to safselvbetjening")
46+
}
47+
48+
return response
49+
}
50+
51+
fun <T> runWithTimingAndLogging(block: () -> T): T {
52+
val start = System.currentTimeMillis()
53+
try {
54+
return block.invoke()
55+
} finally {
56+
val end = System.currentTimeMillis()
57+
logger.debug("Time it took to call saf: ${end - start} millis")
58+
}
59+
}
60+
61+
fun logErrorResponse(response: ClientResponse, functionName: String, logger: Logger): Mono<RuntimeException> {
62+
return response.bodyToMono(String::class.java).map {
63+
val errorString =
64+
"Got ${response.statusCode()} when requesting $functionName - response body: '$it'"
65+
logger.error(errorString)
66+
RuntimeException(errorString)
67+
}
68+
}
69+
70+
}

0 commit comments

Comments
 (0)