From 2dc5fcec394ae86735ccb834fe5129d70212bd07 Mon Sep 17 00:00:00 2001 From: Andreas Jonsson Date: Tue, 16 Jan 2024 11:46:11 +0100 Subject: [PATCH 1/4] Added behandling events --- .../workflows/manual-deploy-kafka-aiven.yaml | 2 +- build.gradle.kts | 1 - deploy/nais.yaml | 6 +- .../dev-vars.yaml | 12 + .../prod-vars.yaml | 12 + .../internal-behandling-events.v1/topic.yaml | 24 ++ kafka-aiven/internal-events.v1/topic.yaml | 2 + .../internal-identity-events.v1/dev-vars.yaml | 12 + .../prod-vars.yaml | 12 + .../internal-identity-events.v1/topic.yaml | 24 ++ .../DokumenterUnderArbeidController.kt | 119 ++----- .../api/controller/SmartEditorController.kt | 46 +-- .../dokument/api/mapper/DokumentMapper.kt | 85 ++--- .../api/view/DokumentUnderArbeidView.kt | 36 +- .../DokumentUnderArbeidAsVedlegg.kt | 2 +- .../service/DokumentUnderArbeidService.kt | 315 +++++++++++++++--- .../service/FerdigstillDokumentService.kt | 60 +++- .../api/controller/BehandlingROLController.kt | 31 +- .../oppgave/api/controller/EventController.kt | 237 +++++++++++-- .../oppgave/api/mapper/BehandlingMapper.kt | 7 + .../clients/azure/DefaultAzureGateway.kt | 14 +- .../clients/azure/MicrosoftGraphResponse.kt | 2 +- .../clients/events/KafkaEventClient.kt | 27 -- .../klage/oppgave/clients/pdl/PdlFacade.kt | 4 + .../clients/pdl/graphql/HentPersonRequest.kt | 16 - .../oppgave/clients/pdl/graphql/PdlClient.kt | 16 + .../clients/pdl/graphql/PdlRequests.kt | 22 ++ ...entPersonerResponse.kt => PdlResponses.kt} | 16 +- .../klage/oppgave/clients/saf/SafFacade.kt | 16 + .../clients/saf/graphql/SafGraphQlClient.kt | 44 ++- .../clients/saf/graphql/SafResponse.kt | 6 + .../oppgave/config/AivenKafkaConfiguration.kt | 23 -- .../oppgave/config/MetricsConfiguration.kt | 13 +- .../ProblemHandlingControllerAdvice.kt | 77 +++-- .../nav/klage/oppgave/domain/kafka/Event.kt | 11 - .../domain/kafka/InternalBehandlingEvent.kt | 162 +++++++++ .../SaksbehandlerPersonligInfo.kt | 2 +- ...endBehandlingEndretToKafkaEventListener.kt | 23 +- .../eventlisteners/StatistikkTilDVHService.kt | 5 +- .../nav/klage/oppgave/service/AdminService.kt | 4 +- .../service/AivenKafkaClientCreator.kt | 82 +++++ .../oppgave/service/BehandlingService.kt | 288 +++++++++++++++- .../klage/oppgave/service/DokumentService.kt | 46 ++- .../service/KafkaInternalEventService.kt | 35 +- .../klage/oppgave/service/MeldingService.kt | 59 ++-- .../klage/oppgave/util/JacksonObjectMapper.kt | 13 + src/main/resources/application.yml | 10 +- src/main/resources/pdl/hentIdenter.graphql | 7 + .../DokumentUnderArbeidControllerTest.kt | 250 +++++++------- .../klage/oppgave/LayeredArchitectureTest.kt | 2 +- .../BehandlingEventsTilFoersteinstansTest.kt | 5 +- .../domain/kafka/StatistikkTilDVHTest.kt | 5 +- .../oppgave/service/BehandlingServiceTest.kt | 2 + .../service/DokumentUnderArbeidServiceTest.kt | 5 +- .../BehandlingAvslutningServiceTest.kt | 6 + 55 files changed, 1702 insertions(+), 661 deletions(-) create mode 100644 kafka-aiven/internal-behandling-events.v1/dev-vars.yaml create mode 100644 kafka-aiven/internal-behandling-events.v1/prod-vars.yaml create mode 100644 kafka-aiven/internal-behandling-events.v1/topic.yaml create mode 100644 kafka-aiven/internal-identity-events.v1/dev-vars.yaml create mode 100644 kafka-aiven/internal-identity-events.v1/prod-vars.yaml create mode 100644 kafka-aiven/internal-identity-events.v1/topic.yaml delete mode 100644 src/main/kotlin/no/nav/klage/oppgave/clients/events/KafkaEventClient.kt delete mode 100644 src/main/kotlin/no/nav/klage/oppgave/clients/pdl/graphql/HentPersonRequest.kt create mode 100644 src/main/kotlin/no/nav/klage/oppgave/clients/pdl/graphql/PdlRequests.kt rename src/main/kotlin/no/nav/klage/oppgave/clients/pdl/graphql/{HentPersonerResponse.kt => PdlResponses.kt} (78%) delete mode 100644 src/main/kotlin/no/nav/klage/oppgave/domain/kafka/Event.kt create mode 100644 src/main/kotlin/no/nav/klage/oppgave/domain/kafka/InternalBehandlingEvent.kt create mode 100644 src/main/kotlin/no/nav/klage/oppgave/service/AivenKafkaClientCreator.kt create mode 100644 src/main/kotlin/no/nav/klage/oppgave/util/JacksonObjectMapper.kt create mode 100644 src/main/resources/pdl/hentIdenter.graphql diff --git a/.github/workflows/manual-deploy-kafka-aiven.yaml b/.github/workflows/manual-deploy-kafka-aiven.yaml index 1ff4b1978..198fe90b0 100644 --- a/.github/workflows/manual-deploy-kafka-aiven.yaml +++ b/.github/workflows/manual-deploy-kafka-aiven.yaml @@ -15,7 +15,7 @@ jobs: strategy: matrix: cluster: [dev, prod] - topic: [behandling-endret.v2, behandling-events.v1, kabal-statistikk.v1, internal-events.v1] + topic: [behandling-endret.v2, behandling-events.v1, kabal-statistikk.v1, internal-behandling-events.v1, internal-identity-events.v1] steps: - name: Checkout code uses: actions/checkout@v3 diff --git a/build.gradle.kts b/build.gradle.kts index 3677d5af8..ca92ef3f7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -33,7 +33,6 @@ java.sourceCompatibility = JavaVersion.VERSION_17 repositories { mavenCentral() maven("https://github-package-registry-mirror.gc.nav.no/cached/maven-release") - maven("https://packages.confluent.io/maven/") } dependencies { diff --git a/deploy/nais.yaml b/deploy/nais.yaml index bad03df5d..04b0c6d8c 100644 --- a/deploy/nais.yaml +++ b/deploy/nais.yaml @@ -58,10 +58,10 @@ spec: timeout: 1 resources: limits: - memory: 5000Mi - requests: - cpu: 1000m memory: 3000Mi + requests: + cpu: 500m + memory: 2048Mi ingresses: {{#each ingresses as |ingress|}} - {{ingress}} diff --git a/kafka-aiven/internal-behandling-events.v1/dev-vars.yaml b/kafka-aiven/internal-behandling-events.v1/dev-vars.yaml new file mode 100644 index 000000000..4ec02eb08 --- /dev/null +++ b/kafka-aiven/internal-behandling-events.v1/dev-vars.yaml @@ -0,0 +1,12 @@ +pool: nav-dev +config: + cleanupPolicy: delete + minimumInSyncReplicas: 1 + partitions: 1 + replication: 3 + retentionBytes: -1 + retentionHours: 8 +acl: + - team: klage + application: klage-kafka-manager + access: read \ No newline at end of file diff --git a/kafka-aiven/internal-behandling-events.v1/prod-vars.yaml b/kafka-aiven/internal-behandling-events.v1/prod-vars.yaml new file mode 100644 index 000000000..6c80311ba --- /dev/null +++ b/kafka-aiven/internal-behandling-events.v1/prod-vars.yaml @@ -0,0 +1,12 @@ +pool: nav-prod +config: + cleanupPolicy: delete + minimumInSyncReplicas: 1 + partitions: 1 + replication: 3 + retentionBytes: -1 + retentionHours: 8 +acl: + - team: klage + application: klage-kafka-manager + access: read \ No newline at end of file diff --git a/kafka-aiven/internal-behandling-events.v1/topic.yaml b/kafka-aiven/internal-behandling-events.v1/topic.yaml new file mode 100644 index 000000000..c0b8452f1 --- /dev/null +++ b/kafka-aiven/internal-behandling-events.v1/topic.yaml @@ -0,0 +1,24 @@ +apiVersion: kafka.nais.io/v1 +kind: Topic +metadata: + name: internal-behandling-events.v1 + annotations: + kafka.nais.io/removeDataWhenResourceIsDeleted: "true" + namespace: klage + labels: + team: klage +spec: + pool: {{pool}} + config: + {{#each config as |value key|}} + {{key}}: {{value}} + {{/each}} + acl: + - team: klage + application: kabal-api + access: readwrite + {{#each acl}} + - team: {{team}} + application: {{application}} + access: {{access}} + {{/each}} diff --git a/kafka-aiven/internal-events.v1/topic.yaml b/kafka-aiven/internal-events.v1/topic.yaml index 7cc9a3899..6389fe994 100644 --- a/kafka-aiven/internal-events.v1/topic.yaml +++ b/kafka-aiven/internal-events.v1/topic.yaml @@ -2,6 +2,8 @@ apiVersion: kafka.nais.io/v1 kind: Topic metadata: name: internal-events.v1 + annotations: + kafka.nais.io/removeDataWhenResourceIsDeleted: "true" namespace: klage labels: team: klage diff --git a/kafka-aiven/internal-identity-events.v1/dev-vars.yaml b/kafka-aiven/internal-identity-events.v1/dev-vars.yaml new file mode 100644 index 000000000..4ec02eb08 --- /dev/null +++ b/kafka-aiven/internal-identity-events.v1/dev-vars.yaml @@ -0,0 +1,12 @@ +pool: nav-dev +config: + cleanupPolicy: delete + minimumInSyncReplicas: 1 + partitions: 1 + replication: 3 + retentionBytes: -1 + retentionHours: 8 +acl: + - team: klage + application: klage-kafka-manager + access: read \ No newline at end of file diff --git a/kafka-aiven/internal-identity-events.v1/prod-vars.yaml b/kafka-aiven/internal-identity-events.v1/prod-vars.yaml new file mode 100644 index 000000000..361b5f85f --- /dev/null +++ b/kafka-aiven/internal-identity-events.v1/prod-vars.yaml @@ -0,0 +1,12 @@ +pool: nav-prod +config: + cleanupPolicy: delete + minimumInSyncReplicas: 1 + partitions: 1 + replication: 3 + retentionBytes: -1 + retentionHours: 24 +acl: + - team: klage + application: klage-kafka-manager + access: read \ No newline at end of file diff --git a/kafka-aiven/internal-identity-events.v1/topic.yaml b/kafka-aiven/internal-identity-events.v1/topic.yaml new file mode 100644 index 000000000..fdce7151f --- /dev/null +++ b/kafka-aiven/internal-identity-events.v1/topic.yaml @@ -0,0 +1,24 @@ +apiVersion: kafka.nais.io/v1 +kind: Topic +metadata: + name: internal-identity-events.v1 + annotations: + kafka.nais.io/removeDataWhenResourceIsDeleted: "true" + namespace: klage + labels: + team: klage +spec: + pool: {{pool}} + config: + {{#each config as |value key|}} + {{key}}: {{value}} + {{/each}} + acl: + - team: klage + application: kabal-api + access: readwrite + {{#each acl}} + - team: {{team}} + application: {{application}} + access: {{access}} + {{/each}} diff --git a/src/main/kotlin/no/nav/klage/dokument/api/controller/DokumenterUnderArbeidController.kt b/src/main/kotlin/no/nav/klage/dokument/api/controller/DokumenterUnderArbeidController.kt index 8c2e77275..a0d4e0bbe 100644 --- a/src/main/kotlin/no/nav/klage/dokument/api/controller/DokumenterUnderArbeidController.kt +++ b/src/main/kotlin/no/nav/klage/dokument/api/controller/DokumenterUnderArbeidController.kt @@ -1,27 +1,19 @@ package no.nav.klage.dokument.api.controller -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import io.swagger.v3.oas.annotations.tags.Tag -import jakarta.servlet.http.HttpServletRequest import no.nav.klage.dokument.api.mapper.DokumentInputMapper import no.nav.klage.dokument.api.mapper.DokumentMapper import no.nav.klage.dokument.api.view.* import no.nav.klage.dokument.service.DokumentUnderArbeidService import no.nav.klage.kodeverk.DokumentType import no.nav.klage.oppgave.api.view.DokumentUnderArbeidMetadata -import no.nav.klage.oppgave.clients.events.KafkaEventClient import no.nav.klage.oppgave.config.SecurityConfiguration -import no.nav.klage.oppgave.domain.kafka.Event import no.nav.klage.oppgave.service.InnloggetSaksbehandlerService import no.nav.klage.oppgave.util.getLogger import no.nav.security.token.support.core.api.ProtectedWithClaims -import org.springframework.http.MediaType import org.springframework.http.ResponseEntity -import org.springframework.http.codec.ServerSentEvent import org.springframework.web.bind.annotation.* -import reactor.core.publisher.Flux -import java.time.Duration import java.util.* @RestController @@ -33,7 +25,6 @@ class DokumentUnderArbeidController( private val innloggetSaksbehandlerService: InnloggetSaksbehandlerService, private val dokumentMapper: DokumentMapper, private val dokumentInputMapper: DokumentInputMapper, - private val kafkaEventClient: KafkaEventClient, ) { companion object { @@ -59,17 +50,14 @@ class DokumentUnderArbeidController( tittel = input.file.originalFilename, dokumentType = DokumentType.of(input.dokumentTypeId), ) - return dokumentMapper.mapToDokumentView( - dokumentUnderArbeid = dokumentUnderArbeidService.createOpplastetDokumentUnderArbeid( - behandlingId = behandlingId, - dokumentType = DokumentType.of(input.dokumentTypeId), - opplastetFil = opplastetFil, - innloggetIdent = innloggetSaksbehandlerService.getInnloggetIdent(), - tittel = opplastetFil.title, - parentId = input.parentId, - datoMottatt = input.datoMottatt, - ), - journalpost = null, + return dokumentUnderArbeidService.createOpplastetDokumentUnderArbeid( + behandlingId = behandlingId, + dokumentType = DokumentType.of(input.dokumentTypeId), + opplastetFil = opplastetFil, + innloggetIdent = innloggetSaksbehandlerService.getInnloggetIdent(), + tittel = opplastetFil.title, + parentId = input.parentId, + datoMottatt = input.datoMottatt, ) } @@ -79,10 +67,10 @@ class DokumentUnderArbeidController( @RequestBody input: JournalfoerteDokumenterInput ): JournalfoerteDokumenterResponse { logger.debug("Kall mottatt på addJournalfoerteDokumenterAsVedlegg") - return dokumentUnderArbeidService.handleJournalfoerteDokumenterAsVedlegg( + return dokumentUnderArbeidService.addJournalfoerteDokumenterAsVedlegg( behandlingId = behandlingId, journalfoerteDokumenterInput = input, - innloggetIdent = innloggetSaksbehandlerService.getInnloggetIdent() + innloggetIdent = innloggetSaksbehandlerService.getInnloggetIdent(), ) } @@ -91,15 +79,14 @@ class DokumentUnderArbeidController( @PathVariable("behandlingId") behandlingId: UUID, @PathVariable("dokumentId") dokumentId: UUID, @RequestBody input: DokumentTypeInput - ): DokumentView { - return dokumentMapper.mapToDokumentView( - dokumentUnderArbeid = dokumentUnderArbeidService.updateDokumentType( + ): DocumentModified { + return DocumentModified( + modified = dokumentUnderArbeidService.updateDokumentType( behandlingId = behandlingId, dokumentId = dokumentId, dokumentType = DokumentType.of(input.dokumentTypeId), innloggetIdent = innloggetSaksbehandlerService.getInnloggetIdent() - ), - journalpost = null, + ).modified ) } @@ -108,15 +95,14 @@ class DokumentUnderArbeidController( @PathVariable("behandlingId") behandlingId: UUID, @PathVariable("dokumentId") dokumentId: UUID, @RequestBody input: DatoMottattInput - ): DokumentView { - return dokumentMapper.mapToDokumentView( - dokumentUnderArbeid = dokumentUnderArbeidService.updateDatoMottatt( + ): DocumentModified { + return DocumentModified( + modified = dokumentUnderArbeidService.updateDatoMottatt( behandlingId = behandlingId, dokumentId = dokumentId, datoMottatt = input.datoMottatt, innloggetIdent = innloggetSaksbehandlerService.getInnloggetIdent() - ), - journalpost = null, + ).modified ) } @@ -203,7 +189,7 @@ class DokumentUnderArbeidController( return dokumentUnderArbeidService.kobleEllerFrikobleVedlegg( behandlingId = behandlingId, persistentDokumentId = persistentDokumentId, - input = input + optionalParentInput = input ) } catch (e: Exception) { logger.error("Feilet under kobling av dokument $persistentDokumentId med ${input.dokumentId}", e) @@ -216,16 +202,15 @@ class DokumentUnderArbeidController( @PathVariable("behandlingId") behandlingId: UUID, @PathVariable("dokumentid") dokumentId: UUID, @RequestBody(required = true) input: FerdigstillDokumentInput, - ): DokumentView { + ): DocumentModified { val ident = innloggetSaksbehandlerService.getInnloggetIdent() - return dokumentMapper.mapToDokumentView( - dokumentUnderArbeid = dokumentUnderArbeidService.finnOgMarkerFerdigHovedDokument( + return DocumentModified( + modified = dokumentUnderArbeidService.finnOgMarkerFerdigHovedDokument( behandlingId = behandlingId, dokumentId = dokumentId, - ident = ident, + innloggetIdent = ident, brevmottakerIdents = input.brevmottakerIds, - ), - journalpost = null + ).modified ) } @@ -238,68 +223,20 @@ class DokumentUnderArbeidController( return dokumentUnderArbeidService.validateIfSmartDokument(dokumentId) } - //Old event stuff. Clients should read from EventController instead, and this can be deleted. - @GetMapping("/events", produces = [MediaType.TEXT_EVENT_STREAM_VALUE]) - fun documentEvents( - @PathVariable("behandlingId") behandlingId: String, - @RequestParam("lastEventIdInput", required = false) lastEventIdInput: UUID?, - request: HttpServletRequest, - ): Flux> { - logger.debug("Kall mottatt på documentEvents for behandlingId $behandlingId") - - //https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-async-disconnects - val heartbeatStream: Flux> = Flux.interval(Duration.ofSeconds(10)) - .takeWhile { true } - .map { tick -> toHeartBeatServerSentEvent(tick) } - - return kafkaEventClient.getEventPublisher() - .mapNotNull { event -> jsonToEvent(event.data()) } - .filter { Objects.nonNull(it) } - .filter { it.behandlingId == behandlingId && it.name == "finished" } - .mapNotNull { eventToServerSentEvent(it) } - .mergeWith(heartbeatStream) - } - - private fun toHeartBeatServerSentEvent(tick: Long): ServerSentEvent { - return eventToServerSentEvent( - Event( - behandlingId = "", - id = "", - name = "heartbeat-event-$tick", - data = "" - ) - ) - } - - private fun eventToServerSentEvent(event: Event): ServerSentEvent { - return ServerSentEvent.builder() - .id(event.id) - .event(event.name) - .data(event.data) - .build() - } - - private fun jsonToEvent(json: String?): Event { - val event = jacksonObjectMapper().readValue(json, Event::class.java) - logger.debug("Received event from Kafka: {}", event) - return event - } - @PutMapping("/{dokumentid}/tittel") fun changeDocumentTitle( @PathVariable("behandlingId") behandlingId: UUID, @PathVariable("dokumentid") dokumentId: UUID, @RequestBody input: DokumentTitleInput, - ): DokumentView { + ): DocumentModified { val ident = innloggetSaksbehandlerService.getInnloggetIdent() - return dokumentMapper.mapToDokumentView( - dokumentUnderArbeid = dokumentUnderArbeidService.updateDokumentTitle( + return DocumentModified( + modified = dokumentUnderArbeidService.updateDokumentTitle( behandlingId = behandlingId, dokumentId = dokumentId, dokumentTitle = input.title, innloggetIdent = ident, - ), - journalpost = null + ).modified ) } } \ No newline at end of file diff --git a/src/main/kotlin/no/nav/klage/dokument/api/controller/SmartEditorController.kt b/src/main/kotlin/no/nav/klage/dokument/api/controller/SmartEditorController.kt index b91771fab..990f404c1 100644 --- a/src/main/kotlin/no/nav/klage/dokument/api/controller/SmartEditorController.kt +++ b/src/main/kotlin/no/nav/klage/dokument/api/controller/SmartEditorController.kt @@ -3,8 +3,9 @@ package no.nav.klage.dokument.api.controller import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.tags.Tag import no.nav.klage.dokument.api.mapper.DokumentMapper +import no.nav.klage.dokument.api.view.DocumentModified +import no.nav.klage.dokument.api.view.DokumentView import no.nav.klage.dokument.api.view.PatchSmartHovedDokumentInput -import no.nav.klage.dokument.api.view.SmartEditorDocumentView import no.nav.klage.dokument.api.view.SmartHovedDokumentInput import no.nav.klage.dokument.clients.kabalsmarteditorapi.KabalSmartEditorApiClient import no.nav.klage.dokument.clients.kabalsmarteditorapi.model.request.CommentInput @@ -34,7 +35,7 @@ class SmartEditorController( private val innloggetSaksbehandlerService: InnloggetSaksbehandlerService, private val behandlingService: BehandlingService - ) { +) { companion object { @Suppress("JAVA_CLASS_ON_COMPANION") private val logger = getLogger(javaClass.enclosingClass) @@ -44,7 +45,7 @@ class SmartEditorController( @GetMapping fun findSmartDokumenter( @PathVariable("behandlingId") behandlingId: UUID, - ): List { + ): List { val ident = innloggetSaksbehandlerService.getInnloggetIdent() return dokumentUnderArbeidService.getSmartDokumenterUnderArbeid(behandlingId = behandlingId, ident = ident) .map { @@ -55,9 +56,10 @@ class SmartEditorController( ) val smartEditorDocument = kabalSmartEditorApiClient.getDocument(smartEditorId) - dokumentMapper.mapToSmartEditorDocumentView( + dokumentMapper.mapToDokumentView( dokumentUnderArbeid = it, - smartEditorDocument = smartEditorDocument + journalpost = null, + smartEditorDocument = smartEditorDocument, ) } } @@ -66,14 +68,14 @@ class SmartEditorController( fun createSmartHoveddokument( @PathVariable("behandlingId") behandlingId: UUID, @RequestBody body: SmartHovedDokumentInput, - ): SmartEditorDocumentView { + ): DokumentView { logger.debug("Kall mottatt på createSmartHoveddokument") if (body.version != null) { throw ValidationException("Du har en gammel versjon av Kabal. Last på nytt. Teknisk: version skal ikke lenger sendes.") } - val dokumentUnderArbeid = dokumentUnderArbeidService.opprettSmartdokument( + return dokumentUnderArbeidService.opprettSmartdokument( behandlingId = behandlingId, dokumentType = if (body.dokumentTypeId != null) DokumentType.of(body.dokumentTypeId) else DokumentType.VEDTAK, json = body.content.toString(), @@ -82,19 +84,6 @@ class SmartEditorController( tittel = body.tittel ?: DokumentType.VEDTAK.defaultFilnavn, parentId = body.parentId, ) - - val smartEditorId = - dokumentUnderArbeidService.getSmartEditorId( - dokumentId = dokumentUnderArbeid.id, - readOnly = false - ) - - val smartEditorDocument = kabalSmartEditorApiClient.getDocument(smartEditorId) - - return dokumentMapper.mapToSmartEditorDocumentView( - dokumentUnderArbeid = dokumentUnderArbeid, - smartEditorDocument = smartEditorDocument, - ) } @Operation( @@ -106,12 +95,7 @@ class SmartEditorController( @PathVariable("behandlingId") behandlingId: UUID, @PathVariable("dokumentId") dokumentId: UUID, @RequestBody input: PatchSmartHovedDokumentInput, - ): SmartEditorDocumentView { - - if (input.version != null) { - throw ValidationException("Du har en gammel versjon av Kabal. Last på nytt. Teknisk: version skal ikke lenger sendes.") - } - + ): DocumentModified { val smartEditorId = dokumentUnderArbeidService.getSmartEditorId( dokumentId = dokumentId, @@ -131,9 +115,8 @@ class SmartEditorController( val updatedDocument = kabalSmartEditorApiClient.updateDocument(smartEditorId, input.content.toString()) - return dokumentMapper.mapToSmartEditorDocumentView( - dokumentUnderArbeid = dokumentUnderArbeidService.getDokumentUnderArbeid(dokumentId), - smartEditorDocument = updatedDocument, + return DocumentModified( + modified = updatedDocument.modified, ) } @@ -142,7 +125,7 @@ class SmartEditorController( description = "Get document" ) @GetMapping("/{dokumentId}") - fun getDocument(@PathVariable("dokumentId") documentId: UUID): SmartEditorDocumentView { + fun getDocument(@PathVariable("dokumentId") documentId: UUID): DokumentView { val smartEditorId = dokumentUnderArbeidService.getSmartEditorId( dokumentId = documentId, @@ -150,8 +133,9 @@ class SmartEditorController( ) val document = kabalSmartEditorApiClient.getDocument(smartEditorId) - return dokumentMapper.mapToSmartEditorDocumentView( + return dokumentMapper.mapToDokumentView( dokumentUnderArbeid = dokumentUnderArbeidService.getDokumentUnderArbeid(documentId), + journalpost = null, smartEditorDocument = document, ) } diff --git a/src/main/kotlin/no/nav/klage/dokument/api/mapper/DokumentMapper.kt b/src/main/kotlin/no/nav/klage/dokument/api/mapper/DokumentMapper.kt index b629c170d..a79ccb5a4 100644 --- a/src/main/kotlin/no/nav/klage/dokument/api/mapper/DokumentMapper.kt +++ b/src/main/kotlin/no/nav/klage/dokument/api/mapper/DokumentMapper.kt @@ -3,7 +3,7 @@ package no.nav.klage.dokument.api.mapper import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import no.nav.klage.dokument.api.view.DokumentView import no.nav.klage.dokument.api.view.DokumentViewWithList -import no.nav.klage.dokument.api.view.SmartEditorDocumentView +import no.nav.klage.dokument.api.view.NewParent import no.nav.klage.dokument.clients.kabaljsontopdf.domain.InnholdsfortegnelseRequest import no.nav.klage.dokument.clients.kabaljsontopdf.domain.InnholdsfortegnelseRequest.Document.Type import no.nav.klage.dokument.clients.kabalsmarteditorapi.model.response.DocumentOutput @@ -117,7 +117,11 @@ class DokumentMapper { ) } - fun mapToDokumentView(dokumentUnderArbeid: DokumentUnderArbeid, journalpost: Journalpost?): DokumentView { + fun mapToDokumentView( + dokumentUnderArbeid: DokumentUnderArbeid, + journalpost: Journalpost?, + smartEditorDocument: DocumentOutput? + ): DokumentView { val unproxiedDUA = Hibernate.unproxy(dokumentUnderArbeid) as DokumentUnderArbeid var journalfoertDokumentReference: DokumentView.JournalfoertDokumentReference? = null @@ -148,11 +152,15 @@ class DokumentMapper { tittel = tittel, dokumentTypeId = unproxiedDUA.dokumentType?.id, created = unproxiedDUA.created, - modified = unproxiedDUA.modified, + modified = if (dokumentUnderArbeid is DokumentUnderArbeidAsSmartdokument) { + smartEditorDocument!!.modified + } else unproxiedDUA.modified, isSmartDokument = unproxiedDUA is DokumentUnderArbeidAsSmartdokument, templateId = if (unproxiedDUA is DokumentUnderArbeidAsSmartdokument) unproxiedDUA.smartEditorTemplateId else null, + content = if (dokumentUnderArbeid is DokumentUnderArbeidAsSmartdokument) { + jacksonObjectMapper().readTree(smartEditorDocument!!.json) + } else null, isMarkertAvsluttet = unproxiedDUA.markertFerdig != null, - parent = if (unproxiedDUA is DokumentUnderArbeidAsVedlegg) unproxiedDUA.parentId else null, parentId = if (unproxiedDUA is DokumentUnderArbeidAsVedlegg) unproxiedDUA.parentId else null, type = unproxiedDUA.getType(), journalfoertDokumentReference = journalfoertDokumentReference, @@ -166,72 +174,25 @@ class DokumentMapper { duplicateJournalfoerteDokumenter: List, journalpostList: List, ): DokumentViewWithList { - val firstDokument = Hibernate.unproxy(dokumentUnderArbeidList.firstOrNull()) - val firstDokumentView = if (firstDokument != null) { - if (firstDokument is JournalfoertDokumentUnderArbeidAsVedlegg) { - mapToDokumentView( - dokumentUnderArbeid = dokumentUnderArbeidList.first(), - journalpost = journalpostList.find { it.journalpostId == firstDokument.journalpostId }) - } else { - mapToDokumentView(dokumentUnderArbeid = dokumentUnderArbeidList.first(), journalpost = null) - } - } else null + val firstDokument = Hibernate.unproxy(dokumentUnderArbeidList.first()) as DokumentUnderArbeid return DokumentViewWithList( - id = firstDokumentView?.id, - tittel = firstDokumentView?.tittel, - dokumentTypeId = firstDokumentView?.dokumentTypeId, - created = firstDokumentView?.created, - modified = firstDokumentView?.modified, - type = firstDokumentView?.type, - isSmartDokument = firstDokumentView?.isSmartDokument, - templateId = firstDokumentView?.templateId, - isMarkertAvsluttet = firstDokumentView?.isMarkertAvsluttet, - parent = firstDokumentView?.parent, - parentId = firstDokumentView?.parentId, - journalfoertDokumentReference = firstDokumentView?.journalfoertDokumentReference, - alteredDocuments = dokumentUnderArbeidList.map { dokumentUnderArbeid -> - val duaUnproxied = Hibernate.unproxy(dokumentUnderArbeid) - if (duaUnproxied is JournalfoertDokumentUnderArbeidAsVedlegg) { - mapToDokumentView( - dokumentUnderArbeid = dokumentUnderArbeid, - journalpost = journalpostList.find { it.journalpostId == duaUnproxied.journalpostId }) - } else { - mapToDokumentView(dokumentUnderArbeid = dokumentUnderArbeid, journalpost = null) - } + modified = firstDokument.modified, + alteredDocuments = dokumentUnderArbeidList.drop(1).map { dokumentUnderArbeid -> + val duaUnproxied = Hibernate.unproxy(dokumentUnderArbeid) as DokumentUnderArbeidAsVedlegg + NewParent( + id = duaUnproxied.id, + modified = duaUnproxied.modified, + parentId = duaUnproxied.parentId!!, + ) }, duplicateJournalfoerteDokumenter = duplicateJournalfoerteDokumenter.map { duplicateJournalfoertDokument -> - val duaUnproxied = Hibernate.unproxy(duplicateJournalfoertDokument) - if (duaUnproxied is JournalfoertDokumentUnderArbeidAsVedlegg) { - mapToDokumentView( - dokumentUnderArbeid = duaUnproxied, - journalpost = journalpostList.find { it.journalpostId == duaUnproxied.journalpostId }) - } else { - mapToDokumentView(dokumentUnderArbeid = duplicateJournalfoertDokument, journalpost = null) - } + val duaUnproxied = Hibernate.unproxy(duplicateJournalfoertDokument) as DokumentUnderArbeid + duaUnproxied.id }, ) } - fun mapToSmartEditorDocumentView( - dokumentUnderArbeid: DokumentUnderArbeid, - smartEditorDocument: DocumentOutput, - ): SmartEditorDocumentView { - return SmartEditorDocumentView( - id = dokumentUnderArbeid.id, - tittel = dokumentUnderArbeid.name, - dokumentTypeId = dokumentUnderArbeid.dokumentType!!.id, - templateId = if (dokumentUnderArbeid is DokumentUnderArbeidAsSmartdokument) dokumentUnderArbeid.smartEditorTemplateId else null, - parent = if (dokumentUnderArbeid is DokumentUnderArbeidAsVedlegg) dokumentUnderArbeid.parentId else null, - parentId = if (dokumentUnderArbeid is DokumentUnderArbeidAsVedlegg) dokumentUnderArbeid.parentId else null, - content = jacksonObjectMapper().readTree(smartEditorDocument.json), - created = smartEditorDocument.created, - modified = smartEditorDocument.modified, - creatorIdent = dokumentUnderArbeid.creatorIdent, - creatorRole = dokumentUnderArbeid.creatorRole, - ) - } - //TODO: Har ikke tatt høyde for skjerming, ref https://confluence.adeo.no/pages/viewpage.action?pageId=320364687 fun mapJournalpostToDokumentReferanse( journalpost: Journalpost, diff --git a/src/main/kotlin/no/nav/klage/dokument/api/view/DokumentUnderArbeidView.kt b/src/main/kotlin/no/nav/klage/dokument/api/view/DokumentUnderArbeidView.kt index 9e700e197..db9346662 100644 --- a/src/main/kotlin/no/nav/klage/dokument/api/view/DokumentUnderArbeidView.kt +++ b/src/main/kotlin/no/nav/klage/dokument/api/view/DokumentUnderArbeidView.kt @@ -20,9 +20,8 @@ data class DokumentView( val type: DokumentUnderArbeid.DokumentUnderArbeidType, val isSmartDokument: Boolean, val templateId: String?, + val content: JsonNode?, val isMarkertAvsluttet: Boolean, - @Deprecated("use parentId") - val parent: UUID?, val parentId: UUID?, val journalfoertDokumentReference: JournalfoertDokumentReference?, val creatorIdent: String, @@ -38,36 +37,17 @@ data class DokumentView( } data class DokumentViewWithList( - val id: UUID?, - val tittel: String?, - val dokumentTypeId: String?, - val created: LocalDateTime?, val modified: LocalDateTime?, - val type: DokumentUnderArbeid.DokumentUnderArbeidType?, - val isSmartDokument: Boolean?, - val templateId: String?, - val isMarkertAvsluttet: Boolean?, - //Deprecated - val parent: UUID?, - val parentId: UUID?, - val journalfoertDokumentReference: DokumentView.JournalfoertDokumentReference?, + val alteredDocuments: List, + val duplicateJournalfoerteDokumenter: List, +) - //TODO: Keep these two lists when FE uses new version - val alteredDocuments: List, - val duplicateJournalfoerteDokumenter: List, +data class DocumentModified( + val modified: LocalDateTime, ) -data class SmartEditorDocumentView( +data class NewParent( val id: UUID, - val tittel: String, - val content: JsonNode, - val created: LocalDateTime, + val parentId: UUID, val modified: LocalDateTime, - val templateId: String?, - val dokumentTypeId: String, - //Deprecated - val parent: UUID?, - val parentId: UUID?, - val creatorIdent: String, - val creatorRole: BehandlingRole, ) \ No newline at end of file diff --git a/src/main/kotlin/no/nav/klage/dokument/domain/dokumenterunderarbeid/DokumentUnderArbeidAsVedlegg.kt b/src/main/kotlin/no/nav/klage/dokument/domain/dokumenterunderarbeid/DokumentUnderArbeidAsVedlegg.kt index 8607e0799..0cc7fc14c 100644 --- a/src/main/kotlin/no/nav/klage/dokument/domain/dokumenterunderarbeid/DokumentUnderArbeidAsVedlegg.kt +++ b/src/main/kotlin/no/nav/klage/dokument/domain/dokumenterunderarbeid/DokumentUnderArbeidAsVedlegg.kt @@ -10,7 +10,7 @@ import java.util.* @Entity abstract class DokumentUnderArbeidAsVedlegg( @Column(name = "parent_id") - open var parentId: UUID? = null, + open var parentId: UUID? = null, //why nullable? //Common properties id: UUID = UUID.randomUUID(), diff --git a/src/main/kotlin/no/nav/klage/dokument/service/DokumentUnderArbeidService.kt b/src/main/kotlin/no/nav/klage/dokument/service/DokumentUnderArbeidService.kt index 110f08886..f834b62a1 100644 --- a/src/main/kotlin/no/nav/klage/dokument/service/DokumentUnderArbeidService.kt +++ b/src/main/kotlin/no/nav/klage/dokument/service/DokumentUnderArbeidService.kt @@ -1,11 +1,13 @@ package no.nav.klage.dokument.service +import com.fasterxml.jackson.databind.ObjectMapper import jakarta.transaction.Transactional import no.nav.klage.dokument.api.mapper.DokumentMapper import no.nav.klage.dokument.api.view.* import no.nav.klage.dokument.api.view.JournalfoertDokumentReference import no.nav.klage.dokument.clients.kabaljsontopdf.KabalJsonToPdfClient import no.nav.klage.dokument.clients.kabalsmarteditorapi.DefaultKabalSmartEditorApiGateway +import no.nav.klage.dokument.clients.kabalsmarteditorapi.KabalSmartEditorApiClient import no.nav.klage.dokument.domain.FysiskDokument import no.nav.klage.dokument.domain.PDFDocument import no.nav.klage.dokument.domain.dokumenterunderarbeid.* @@ -22,18 +24,18 @@ import no.nav.klage.oppgave.clients.saf.SafFacade import no.nav.klage.oppgave.clients.saf.graphql.Journalpost import no.nav.klage.oppgave.domain.events.BehandlingEndretEvent import no.nav.klage.oppgave.domain.events.DokumentFerdigstiltAvSaksbehandler +import no.nav.klage.oppgave.domain.kafka.* import no.nav.klage.oppgave.domain.klage.* import no.nav.klage.oppgave.domain.klage.BehandlingSetters.addSaksdokument import no.nav.klage.oppgave.exceptions.InvalidProperty import no.nav.klage.oppgave.exceptions.MissingTilgangException import no.nav.klage.oppgave.exceptions.SectionedValidationErrorWithDetailsException import no.nav.klage.oppgave.exceptions.ValidationSection -import no.nav.klage.oppgave.service.BehandlingService -import no.nav.klage.oppgave.service.DokumentService -import no.nav.klage.oppgave.service.InnloggetSaksbehandlerService +import no.nav.klage.oppgave.service.* import no.nav.klage.oppgave.util.getLogger import no.nav.klage.oppgave.util.getSecureLogger import no.nav.klage.oppgave.util.getSortKey +import no.nav.klage.oppgave.util.ourJacksonObjectMapper import org.hibernate.Hibernate import org.springframework.beans.factory.annotation.Value import org.springframework.context.ApplicationEventPublisher @@ -67,12 +69,16 @@ class DokumentUnderArbeidService( private val innholdsfortegnelseService: InnholdsfortegnelseService, private val safFacade: SafFacade, private val dokumentMapper: DokumentMapper, + private val kafkaInternalEventService: KafkaInternalEventService, + private val saksbehandlerService: SaksbehandlerService, + private val kabalSmartEditorApiClient: KabalSmartEditorApiClient, @Value("\${SYSTEMBRUKER_IDENT}") private val systembrukerIdent: String, ) { companion object { @Suppress("JAVA_CLASS_ON_COMPANION") private val logger = getLogger(javaClass.enclosingClass) - private val securLogger = getSecureLogger() + private val secureLogger = getSecureLogger() + private val objectMapper: ObjectMapper = ourJacksonObjectMapper() } fun createOpplastetDokumentUnderArbeid( @@ -83,7 +89,7 @@ class DokumentUnderArbeidService( tittel: String, parentId: UUID?, datoMottatt: LocalDate?, - ): DokumentUnderArbeid { + ): DokumentView { //Sjekker lesetilgang på behandlingsnivå: val behandling = behandlingService.getBehandlingAndCheckLeseTilgangForPerson(behandlingId) @@ -146,17 +152,40 @@ class DokumentUnderArbeidService( tidspunkt = document.created, ) - return document + val dokumentView = dokumentMapper.mapToDokumentView( + dokumentUnderArbeid = document, + journalpost = null, + smartEditorDocument = null, + ) + + publishInternalEvent( + data = objectMapper.writeValueAsString( + DocumentsAddedEvent( + actor = BaseEvent.Actor( + navIdent = innloggetIdent, + name = saksbehandlerService.getNameForIdent(innloggetIdent), + ), + timestamp = LocalDateTime.now(), + documents = listOf( + dokumentView + ) + ) + ), + behandlingId = behandling.id, + type = InternalEventType.DOCUMENTS_ADDED, + ) + + return dokumentView } fun kobleEllerFrikobleVedlegg( behandlingId: UUID, persistentDokumentId: UUID, - input: OptionalPersistentDokumentIdInput, + optionalParentInput: OptionalPersistentDokumentIdInput, ): DokumentViewWithList { val behandling = behandlingService.getBehandlingAndCheckLeseTilgangForPerson(behandlingId = behandlingId) - val (dokumentUnderArbeidList, duplicateJournalfoerteDokumenter) = if (input.dokumentId == null) { + val (dokumentUnderArbeidList, duplicateJournalfoerteDokumenter) = if (optionalParentInput.dokumentId == null) { listOf( setAsHoveddokument( behandlingId = behandlingId, @@ -166,7 +195,7 @@ class DokumentUnderArbeidService( ) to emptyList() } else { setAsVedlegg( - parentId = input.dokumentId, + parentId = optionalParentInput.dokumentId, dokumentId = persistentDokumentId, innloggetIdent = innloggetSaksbehandlerService.getInnloggetIdent() ) @@ -182,6 +211,31 @@ class DokumentUnderArbeidService( saksbehandlerContext = true, ) + val innloggetIdent = innloggetSaksbehandlerService.getInnloggetIdent() + + publishInternalEvent( + data = objectMapper.writeValueAsString( + DocumentsChangedEvent( + actor = BaseEvent.Actor( + navIdent = innloggetIdent, + name = saksbehandlerService.getNameForIdent(innloggetIdent), + ), + timestamp = LocalDateTime.now(), + documents = dokumentUnderArbeidList.map { + DocumentsChangedEvent.DocumentChanged( + id = it.id.toString(), + parentId = if (it is DokumentUnderArbeidAsVedlegg) it.parentId.toString() else null, + dokumentTypeId = it.dokumentType?.id, + tittel = it.name, + isMarkertAvsluttet = it.erMarkertFerdig(), + ) + } + ) + ), + behandlingId = behandling.id, + type = InternalEventType.DOCUMENTS_CHANGED, + ) + return dokumentMapper.mapToDokumentListView( dokumentUnderArbeidList = dokumentUnderArbeidList, duplicateJournalfoerteDokumenter = duplicateJournalfoerteDokumenter, @@ -197,7 +251,7 @@ class DokumentUnderArbeidService( innloggetIdent: String, tittel: String, parentId: UUID?, - ): DokumentUnderArbeid { + ): DokumentView { //Sjekker lesetilgang på behandlingsnivå: val behandling = behandlingService.getBehandlingAndCheckLeseTilgangForPerson(behandlingId) @@ -267,10 +321,35 @@ class DokumentUnderArbeidService( tidspunkt = document.created, ) - return document + val smartEditorDocument = kabalSmartEditorApiClient.getDocument(documentId = document.smartEditorId) + + val dokumentView = dokumentMapper.mapToDokumentView( + dokumentUnderArbeid = document, + journalpost = null, + smartEditorDocument = smartEditorDocument, + ) + + publishInternalEvent( + data = objectMapper.writeValueAsString( + DocumentsAddedEvent( + actor = BaseEvent.Actor( + navIdent = innloggetIdent, + name = saksbehandlerService.getNameForIdent(innloggetIdent), + ), + timestamp = LocalDateTime.now(), + documents = listOf( + dokumentView + ) + ) + ), + behandlingId = behandling.id, + type = InternalEventType.DOCUMENTS_ADDED, + ) + + return dokumentView } - fun handleJournalfoerteDokumenterAsVedlegg( + fun addJournalfoerteDokumenterAsVedlegg( behandlingId: UUID, journalfoerteDokumenterInput: JournalfoerteDokumenterInput, innloggetIdent: String @@ -297,13 +376,32 @@ class DokumentUnderArbeidService( journalpostListForUser = journalpostListForUser ) + val addedJournalfoerteDokumenter = getDokumentViewListForJournalfoertDokumentUnderArbeidAsVedleggList( + dokumentUnderArbeidList = added, + behandling = behandling, + journalpostList = journalpostListForUser, + ) + + if (addedJournalfoerteDokumenter.isNotEmpty()) { + publishInternalEvent( + data = objectMapper.writeValueAsString( + DocumentsAddedEvent( + actor = BaseEvent.Actor( + navIdent = innloggetIdent, + name = saksbehandlerService.getNameForIdent(innloggetIdent), + ), + timestamp = LocalDateTime.now(), + documents = addedJournalfoerteDokumenter, + ) + ), + behandlingId = behandling.id, + type = InternalEventType.DOCUMENTS_ADDED, + ) + } + return JournalfoerteDokumenterResponse( - addedJournalfoerteDokumenter = getDokumentViewListForJournalfoertDokumentUnderArbeidAsVedleggList( - dokumentUnderArbeidList = added, - behandling = behandling, - journalpostList = journalpostListForUser, - ), - duplicateJournalfoerteDokumenter = duplicates + addedJournalfoerteDokumenter = addedJournalfoerteDokumenter, + duplicateJournalfoerteDokumenter = duplicates, ) } @@ -316,7 +414,8 @@ class DokumentUnderArbeidService( .map { journalfoertVedlegg -> dokumentMapper.mapToDokumentView( dokumentUnderArbeid = journalfoertVedlegg, - journalpost = journalpostList.find { it.journalpostId == journalfoertVedlegg.journalpostId }!! + journalpost = journalpostList.find { it.journalpostId == journalfoertVedlegg.journalpostId }!!, + smartEditorDocument = null, ) } } @@ -488,6 +587,29 @@ class DokumentUnderArbeidService( tidspunkt = dokumentUnderArbeid.modified, ) + publishInternalEvent( + data = objectMapper.writeValueAsString( + DocumentsChangedEvent( + actor = BaseEvent.Actor( + navIdent = innloggetIdent, + name = saksbehandlerService.getNameForIdent(innloggetIdent), + ), + timestamp = LocalDateTime.now(), + documents = listOf( + DocumentsChangedEvent.DocumentChanged( + id = dokumentUnderArbeid.id.toString(), + parentId = if (dokumentUnderArbeid is DokumentUnderArbeidAsVedlegg) dokumentUnderArbeid.parentId.toString() else null, + dokumentTypeId = dokumentUnderArbeid.dokumentType?.id, + tittel = dokumentUnderArbeid.name, + isMarkertAvsluttet = dokumentUnderArbeid.erMarkertFerdig(), + ) + ), + ) + ), + behandlingId = behandling.id, + type = InternalEventType.DOCUMENTS_CHANGED, + ) + return dokumentUnderArbeid } @@ -529,6 +651,29 @@ class DokumentUnderArbeidService( tidspunkt = dokumentUnderArbeid.modified, ) + publishInternalEvent( + data = objectMapper.writeValueAsString( + DocumentsChangedEvent( + actor = BaseEvent.Actor( + navIdent = innloggetIdent, + name = saksbehandlerService.getNameForIdent(innloggetIdent), + ), + timestamp = LocalDateTime.now(), + documents = listOf( + DocumentsChangedEvent.DocumentChanged( + id = dokumentUnderArbeid.id.toString(), + parentId = if (dokumentUnderArbeid is DokumentUnderArbeidAsVedlegg) dokumentUnderArbeid.parentId.toString() else null, + dokumentTypeId = dokumentUnderArbeid.dokumentType?.id, + tittel = dokumentUnderArbeid.name, + isMarkertAvsluttet = dokumentUnderArbeid.erMarkertFerdig(), + ) + ), + ) + ), + behandlingId = behandling.id, + type = InternalEventType.DOCUMENTS_CHANGED, + ) + return dokumentUnderArbeid } @@ -545,36 +690,60 @@ class DokumentUnderArbeidService( dokumentTitle: String, innloggetIdent: String ): DokumentUnderArbeid { - val dokument = dokumentUnderArbeidRepository.findById(dokumentId).get() + val dokumentUnderArbeid = dokumentUnderArbeidRepository.findById(dokumentId).get() - val behandling = behandlingService.getBehandlingAndCheckLeseTilgangForPerson(dokument.behandlingId) + val behandling = behandlingService.getBehandlingAndCheckLeseTilgangForPerson(dokumentUnderArbeid.behandlingId) val behandlingRole = behandling.getRoleInBehandling(innloggetIdent) if (behandling.avsluttetAvSaksbehandler == null) { - if (dokument.creatorRole != behandlingRole) { - throw MissingTilgangException("$behandlingRole har ikke anledning til å endre tittel på dette dokumentet eiet av ${dokument.creatorRole}.") + if (dokumentUnderArbeid.creatorRole != behandlingRole) { + throw MissingTilgangException("$behandlingRole har ikke anledning til å endre tittel på dette dokumentet eiet av ${dokumentUnderArbeid.creatorRole}.") } } - if (dokument.erMarkertFerdig()) { + if (dokumentUnderArbeid.erMarkertFerdig()) { throw DokumentValidationException("Kan ikke endre tittel på et dokument som er ferdigstilt") } - if (dokument is JournalfoertDokumentUnderArbeidAsVedlegg) { + if (dokumentUnderArbeid is JournalfoertDokumentUnderArbeidAsVedlegg) { throw DokumentValidationException("Kan ikke endre tittel på journalført dokument i denne konteksten") } - val oldValue = dokument.name - dokument.name = dokumentTitle + val oldValue = dokumentUnderArbeid.name + dokumentUnderArbeid.name = dokumentTitle behandling.publishEndringsloggEvent( saksbehandlerident = innloggetIdent, felt = Felt.DOKUMENT_UNDER_ARBEID_NAME, fraVerdi = oldValue, - tilVerdi = dokument.name, + tilVerdi = dokumentUnderArbeid.name, tidspunkt = LocalDateTime.now(), ) - return dokument + + publishInternalEvent( + data = objectMapper.writeValueAsString( + DocumentsChangedEvent( + actor = BaseEvent.Actor( + navIdent = innloggetIdent, + name = saksbehandlerService.getNameForIdent(innloggetIdent), + ), + timestamp = LocalDateTime.now(), + documents = listOf( + DocumentsChangedEvent.DocumentChanged( + id = dokumentUnderArbeid.id.toString(), + parentId = if (dokumentUnderArbeid is DokumentUnderArbeidAsVedlegg) dokumentUnderArbeid.parentId.toString() else null, + dokumentTypeId = dokumentUnderArbeid.dokumentType?.id, + tittel = dokumentUnderArbeid.name, + isMarkertAvsluttet = dokumentUnderArbeid.erMarkertFerdig(), + ) + ), + ) + ), + behandlingId = behandling.id, + type = InternalEventType.DOCUMENTS_CHANGED, + ) + + return dokumentUnderArbeid } fun validateDocument( @@ -692,7 +861,7 @@ class DokumentUnderArbeidService( fun finnOgMarkerFerdigHovedDokument( behandlingId: UUID, dokumentId: UUID, - ident: String, + innloggetIdent: String, brevmottakerIdents: Set?, ): DokumentUnderArbeid { val hovedDokument = dokumentUnderArbeidRepository.findById(dokumentId).get() @@ -724,7 +893,7 @@ class DokumentUnderArbeidService( } val now = LocalDateTime.now() - hovedDokument.markerFerdigHvisIkkeAlleredeMarkertFerdig(tidspunkt = now, saksbehandlerIdent = ident) + hovedDokument.markerFerdigHvisIkkeAlleredeMarkertFerdig(tidspunkt = now, saksbehandlerIdent = innloggetIdent) val mapBrevmottakerIdentToBrevmottakerInput = kabalDocumentMapper.mapBrevmottakerIdentToBrevmottakerInput( behandling = behandling, brevmottakerIdents = processedBrevmottakerIdents, @@ -734,7 +903,12 @@ class DokumentUnderArbeidService( it.partId.value }.toSet() - vedlegg.forEach { it.markerFerdigHvisIkkeAlleredeMarkertFerdig(tidspunkt = now, saksbehandlerIdent = ident) } + vedlegg.forEach { + it.markerFerdigHvisIkkeAlleredeMarkertFerdig( + tidspunkt = now, + saksbehandlerIdent = innloggetIdent + ) + } if (vedlegg.isNotEmpty() && hovedDokument.dokumentType != DokumentType.KJENNELSE_FRA_TRYGDERETTEN) { innholdsfortegnelseService.saveInnholdsfortegnelse( @@ -743,9 +917,8 @@ class DokumentUnderArbeidService( ) } - behandling.publishEndringsloggEvent( - saksbehandlerident = ident, + saksbehandlerident = innloggetIdent, felt = Felt.DOKUMENT_UNDER_ARBEID_MARKERT_FERDIG, fraVerdi = null, tilVerdi = hovedDokument.markertFerdig.toString(), @@ -753,7 +926,7 @@ class DokumentUnderArbeidService( ) behandling.publishEndringsloggEvent( - saksbehandlerident = ident, + saksbehandlerident = innloggetIdent, felt = Felt.DOKUMENT_UNDER_ARBEID_BREVMOTTAKER_IDENTS, fraVerdi = null, tilVerdi = hovedDokument.brevmottakerIdents.joinToString { it }, @@ -762,6 +935,35 @@ class DokumentUnderArbeidService( applicationEventPublisher.publishEvent(DokumentFerdigstiltAvSaksbehandler(hovedDokument)) + publishInternalEvent( + data = objectMapper.writeValueAsString( + DocumentsChangedEvent( + actor = BaseEvent.Actor( + navIdent = innloggetIdent, + name = saksbehandlerService.getNameForIdent(innloggetIdent), + ), + timestamp = LocalDateTime.now(), + documents = vedlegg.map { + DocumentsChangedEvent.DocumentChanged( + id = it.id.toString(), + parentId = if (it is DokumentUnderArbeidAsVedlegg) it.parentId.toString() else null, + dokumentTypeId = it.dokumentType?.id, + tittel = it.name, + isMarkertAvsluttet = it.erMarkertFerdig(), + ) + } + DocumentsChangedEvent.DocumentChanged( + id = hovedDokument.id.toString(), + parentId = null, + dokumentTypeId = hovedDokument.dokumentType?.id, + tittel = hovedDokument.name, + isMarkertAvsluttet = hovedDokument.erMarkertFerdig(), + ) + ) + ), + behandlingId = behandling.id, + type = InternalEventType.DOCUMENTS_CHANGED, + ) + return hovedDokument } @@ -935,6 +1137,21 @@ class DokumentUnderArbeidService( behandlingRole = behandling.getRoleInBehandling(innloggetIdent), behandling = behandling, ) + + publishInternalEvent( + data = objectMapper.writeValueAsString( + DocumentsRemovedEvent( + actor = BaseEvent.Actor( + navIdent = innloggetIdent, + name = saksbehandlerService.getNameForIdent(innloggetIdent), + ), + timestamp = LocalDateTime.now(), + idList = vedlegg.map { it.id.toString() } + document.id.toString(), + ) + ), + behandlingId = behandling.id, + type = InternalEventType.DOCUMENTS_REMOVED, + ) } private fun deleteDocuments( @@ -985,7 +1202,7 @@ class DokumentUnderArbeidService( parentId: UUID, dokumentId: UUID, innloggetIdent: String - ): Pair, List> { + ): Pair, List> { if (parentId == dokumentId) { throw DokumentValidationException("Kan ikke gjøre et dokument til vedlegg for seg selv.") } @@ -1022,7 +1239,8 @@ class DokumentUnderArbeidService( } val alteredDocuments = processedDokumentUnderArbeidOutput.mapNotNull { it.first } - val duplicateJournalfoerteDokumenterUnderArbeid = processedDokumentUnderArbeidOutput.mapNotNull { it.second } + val duplicateJournalfoerteDokumenterUnderArbeid: List = + processedDokumentUnderArbeidOutput.mapNotNull { it.second } duplicateJournalfoerteDokumenterUnderArbeid.forEach { dokumentUnderArbeidRepository.deleteById(it.id) @@ -1034,7 +1252,7 @@ class DokumentUnderArbeidService( private fun setParentInDokumentUnderArbeidAndFindDuplicates( currentDokumentId: UUID, parentId: UUID, - ): Pair { + ): Pair { var dokumentUnderArbeid: DokumentUnderArbeid = dokumentUnderArbeidRepository.findById(currentDokumentId).get() @@ -1165,7 +1383,16 @@ class DokumentUnderArbeidService( } else emptyList() return dokumenterUnderArbeid.sortedByDescending { it.created } - .map { dokumentMapper.mapToDokumentView(dokumentUnderArbeid = it, journalpost = null) } + .map { + val smartEditorDocument = if (it is DokumentUnderArbeidAsSmartdokument) { + kabalSmartEditorApiClient.getDocument(documentId = it.smartEditorId) + } else null + dokumentMapper.mapToDokumentView( + dokumentUnderArbeid = it, + journalpost = null, + smartEditorDocument = smartEditorDocument + ) + } .plus( getDokumentViewListForJournalfoertDokumentUnderArbeidAsVedleggList( dokumentUnderArbeidList = journalfoerteDokumenterUnderArbeid, @@ -1405,6 +1632,16 @@ class DokumentUnderArbeidService( ) } ?: emptyList() } + + private fun publishInternalEvent(data: String, behandlingId: UUID, type: InternalEventType) { + kafkaInternalEventService.publishInternalBehandlingEvent( + InternalBehandlingEvent( + behandlingId = behandlingId.toString(), + type = type, + data = data, + ) + ) + } } diff --git a/src/main/kotlin/no/nav/klage/dokument/service/FerdigstillDokumentService.kt b/src/main/kotlin/no/nav/klage/dokument/service/FerdigstillDokumentService.kt index ac6978afa..848435b39 100644 --- a/src/main/kotlin/no/nav/klage/dokument/service/FerdigstillDokumentService.kt +++ b/src/main/kotlin/no/nav/klage/dokument/service/FerdigstillDokumentService.kt @@ -1,29 +1,44 @@ package no.nav.klage.dokument.service +import com.fasterxml.jackson.databind.ObjectMapper import net.javacrumbs.shedlock.spring.annotation.SchedulerLock +import no.nav.klage.dokument.api.mapper.DokumentMapper import no.nav.klage.dokument.domain.dokumenterunderarbeid.DokumentUnderArbeidAsHoveddokument +import no.nav.klage.oppgave.clients.saf.SafFacade import no.nav.klage.oppgave.domain.events.DokumentFerdigstiltAvSaksbehandler -import no.nav.klage.oppgave.domain.kafka.Event +import no.nav.klage.oppgave.domain.kafka.BaseEvent +import no.nav.klage.oppgave.domain.kafka.DocumentFinishedEvent +import no.nav.klage.oppgave.domain.kafka.InternalBehandlingEvent +import no.nav.klage.oppgave.domain.kafka.InternalEventType +import no.nav.klage.oppgave.service.BehandlingService import no.nav.klage.oppgave.service.KafkaInternalEventService +import no.nav.klage.oppgave.service.SaksbehandlerService import no.nav.klage.oppgave.util.getLogger import no.nav.klage.oppgave.util.getSecureLogger +import no.nav.klage.oppgave.util.ourJacksonObjectMapper import org.hibernate.Hibernate import org.springframework.scheduling.annotation.Async import org.springframework.scheduling.annotation.Scheduled import org.springframework.stereotype.Service import org.springframework.transaction.event.TransactionPhase import org.springframework.transaction.event.TransactionalEventListener +import java.util.* @Service class FerdigstillDokumentService( private val dokumentUnderArbeidService: DokumentUnderArbeidService, private val dokumentUnderArbeidCommonService: DokumentUnderArbeidCommonService, private val kafkaInternalEventService: KafkaInternalEventService, + private val saksbehandlerService: SaksbehandlerService, + private val safFacade: SafFacade, + private val dokumentMapper: DokumentMapper, + private val behandlingService: BehandlingService, ) { companion object { @Suppress("JAVA_CLASS_ON_COMPANION") private val logger = getLogger(javaClass.enclosingClass) private val secureLogger = getSecureLogger() + private val objectMapper: ObjectMapper = ourJacksonObjectMapper() } @Scheduled(fixedDelayString = "\${FERDIGSTILLE_DOKUMENTER_DELAY_MILLIS}", initialDelay = 45000) @@ -55,16 +70,31 @@ class FerdigstillDokumentService( logger.debug("dokumentUnderArbeidService.ferdigstillDokumentEnhet(updatedDokument.id) for document with id {} done", updatedDokument.id) + val behandling = behandlingService.getBehandlingForReadWithoutCheckForAccess(behandlingId = updatedDokument.behandlingId) - logger.debug("about to publish 'finished' to Kafak for document with id {}", updatedDokument.id) - //Send to all subscribers. If this fails, it's not the end of the world. - kafkaInternalEventService.publishEvent( - Event( - behandlingId = updatedDokument.behandlingId.toString(), - name = "finished", - id = updatedDokument.id.toString(), - data = updatedDokument.id.toString(), - ) + val dokumentReferanseList = updatedDokument.dokarkivReferences.map { + val journalpost = safFacade.getJournalpostAsSystembruker(journalpostId = it.journalpostId) + + dokumentMapper.mapJournalpostToDokumentReferanse(journalpost, behandling) + } + + publishInternalEvent( + data = objectMapper.writeValueAsString( + DocumentFinishedEvent( + actor = BaseEvent.Actor( + navIdent = updatedDokument.markertFerdigBy!!, + name = saksbehandlerService.getNameForIdent(updatedDokument.markertFerdigBy!!), + ), + timestamp = updatedDokument.ferdigstilt!!, + id = updatedDokument.id.toString(), + journalpostList = dokumentReferanseList.map { + //small hack for now, until we fetch data from SAF on consumer side of event. + it.copy(harTilgangTilArkivvariant = true) + }, + ) + ), + behandlingId = updatedDokument.behandlingId, + type = InternalEventType.DOCUMENT_FINISHED, ) } catch (e: Exception) { @@ -76,4 +106,14 @@ class FerdigstillDokumentService( } logger.debug("ferdigstill for document with id {} successful", updatedDokument.id) } + + private fun publishInternalEvent(data: String, behandlingId: UUID, type: InternalEventType) { + kafkaInternalEventService.publishInternalBehandlingEvent( + InternalBehandlingEvent( + behandlingId = behandlingId.toString(), + type = type, + data = data, + ) + ) + } } \ No newline at end of file diff --git a/src/main/kotlin/no/nav/klage/oppgave/api/controller/BehandlingROLController.kt b/src/main/kotlin/no/nav/klage/oppgave/api/controller/BehandlingROLController.kt index d8a5fa9d3..81e17a0aa 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/api/controller/BehandlingROLController.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/api/controller/BehandlingROLController.kt @@ -1,15 +1,14 @@ package no.nav.klage.oppgave.api.controller import io.swagger.v3.oas.annotations.tags.Tag +import no.nav.klage.oppgave.api.mapper.BehandlingMapper import no.nav.klage.oppgave.api.view.FlowStateInput import no.nav.klage.oppgave.api.view.FlowStateView import no.nav.klage.oppgave.api.view.RolView import no.nav.klage.oppgave.api.view.SaksbehandlerInput import no.nav.klage.oppgave.config.SecurityConfiguration.Companion.ISSUER_AAD -import no.nav.klage.oppgave.domain.klage.Behandling import no.nav.klage.oppgave.service.BehandlingService import no.nav.klage.oppgave.service.InnloggetSaksbehandlerService -import no.nav.klage.oppgave.service.SaksbehandlerService import no.nav.klage.oppgave.util.getLogger import no.nav.klage.oppgave.util.logBehandlingMethodDetails import no.nav.security.token.support.core.api.ProtectedWithClaims @@ -23,7 +22,7 @@ import java.util.* class BehandlingROLController( private val innloggetSaksbehandlerService: InnloggetSaksbehandlerService, private val behandlingService: BehandlingService, - private val saksbehandlerService: SaksbehandlerService, + private val behandlingMapper: BehandlingMapper, ) { companion object { @@ -43,7 +42,7 @@ class BehandlingROLController( ) val behandling = behandlingService.getBehandlingAndCheckLeseTilgangForPerson(behandlingId) - return behandling.toRolView() + return behandlingMapper.mapToRolView(behandling) } @GetMapping("/{id}/rolflowstate") @@ -68,16 +67,14 @@ class BehandlingROLController( ::setROLIdent.name, innloggetSaksbehandlerService.getInnloggetIdent(), behandlingId, - logger + logger, ) - val behandling = behandlingService.setROLIdent( + return behandlingService.setROLIdent( behandlingId = behandlingId, rolIdent = input.navIdent, - utfoerendeSaksbehandlerIdent = innloggetSaksbehandlerService.getInnloggetIdent() + utfoerendeSaksbehandlerIdent = innloggetSaksbehandlerService.getInnloggetIdent(), ) - - return behandling.toRolView() } @PutMapping("/{behandlingId}/rolflowstate") @@ -89,21 +86,13 @@ class BehandlingROLController( ::setROLFlowState.name, innloggetSaksbehandlerService.getInnloggetIdent(), behandlingId, - logger + logger, ) - val behandling = behandlingService.setROLFlowState( + return behandlingService.setROLFlowState( behandlingId = behandlingId, flowState = input.flowState, - utfoerendeSaksbehandlerIdent = innloggetSaksbehandlerService.getInnloggetIdent()) - - return behandling.toRolView() + utfoerendeSaksbehandlerIdent = innloggetSaksbehandlerService.getInnloggetIdent(), + ) } - - private fun Behandling.toRolView() = RolView( - navIdent = rolIdent, - navn = if (rolIdent != null) saksbehandlerService.getNameForIdent(rolIdent!!) else null, - flowState = rolFlowState, - modified = modified, - ) } \ No newline at end of file diff --git a/src/main/kotlin/no/nav/klage/oppgave/api/controller/EventController.kt b/src/main/kotlin/no/nav/klage/oppgave/api/controller/EventController.kt index 1f6833281..c8965a16b 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/api/controller/EventController.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/api/controller/EventController.kt @@ -2,74 +2,237 @@ package no.nav.klage.oppgave.api.controller import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import io.micrometer.core.instrument.MeterRegistry import io.swagger.v3.oas.annotations.tags.Tag -import no.nav.klage.oppgave.clients.events.KafkaEventClient +import jakarta.servlet.http.HttpServletRequest import no.nav.klage.oppgave.config.SecurityConfiguration -import no.nav.klage.oppgave.domain.kafka.Event +import no.nav.klage.oppgave.config.getGauge +import no.nav.klage.oppgave.domain.kafka.InternalBehandlingEvent +import no.nav.klage.oppgave.domain.kafka.InternalIdentityEvent +import no.nav.klage.oppgave.domain.klage.Behandling +import no.nav.klage.oppgave.service.AivenKafkaClientCreator +import no.nav.klage.oppgave.service.BehandlingService import no.nav.klage.oppgave.util.getLogger +import no.nav.klage.oppgave.util.getSecureLogger import no.nav.security.token.support.core.api.ProtectedWithClaims import org.springframework.http.MediaType import org.springframework.http.codec.ServerSentEvent -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 org.springframework.web.bind.annotation.* import reactor.core.publisher.Flux import java.time.Duration +import java.util.* +import java.util.concurrent.atomic.AtomicInteger + @RestController @Tag(name = "kabal-api") @ProtectedWithClaims(issuer = SecurityConfiguration.ISSUER_AAD) -@RequestMapping("/behandlinger/{behandlingId}/events") +@RequestMapping("/behandlinger/{behandlingId}") class EventController( - private val kafkaEventClient: KafkaEventClient, + private val aivenKafkaClientCreator: AivenKafkaClientCreator, + private val meterRegistry: MeterRegistry, + private val behandlingService: BehandlingService, ) { companion object { @Suppress("JAVA_CLASS_ON_COMPANION") private val logger = getLogger(javaClass.enclosingClass) + private val secureLogger = getSecureLogger() } - @GetMapping(produces = [MediaType.TEXT_EVENT_STREAM_VALUE]) + private val gaugeBehandling = meterRegistry.getGauge( + eventType = "behandling", + currentCount = AtomicInteger(0), + ) + + private val gaugeIdentity = meterRegistry.getGauge( + eventType = "identity", + currentCount = AtomicInteger(0), + ) + + private val gaugeBehandlingHeartbeat = meterRegistry.getGauge( + eventType = "behandling-heartbeat", + currentCount = AtomicInteger(0), + ) + + @GetMapping("/events", produces = [MediaType.TEXT_EVENT_STREAM_VALUE]) fun events( - @PathVariable("behandlingId") behandlingId: String, - ): Flux> { - logger.debug("events called for behandlingId: {}", behandlingId) + @PathVariable("behandlingId") behandlingId: UUID, + @RequestParam("test-id", required = false) testId: String? = null, + request: HttpServletRequest, + ): Flux?> { + logger.debug( + "new events called with testId: {}, for behandlingId: {} and protocol: {}", + testId, + behandlingId, + request.protocol + ) + + val behandling = behandlingService.getBehandlingAndCheckLeseTilgangForPerson(behandlingId = behandlingId) //https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-async-disconnects - val heartbeatStream: Flux> = Flux.interval(Duration.ofSeconds(10)) - .takeWhile { true } - .map { tick -> toHeartBeatServerSentEvent(tick) } + val heartbeatStream: Flux> = getHeartbeatStream( + behandlingId = behandlingId, + testId = testId + ) - return kafkaEventClient.getEventPublisher() - .mapNotNull { event -> jsonToEvent(event.data()) } - .filter { it.behandlingId == behandlingId } - .mapNotNull { eventToServerSentEvent(it) } + val behandlingEventPublisher = getBehandlingEventPublisher( + behandlingId = behandlingId, + testId = testId, + ) + + val identityEventPublisher = getIdentityEventPublisher( + behandling = behandling, + testId = testId, + ) + + val emitFirstHeartbeat = getFirstHeartbeat( + behandlingId = behandlingId, + testId = testId, + ) + + return behandlingEventPublisher + .mergeWith(emitFirstHeartbeat) .mergeWith(heartbeatStream) + .mergeWith(identityEventPublisher) } - private fun toHeartBeatServerSentEvent(tick: Long): ServerSentEvent { - return eventToServerSentEvent( - Event( - behandlingId = "", - id = "", - name = "heartbeat-event-$tick", - data = "{}" - ) - ) + private fun getBehandlingEventPublisher( + behandlingId: UUID, + testId: String? + ): Flux?> { + val flux = aivenKafkaClientCreator.getNewKafkaInternalBehandlingEventReceiver().receive() + .mapNotNull { consumerRecord -> + val internalBehandlingEvent = jsonToInternalBehandlingEvent(consumerRecord.value()) + if (internalBehandlingEvent.behandlingId == behandlingId.toString()) { + ServerSentEvent.builder() + .id(consumerRecord.offset().toString()) + .event(internalBehandlingEvent.type.name) + .data(jacksonObjectMapper().readTree(internalBehandlingEvent.data)) + .build() + } else null + } + .doOnCancel { + logger.debug("events cancel for testId: {}, behandlingId: {}", testId, behandlingId) + } + .doOnTerminate { + logger.debug("events terminate for testId: {}, behandlingId: {}", testId, behandlingId) + } + .doFinally { signalType -> + logger.debug( + "events closed for testId: {}, behandlingId: {}. SignalType: {}", + testId, + behandlingId, + signalType, + ) + gaugeBehandling.decrementAndGet() + } + + gaugeBehandling.incrementAndGet() + + return flux + } + + private fun getIdentityEventPublisher( + behandling: Behandling, + testId: String? + ): Flux?> { + val flux = aivenKafkaClientCreator.getNewKafkaInternalIdentityEventReceiver().receive() + .mapNotNull { consumerRecord -> + val internalIdentityEvent = jsonToInternalIdentityEvent(consumerRecord.value()) + if (internalIdentityEvent.identifikator == behandling.sakenGjelder.partId.value) { + ServerSentEvent.builder() + .id(consumerRecord.offset().toString()) + .event(internalIdentityEvent.type.name) + .data(jacksonObjectMapper().readTree(internalIdentityEvent.data)) + .build() + } else null + } + .doOnCancel { + logger.debug("events cancel for testId: {}, behandlingId: {}", testId, behandling.id) + } + .doOnTerminate { + logger.debug("events terminate for testId: {}, behandlingId: {}", testId, behandling.id) + } + .doFinally { signalType -> + logger.debug( + "events closed for testId: {}, behandlingId: {}. SignalType: {}", + testId, + behandling.id, + signalType, + ) + gaugeIdentity.decrementAndGet() + } + + gaugeIdentity.incrementAndGet() + + return flux + } + + private fun getFirstHeartbeat( + behandlingId: UUID, + testId: String?, + ): Flux> { + val emitFirstHeartbeat = Flux.generate> { + it.next(toHeartBeatServerSentEvent()) + it.complete() + } + .doFinally { signalType -> + logger.debug( + "emitFirstHeartbeat closed for testId: {}, behandlingId: {}. SignalType: {}", + testId, + behandlingId, + signalType + ) + } + .doOnCancel { + logger.debug("emitFirstHeartbeat cancel for testId: {}, behandlingId: {}", testId, behandlingId) + } + .doOnTerminate { + logger.debug("emitFirstHeartbeat terminate for testId: {}, behandlingId: {}", testId, behandlingId) + } + return emitFirstHeartbeat } - private fun eventToServerSentEvent(event: Event): ServerSentEvent { + private fun getHeartbeatStream( + behandlingId: UUID, + testId: String?, + ): Flux> { + val heartbeatStream: Flux> = Flux.interval(Duration.ofSeconds(10)) + .map { + logger.debug("creating heartbeat event for testId: {}, behandlingId: {}", testId, behandlingId) + toHeartBeatServerSentEvent() + } + .doFinally { signalType -> + logger.debug( + "heartbeat closed for testId: {}, behandlingId: {}. SignalType: {}", + testId, + behandlingId, + signalType, + ) + gaugeBehandlingHeartbeat.decrementAndGet() + } + .doOnCancel { + logger.debug("heartbeat cancel for testId: {}, behandlingId: {}", testId, behandlingId) + } + .doOnTerminate { + logger.debug("heartbeat terminate for testId: {}, behandlingId: {}", testId, behandlingId) + } + + gaugeBehandlingHeartbeat.incrementAndGet() + + return heartbeatStream + } + + private fun toHeartBeatServerSentEvent(): ServerSentEvent { return ServerSentEvent.builder() - .id(event.id) - .event(event.name) - .data(jacksonObjectMapper().readTree(event.data)) + .event("HEARTBEAT") .build() } - private fun jsonToEvent(json: String?): Event { - val event = jacksonObjectMapper().readValue(json, Event::class.java) - logger.debug("Received event from Kafka: {}", event) - return event - } + private fun jsonToInternalBehandlingEvent(json: String?): InternalBehandlingEvent = + jacksonObjectMapper().readValue(json, InternalBehandlingEvent::class.java) + + private fun jsonToInternalIdentityEvent(json: String?): InternalIdentityEvent = + jacksonObjectMapper().readValue(json, InternalIdentityEvent::class.java) } \ No newline at end of file diff --git a/src/main/kotlin/no/nav/klage/oppgave/api/mapper/BehandlingMapper.kt b/src/main/kotlin/no/nav/klage/oppgave/api/mapper/BehandlingMapper.kt index 6ff1e79a8..4a67335cc 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/api/mapper/BehandlingMapper.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/api/mapper/BehandlingMapper.kt @@ -368,6 +368,13 @@ class BehandlingMapper( ) } + fun mapToRolView(behandling: Behandling) = RolView( + navIdent = behandling.rolIdent, + navn = if (behandling.rolIdent != null) saksbehandlerRepository.getNameForSaksbehandler(behandling.rolIdent!!) else null, + flowState = behandling.rolFlowState, + modified = behandling.modified, + ) + private fun Feilregistrering?.toView(): BehandlingDetaljerView.FeilregistreringView? { return this?.let { BehandlingDetaljerView.FeilregistreringView( diff --git a/src/main/kotlin/no/nav/klage/oppgave/clients/azure/DefaultAzureGateway.kt b/src/main/kotlin/no/nav/klage/oppgave/clients/azure/DefaultAzureGateway.kt index 29eb082a1..843c4ed73 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/clients/azure/DefaultAzureGateway.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/clients/azure/DefaultAzureGateway.kt @@ -52,13 +52,13 @@ class DefaultAzureGateway(private val microsoftGraphClient: MicrosoftGraphClient throw e } return SaksbehandlerPersonligInfo( - data.onPremisesSamAccountName, - data.id, - data.givenName, - data.surname, - data.displayName, - data.mail, - mapToEnhet(data.streetAddress), + navIdent = data.onPremisesSamAccountName, + azureId = data.id, + fornavn = data.givenName, + etternavn = data.surname, + sammensattNavn = data.displayName, + epost = data.mail, + enhet = mapToEnhet(data.streetAddress), ) } diff --git a/src/main/kotlin/no/nav/klage/oppgave/clients/azure/MicrosoftGraphResponse.kt b/src/main/kotlin/no/nav/klage/oppgave/clients/azure/MicrosoftGraphResponse.kt index 5bb1e414f..bd5bce39b 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/clients/azure/MicrosoftGraphResponse.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/clients/azure/MicrosoftGraphResponse.kt @@ -8,7 +8,7 @@ data class AzureUser( val displayName: String, val givenName: String, val surname: String, - val mail: String, + val mail: String?, val officeLocation: String?, val userPrincipalName: String, val id: String, diff --git a/src/main/kotlin/no/nav/klage/oppgave/clients/events/KafkaEventClient.kt b/src/main/kotlin/no/nav/klage/oppgave/clients/events/KafkaEventClient.kt deleted file mode 100644 index ebeae1ea4..000000000 --- a/src/main/kotlin/no/nav/klage/oppgave/clients/events/KafkaEventClient.kt +++ /dev/null @@ -1,27 +0,0 @@ -package no.nav.klage.oppgave.clients.events - -import jakarta.annotation.PostConstruct -import org.springframework.http.codec.ServerSentEvent -import org.springframework.stereotype.Service -import reactor.core.publisher.ConnectableFlux -import reactor.kafka.receiver.KafkaReceiver - -@Service -class KafkaEventClient( - private val kafkaReceiver: KafkaReceiver -) { - - private lateinit var eventPublisher: ConnectableFlux> - - @PostConstruct - fun init() { - eventPublisher = kafkaReceiver.receive() - .map { consumerRecord -> ServerSentEvent.builder(consumerRecord.value()).build() } - .publish() - - // subscribes to the KafkaReceiver -> starts consumption (without observers attached) - eventPublisher.connect() - } - - fun getEventPublisher() = eventPublisher -} \ No newline at end of file diff --git a/src/main/kotlin/no/nav/klage/oppgave/clients/pdl/PdlFacade.kt b/src/main/kotlin/no/nav/klage/oppgave/clients/pdl/PdlFacade.kt index 7815c76cd..31d78874a 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/clients/pdl/PdlFacade.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/clients/pdl/PdlFacade.kt @@ -41,6 +41,10 @@ class PdlFacade( return true } + fun getFoedselsnummerFromSomeIdent(ident: String): String { + return pdlClient.getFoedselsnummerFromSomeIdent(ident = ident) + } + private fun HentPersonResponse.getPersonOrThrowError(fnr: String): PdlPerson = if (this.errors.isNullOrEmpty() && this.data != null && this.data.hentPerson != null) { this.data.hentPerson diff --git a/src/main/kotlin/no/nav/klage/oppgave/clients/pdl/graphql/HentPersonRequest.kt b/src/main/kotlin/no/nav/klage/oppgave/clients/pdl/graphql/HentPersonRequest.kt deleted file mode 100644 index b49719c0a..000000000 --- a/src/main/kotlin/no/nav/klage/oppgave/clients/pdl/graphql/HentPersonRequest.kt +++ /dev/null @@ -1,16 +0,0 @@ -package no.nav.klage.oppgave.clients.pdl.graphql - -data class PersonGraphqlQuery( - val query: String, - val variables: FnrVariables -) - -data class FnrVariables( - val ident: String -) - -fun hentPersonQuery(fnr: String): PersonGraphqlQuery { - val query = - PersonGraphqlQuery::class.java.getResource("/pdl/hentPerson.graphql").cleanForGraphql() - return PersonGraphqlQuery(query, FnrVariables(fnr)) -} diff --git a/src/main/kotlin/no/nav/klage/oppgave/clients/pdl/graphql/PdlClient.kt b/src/main/kotlin/no/nav/klage/oppgave/clients/pdl/graphql/PdlClient.kt index 7dc63b0d6..1fe8e04d3 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/clients/pdl/graphql/PdlClient.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/clients/pdl/graphql/PdlClient.kt @@ -49,4 +49,20 @@ class PdlClient( .block() ?: throw RuntimeException("Person not found") } } + + @Retryable + fun getFoedselsnummerFromSomeIdent(ident: String): String { + return runWithTiming { + val stsSystembrukerToken = tokenUtil.getAppAccessTokenWithPdlScope() + pdlWebClient.post() + .header(HttpHeaders.AUTHORIZATION, "Bearer $stsSystembrukerToken") + .bodyValue(hentFolkeregisterIdentQuery(ident)) + .retrieve() + .onStatus(HttpStatusCode::isError) { response -> + logErrorResponse(response, ::getFoedselsnummerFromSomeIdent.name, secureLogger) + } + .bodyToMono() + .block()?.data?.hentIdenter?.identer?.firstOrNull()?.ident ?: throw RuntimeException("Person not found") + } + } } diff --git a/src/main/kotlin/no/nav/klage/oppgave/clients/pdl/graphql/PdlRequests.kt b/src/main/kotlin/no/nav/klage/oppgave/clients/pdl/graphql/PdlRequests.kt new file mode 100644 index 000000000..4a7fb2ad6 --- /dev/null +++ b/src/main/kotlin/no/nav/klage/oppgave/clients/pdl/graphql/PdlRequests.kt @@ -0,0 +1,22 @@ +package no.nav.klage.oppgave.clients.pdl.graphql + +data class PersonGraphqlQuery( + val query: String, + val variables: IdentVariables +) + +data class IdentVariables( + val ident: String +) + +fun hentPersonQuery(ident: String): PersonGraphqlQuery { + val query = + PersonGraphqlQuery::class.java.getResource("/pdl/hentPerson.graphql").cleanForGraphql() + return PersonGraphqlQuery(query, IdentVariables(ident)) +} + +fun hentFolkeregisterIdentQuery(ident: String): PersonGraphqlQuery { + val query = + PersonGraphqlQuery::class.java.getResource("/pdl/hentIdenter.graphql").cleanForGraphql() + return PersonGraphqlQuery(query, IdentVariables(ident)) +} diff --git a/src/main/kotlin/no/nav/klage/oppgave/clients/pdl/graphql/HentPersonerResponse.kt b/src/main/kotlin/no/nav/klage/oppgave/clients/pdl/graphql/PdlResponses.kt similarity index 78% rename from src/main/kotlin/no/nav/klage/oppgave/clients/pdl/graphql/HentPersonerResponse.kt rename to src/main/kotlin/no/nav/klage/oppgave/clients/pdl/graphql/PdlResponses.kt index ab2186aa2..3f4ff2116 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/clients/pdl/graphql/HentPersonerResponse.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/clients/pdl/graphql/PdlResponses.kt @@ -3,11 +3,23 @@ package no.nav.klage.oppgave.clients.pdl.graphql import com.fasterxml.jackson.annotation.JsonIgnoreProperties import java.time.LocalDate +@JsonIgnoreProperties(ignoreUnknown = true) +data class HentFolkeregisterIdentResponse(val data: HentFolkeregisterIdentDataWrapper?, val errors: List? = null) + +data class HentFolkeregisterIdentDataWrapper(val hentIdenter: FolkeregisterIdenter) + +data class FolkeregisterIdenter( + val identer: List, +) + +data class FolkeregisterIdent( + val ident: String, +) @JsonIgnoreProperties(ignoreUnknown = true) -data class HentPersonResponse(val data: DataWrapper?, val errors: List? = null) +data class HentPersonResponse(val data: PdlPersonDataWrapper?, val errors: List? = null) -data class DataWrapper(val hentPerson: PdlPerson?) +data class PdlPersonDataWrapper(val hentPerson: PdlPerson?) data class PdlPerson( val adressebeskyttelse: List, diff --git a/src/main/kotlin/no/nav/klage/oppgave/clients/saf/SafFacade.kt b/src/main/kotlin/no/nav/klage/oppgave/clients/saf/SafFacade.kt index 6ce975a60..64496263e 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/clients/saf/SafFacade.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/clients/saf/SafFacade.kt @@ -65,6 +65,22 @@ class SafFacade( } } + fun getJournalpostAsSystembruker( + journalpostId: String, + ): Journalpost { + return runWithTimingAndLogging({ + safGraphQlClient.getJournalpostAsSystembruker(journalpostId = journalpostId) + }, this::getJournalpostAsSystembruker.name) + } + + fun getJournalpostAsSaksbehandler( + journalpostId: String, + ): Journalpost { + return runWithTimingAndLogging({ + safGraphQlClient.getJournalpostAsSaksbehandler(journalpostId = journalpostId) + }, this::getJournalpostAsSaksbehandler.name) + } + fun runWithTimingAndLogging(block: () -> T, method: String): T { val start = System.currentTimeMillis() try { diff --git a/src/main/kotlin/no/nav/klage/oppgave/clients/saf/graphql/SafGraphQlClient.kt b/src/main/kotlin/no/nav/klage/oppgave/clients/saf/graphql/SafGraphQlClient.kt index 222bd0018..b42cfd3dc 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/clients/saf/graphql/SafGraphQlClient.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/clients/saf/graphql/SafGraphQlClient.kt @@ -62,6 +62,7 @@ class SafGraphQlClient( } } + @Retryable fun getJournalpostsAsSaksbehandler(journalpostIdSet: Set): List { return getJournalposts( journalpostIdSet = journalpostIdSet, @@ -69,6 +70,7 @@ class SafGraphQlClient( ) } + @Retryable fun getJournalpostsAsSystembruker(journalpostIdSet: Set): List { return getJournalposts( journalpostIdSet = journalpostIdSet, @@ -76,7 +78,27 @@ class SafGraphQlClient( ) } - fun getJournalposts( + @Retryable + fun getJournalpostAsSaksbehandler( + journalpostId: String, + ): Journalpost { + return getJournalpostWithToken( + journalpostId = journalpostId, + token = tokenUtil.getSaksbehandlerAccessTokenWithSafScope() + ) + } + + @Retryable + fun getJournalpostAsSystembruker( + journalpostId: String, + ): Journalpost { + return getJournalpostWithToken( + journalpostId = journalpostId, + token = tokenUtil.getAppAccessTokenWithSafScope() + ) + } + + private fun getJournalposts( journalpostIdSet: Set, token: String, ): List { @@ -99,7 +121,6 @@ class SafGraphQlClient( } } - @Retryable private fun getJournalpostWithTokenAsMono( journalpostId: String, token: String @@ -123,6 +144,25 @@ class SafGraphQlClient( } } + private fun getJournalpostWithToken( + journalpostId: String, + token: String, + ): Journalpost { + return safWebClient.post() + .uri("graphql") + .header( + HttpHeaders.AUTHORIZATION, + "Bearer $token" + ) + .bodyValue(hentJournalpostQuery(journalpostId)) + .retrieve() + .onStatus(HttpStatusCode::isError) { response -> + logErrorResponse(response, "getJournalpost", secureLogger) + } + .bodyToMono() + .block()?.data?.journalpost ?: throw RuntimeException("Got null from SAF for journalpost with id $journalpostId") + } + private fun failOnErrors(response: JournalpostResponse) { if (response.data?.journalpost == null || (response.errors != null && response.errors.map { it.extensions.classification } .contains("ValidationError"))) { diff --git a/src/main/kotlin/no/nav/klage/oppgave/clients/saf/graphql/SafResponse.kt b/src/main/kotlin/no/nav/klage/oppgave/clients/saf/graphql/SafResponse.kt index 6c7be63da..f6e7a9580 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/clients/saf/graphql/SafResponse.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/clients/saf/graphql/SafResponse.kt @@ -27,6 +27,7 @@ data class Journalpost( val journalstatus: Journalstatus?, val tema: Tema?, val sak: Sak?, + val bruker: Bruker, val avsenderMottaker: AvsenderMottaker?, val opprettetAvNavn: String?, val skjerming: String?, @@ -38,6 +39,11 @@ data class Journalpost( val utsendingsinfo: Utsendingsinfo?, ) +data class Bruker( + val id: String, + val type: String, +) + data class AvsenderMottaker( val id: String?, val type: AvsenderMottakerIdType?, diff --git a/src/main/kotlin/no/nav/klage/oppgave/config/AivenKafkaConfiguration.kt b/src/main/kotlin/no/nav/klage/oppgave/config/AivenKafkaConfiguration.kt index 56a87141c..c9d296558 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/config/AivenKafkaConfiguration.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/config/AivenKafkaConfiguration.kt @@ -20,11 +20,7 @@ import org.springframework.kafka.core.KafkaTemplate import org.springframework.kafka.listener.CommonLoggingErrorHandler import org.springframework.kafka.listener.ContainerProperties import org.springframework.kafka.support.serializer.ErrorHandlingDeserializer -import reactor.kafka.receiver.KafkaReceiver -import reactor.kafka.receiver.ReceiverOptions -import reactor.kafka.receiver.internals.DefaultKafkaReceiver import java.time.Duration -import java.util.* @Configuration @@ -37,8 +33,6 @@ class AivenKafkaConfiguration( private val kafkaCredstorePassword: String, @Value("\${KAFKA_KEYSTORE_PATH}") private val kafkaKeystorePath: String, - @Value("\${INTERNAL_EVENT_TOPIC}") - private val internalEventTopic: String, ) { companion object { @@ -61,23 +55,6 @@ class AivenKafkaConfiguration( } //Consumer beans - @Bean - fun kafkaEventReceiver(): KafkaReceiver { - val uniqueIdPerInstance = UUID.randomUUID().toString() - val config = mapOf( - ConsumerConfig.GROUP_ID_CONFIG to "kabal-api-event-consumer-$uniqueIdPerInstance", - ConsumerConfig.CLIENT_ID_CONFIG to "kabal-api-event-client-$uniqueIdPerInstance", - ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG to true, - ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG to StringDeserializer::class.java, - ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG to StringDeserializer::class.java, - ) + commonConfig() - - return DefaultKafkaReceiver( - reactor.kafka.receiver.internals.ConsumerFactory.INSTANCE, - ReceiverOptions.create(config).subscription(listOf(internalEventTopic)) - ) - } - @Bean fun egenAnsattKafkaListenerContainerFactory(): ConcurrentKafkaListenerContainerFactory { val factory = ConcurrentKafkaListenerContainerFactory() diff --git a/src/main/kotlin/no/nav/klage/oppgave/config/MetricsConfiguration.kt b/src/main/kotlin/no/nav/klage/oppgave/config/MetricsConfiguration.kt index a00113ad2..78026bb6a 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/config/MetricsConfiguration.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/config/MetricsConfiguration.kt @@ -1,8 +1,10 @@ package no.nav.klage.oppgave.config import io.micrometer.core.instrument.MeterRegistry +import io.micrometer.core.instrument.Tag import no.nav.klage.oppgave.util.getLogger import org.springframework.context.annotation.Configuration +import java.util.concurrent.atomic.AtomicInteger @Configuration @@ -12,11 +14,12 @@ class MetricsConfiguration { @Suppress("JAVA_CLASS_ON_COMPANION") private val logger = getLogger(javaClass.enclosingClass) const val MOTTATT_KLAGEANKE = "funksjonell.mottattklageanke" + const val CURRENT_EVENT_LISTENERS = "technical.current_event_listeners" } } fun MeterRegistry.incrementMottattKlageAnke(kildesystem: String, ytelse: String, type: String) { - counter( + this.counter( MetricsConfiguration.MOTTATT_KLAGEANKE, "kildesystem", kildesystem, @@ -25,4 +28,12 @@ fun MeterRegistry.incrementMottattKlageAnke(kildesystem: String, ytelse: String, "type", type ).increment() +} + +fun MeterRegistry.getGauge(eventType: String, currentCount: AtomicInteger): AtomicInteger { + return this.gauge( + /* name = */ MetricsConfiguration.CURRENT_EVENT_LISTENERS, + /* tags = */ listOf(Tag.of("event-type", eventType)), + /* number = */ currentCount + )!! } \ No newline at end of file diff --git a/src/main/kotlin/no/nav/klage/oppgave/config/problem/ProblemHandlingControllerAdvice.kt b/src/main/kotlin/no/nav/klage/oppgave/config/problem/ProblemHandlingControllerAdvice.kt index e523667ed..4e7c13cde 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/config/problem/ProblemHandlingControllerAdvice.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/config/problem/ProblemHandlingControllerAdvice.kt @@ -1,11 +1,9 @@ package no.nav.klage.oppgave.config.problem -import jakarta.servlet.http.HttpServletRequest import no.nav.klage.dokument.exceptions.DokumentValidationException import no.nav.klage.dokument.exceptions.JsonToPdfValidationException import no.nav.klage.oppgave.exceptions.* import no.nav.klage.oppgave.util.getSecureLogger -import org.apache.tomcat.util.http.fileupload.impl.SizeLimitExceededException import org.springframework.http.HttpStatus import org.springframework.http.ProblemDetail import org.springframework.web.bind.annotation.ExceptionHandler @@ -22,43 +20,43 @@ class ProblemHandlingControllerAdvice : ResponseEntityExceptionHandler() { private val secureLogger = getSecureLogger() } - @ExceptionHandler - fun catchISE( - ex: IllegalStateException, - request: NativeWebRequest - ): ProblemDetail { - logger.debug("catching IllegalStateException", ex) - - if (ex.cause is SizeLimitExceededException) { - return create(HttpStatus.PAYLOAD_TOO_LARGE, ex) - } - - try { - val nativeRequest = request.nativeRequest - - if (nativeRequest is HttpServletRequest) { - logger.debug("dispatcherType = " + nativeRequest.dispatcherType?.name) - - logger.debug("path = " + nativeRequest.pathInfo) - logger.debug("requestURI = " + nativeRequest.requestURI) - - if (nativeRequest.isAsyncStarted) { - logger.debug("asyncContext = " + nativeRequest.asyncContext) - } - } - } catch (e: Exception) { - logger.warn("problems with handling ISE", e) - } - - return create(HttpStatus.INTERNAL_SERVER_ERROR, ex) - } - - @ExceptionHandler - fun handleSizeLimitExceededException( - ex: SizeLimitExceededException, - request: NativeWebRequest - ): ProblemDetail = - create(HttpStatus.PAYLOAD_TOO_LARGE, ex) +// @ExceptionHandler +// fun catchISE( +// ex: IllegalStateException, +// request: NativeWebRequest +// ): ProblemDetail { +// logger.debug("catching IllegalStateException", ex) +// +// if (ex.cause is SizeLimitExceededException) { +// return create(HttpStatus.PAYLOAD_TOO_LARGE, ex) +// } +// +// try { +// val nativeRequest = request.nativeRequest +// +// if (nativeRequest is HttpServletRequest) { +// logger.debug("dispatcherType = " + nativeRequest.dispatcherType?.name) +// +// logger.debug("path = " + nativeRequest.pathInfo) +// logger.debug("requestURI = " + nativeRequest.requestURI) +// +// if (nativeRequest.isAsyncStarted) { +// logger.debug("asyncContext = " + nativeRequest.asyncContext) +// } +// } +// } catch (e: Exception) { +// logger.warn("problems with handling ISE", e) +// } +// +// return create(HttpStatus.INTERNAL_SERVER_ERROR, ex) +// } +// +// @ExceptionHandler +// fun handleSizeLimitExceededException( +// ex: SizeLimitExceededException, +// request: NativeWebRequest +// ): ProblemDetail = +// create(HttpStatus.PAYLOAD_TOO_LARGE, ex) @ExceptionHandler fun handleFeilregistreringException( @@ -255,6 +253,7 @@ class ProblemHandlingControllerAdvice : ResponseEntityExceptionHandler() { httpStatus.is5xxServerError -> { secureLogger.error("Exception thrown to client: ${httpStatus.reasonPhrase}, $errorMessage", exception) } + else -> { secureLogger.warn("Exception thrown to client: ${httpStatus.reasonPhrase}, $errorMessage", exception) } diff --git a/src/main/kotlin/no/nav/klage/oppgave/domain/kafka/Event.kt b/src/main/kotlin/no/nav/klage/oppgave/domain/kafka/Event.kt deleted file mode 100644 index c5589f614..000000000 --- a/src/main/kotlin/no/nav/klage/oppgave/domain/kafka/Event.kt +++ /dev/null @@ -1,11 +0,0 @@ -package no.nav.klage.oppgave.domain.kafka - -/** - * SSE for subscribing clients - */ -data class Event( - val behandlingId: String, - val name: String, - val id: String, - val data: String, -) \ No newline at end of file diff --git a/src/main/kotlin/no/nav/klage/oppgave/domain/kafka/InternalBehandlingEvent.kt b/src/main/kotlin/no/nav/klage/oppgave/domain/kafka/InternalBehandlingEvent.kt new file mode 100644 index 000000000..e23a49723 --- /dev/null +++ b/src/main/kotlin/no/nav/klage/oppgave/domain/kafka/InternalBehandlingEvent.kt @@ -0,0 +1,162 @@ +package no.nav.klage.oppgave.domain.kafka + +import no.nav.klage.dokument.api.view.DokumentView +import no.nav.klage.kodeverk.FlowState +import no.nav.klage.oppgave.api.view.BehandlingDetaljerView +import no.nav.klage.oppgave.api.view.DokumentReferanse +import java.time.LocalDate +import java.time.LocalDateTime + +data class InternalBehandlingEvent( + val behandlingId: String, + val type: InternalEventType, + val data: String, +) + +data class InternalIdentityEvent( + val identifikator: String, + val type: InternalEventType, + val data: String, +) + +enum class InternalEventType { + DOCUMENT_FINISHED, + DOCUMENTS_ADDED, + DOCUMENTS_REMOVED, + DOCUMENTS_CHANGED, + MESSAGE, // Polling + ROL, // Polling + MEDUNDERSKRIVER, // Polling + KLAGER, + FULLMEKTIG, + UTFALL, + EXTRA_UTFALL, + INNSENDINGSHJEMLER, + REGISTRERINGSHJEMLER, + MOTTATT_VEDTAKSINSTANS, + JOURNALFOERT_DOCUMENT_MODIFIED, +} + +abstract class BaseEvent( + open val actor: Actor, + open val timestamp: LocalDateTime, +) { + data class Actor( + val navIdent: String, + val name: String, + ) +} + +data class MedunderskriverEvent( + override val actor: Actor, + override val timestamp: LocalDateTime, + val navIdent: String?, + val name: String?, + val flowState: FlowState, +) : BaseEvent(actor = actor, timestamp = timestamp) + +data class RolEvent( + override val actor: Actor, + override val timestamp: LocalDateTime, + val navIdent: String?, + val name: String?, + val flowState: FlowState, + val returnDate: LocalDateTime?, +) : BaseEvent(actor = actor, timestamp = timestamp) + +data class KlagerEvent( + override val actor: Actor, + override val timestamp: LocalDateTime, + val part: Part, +) : BaseEvent(actor = actor, timestamp = timestamp) + +data class FullmektigEvent( + override val actor: Actor, + override val timestamp: LocalDateTime, + val part: Part?, +) : BaseEvent(actor = actor, timestamp = timestamp) + +data class Part( + val id: String, + val type: BehandlingDetaljerView.IdType, + val name: String?, + val statusList: List, +) + +data class MeldingEvent( + override val actor: Actor, + override val timestamp: LocalDateTime, + val id: String, + val text: String, +) : BaseEvent(actor = actor, timestamp = timestamp) + +data class UtfallEvent( + override val actor: Actor, + override val timestamp: LocalDateTime, + val utfallId: String?, +) : BaseEvent(actor = actor, timestamp = timestamp) + +data class ExtraUtfallEvent( + override val actor: Actor, + override val timestamp: LocalDateTime, + val utfallIdList: List, +) : BaseEvent(actor = actor, timestamp = timestamp) + +data class InnsendingshjemlerEvent( + override val actor: Actor, + override val timestamp: LocalDateTime, + val hjemmelIdSet: Set, +) : BaseEvent(actor = actor, timestamp = timestamp) + +data class RegistreringshjemlerEvent( + override val actor: Actor, + override val timestamp: LocalDateTime, + val hjemmelIdSet: Set, +) : BaseEvent(actor = actor, timestamp = timestamp) + +data class MottattVedtaksinstansEvent( + override val actor: Actor, + override val timestamp: LocalDateTime, + val mottattVedtaksinstans: LocalDate, +) : BaseEvent(actor = actor, timestamp = timestamp) + +data class DocumentsChangedEvent( + override val actor: Actor, + override val timestamp: LocalDateTime, + val documents: List, +) : BaseEvent(actor = actor, timestamp = timestamp) { + data class DocumentChanged( + val id: String, + val parentId: String?, + val dokumentTypeId: String?, + val tittel: String, + val isMarkertAvsluttet: Boolean, + ) +} + +data class DocumentFinishedEvent( + override val actor: Actor, + override val timestamp: LocalDateTime, + val id: String, + val journalpostList: List, +) : BaseEvent(actor = actor, timestamp = timestamp) + +data class DocumentsRemovedEvent( + override val actor: Actor, + override val timestamp: LocalDateTime, + val idList: List, +) : BaseEvent(actor = actor, timestamp = timestamp) + +data class DocumentsAddedEvent( + override val actor: Actor, + override val timestamp: LocalDateTime, + val documents: List, +) : BaseEvent(actor = actor, timestamp = timestamp) + +data class JournalfoertDocumentModified( + override val actor: Actor, + override val timestamp: LocalDateTime, + val journalpostId: String, + val dokumentInfoId: String, + val tittel: String, +) : BaseEvent(actor = actor, timestamp = timestamp) diff --git a/src/main/kotlin/no/nav/klage/oppgave/domain/saksbehandler/SaksbehandlerPersonligInfo.kt b/src/main/kotlin/no/nav/klage/oppgave/domain/saksbehandler/SaksbehandlerPersonligInfo.kt index 2bcaa766b..0f01ef3f4 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/domain/saksbehandler/SaksbehandlerPersonligInfo.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/domain/saksbehandler/SaksbehandlerPersonligInfo.kt @@ -6,6 +6,6 @@ data class SaksbehandlerPersonligInfo( val fornavn: String, val etternavn: String, val sammensattNavn: String, - val epost: String, + val epost: String?, val enhet: Enhet, ) \ No newline at end of file diff --git a/src/main/kotlin/no/nav/klage/oppgave/eventlisteners/SendBehandlingEndretToKafkaEventListener.kt b/src/main/kotlin/no/nav/klage/oppgave/eventlisteners/SendBehandlingEndretToKafkaEventListener.kt index 61d534c47..686af97b6 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/eventlisteners/SendBehandlingEndretToKafkaEventListener.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/eventlisteners/SendBehandlingEndretToKafkaEventListener.kt @@ -1,11 +1,8 @@ package no.nav.klage.oppgave.eventlisteners import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import no.nav.klage.kodeverk.Type import no.nav.klage.oppgave.domain.events.BehandlingEndretEvent -import no.nav.klage.oppgave.domain.kafka.Event import no.nav.klage.oppgave.domain.klage.AnkeITrygderettenbehandling import no.nav.klage.oppgave.domain.klage.Ankebehandling import no.nav.klage.oppgave.domain.klage.Behandling @@ -13,6 +10,7 @@ import no.nav.klage.oppgave.domain.klage.Klagebehandling import no.nav.klage.oppgave.service.BehandlingEndretKafkaProducer import no.nav.klage.oppgave.service.KafkaInternalEventService import no.nav.klage.oppgave.util.getLogger +import no.nav.klage.oppgave.util.ourJacksonObjectMapper import org.hibernate.Hibernate import org.springframework.context.event.EventListener import org.springframework.stereotype.Service @@ -28,7 +26,7 @@ class SendBehandlingEndretToKafkaEventListener( companion object { @Suppress("JAVA_CLASS_ON_COMPANION") private val logger = getLogger(javaClass.enclosingClass) - val objectMapper: ObjectMapper = jacksonObjectMapper().registerModule(JavaTimeModule()) + val objectMapper: ObjectMapper = ourJacksonObjectMapper() } /* Denne kjøres utenfor transaksjonen. Trenger man at dette kjøres i en transaksjon, kan man bruke @Transactional(propagation = Propagation.REQUIRES_NEW) eller en kombinasjon av @Transactional og @Async */ @@ -50,21 +48,4 @@ class SendBehandlingEndretToKafkaEventListener( logger.error("could not index behandling with id ${behandlingEndretEvent.behandling.id}", e) } } - - //POC for updating FE - @EventListener - @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) - fun publishInternalEvent(behandlingEndretEvent: BehandlingEndretEvent) { - logger.debug("Publishing internal event based on BehandlingEndretEvent for behandlingId {}", behandlingEndretEvent.behandling.id) - behandlingEndretEvent.endringslogginnslag.forEach { - kafkaInternalEventService.publishEvent( - Event( - behandlingId = behandlingEndretEvent.behandling.id.toString(), - name = it.felt.toString(), - id = "", - data = objectMapper.writeValueAsString(it.tilVerdi), - ) - ) - } - } } \ No newline at end of file diff --git a/src/main/kotlin/no/nav/klage/oppgave/eventlisteners/StatistikkTilDVHService.kt b/src/main/kotlin/no/nav/klage/oppgave/eventlisteners/StatistikkTilDVHService.kt index be9a59056..d7911585c 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/eventlisteners/StatistikkTilDVHService.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/eventlisteners/StatistikkTilDVHService.kt @@ -1,7 +1,5 @@ package no.nav.klage.oppgave.eventlisteners -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import no.nav.klage.kodeverk.Fagsystem import no.nav.klage.kodeverk.PartIdType import no.nav.klage.kodeverk.Type @@ -12,6 +10,7 @@ import no.nav.klage.oppgave.domain.klage.* import no.nav.klage.oppgave.repositories.KafkaEventRepository import no.nav.klage.oppgave.util.getLogger import no.nav.klage.oppgave.util.getSecureLogger +import no.nav.klage.oppgave.util.ourJacksonObjectMapper import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import java.time.LocalDateTime @@ -27,7 +26,7 @@ class StatistikkTilDVHService( @Suppress("JAVA_CLASS_ON_COMPANION") private val logger = getLogger(javaClass.enclosingClass) private val secureLogger = getSecureLogger() - private val objectMapper = jacksonObjectMapper().registerModule(JavaTimeModule()) + private val objectMapper = ourJacksonObjectMapper() const val TR_ENHET = "TR0000" } diff --git a/src/main/kotlin/no/nav/klage/oppgave/service/AdminService.kt b/src/main/kotlin/no/nav/klage/oppgave/service/AdminService.kt index df60fedf4..0f67187ff 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/service/AdminService.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/service/AdminService.kt @@ -2,7 +2,6 @@ package no.nav.klage.oppgave.service import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.node.ArrayNode -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import net.javacrumbs.shedlock.spring.annotation.SchedulerLock import no.nav.klage.dokument.clients.klagefileapi.FileApiClient @@ -25,6 +24,7 @@ import no.nav.klage.oppgave.repositories.* import no.nav.klage.oppgave.util.getLogger import no.nav.klage.oppgave.util.getSecureLogger import no.nav.klage.oppgave.util.getSortKey +import no.nav.klage.oppgave.util.ourJacksonObjectMapper import org.slf4j.Logger import org.springframework.data.domain.PageRequest import org.springframework.data.domain.Pageable @@ -62,7 +62,7 @@ class AdminService( @Suppress("JAVA_CLASS_ON_COMPANION") private val logger = getLogger(javaClass.enclosingClass) private val secureLogger = getSecureLogger() - private val objectMapper = jacksonObjectMapper().registerModule(JavaTimeModule()) + private val objectMapper = ourJacksonObjectMapper() } fun syncKafkaWithDb() { diff --git a/src/main/kotlin/no/nav/klage/oppgave/service/AivenKafkaClientCreator.kt b/src/main/kotlin/no/nav/klage/oppgave/service/AivenKafkaClientCreator.kt new file mode 100644 index 000000000..0dbe21741 --- /dev/null +++ b/src/main/kotlin/no/nav/klage/oppgave/service/AivenKafkaClientCreator.kt @@ -0,0 +1,82 @@ +package no.nav.klage.oppgave.service + +import no.nav.klage.oppgave.util.getLogger +import no.nav.klage.oppgave.util.getSecureLogger +import org.apache.kafka.clients.CommonClientConfigs +import org.apache.kafka.clients.CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG +import org.apache.kafka.clients.consumer.ConsumerConfig +import org.apache.kafka.common.config.SslConfigs +import org.apache.kafka.common.serialization.StringDeserializer +import org.springframework.beans.factory.annotation.Value +import org.springframework.stereotype.Service +import reactor.kafka.receiver.KafkaReceiver +import reactor.kafka.receiver.ReceiverOptions +import reactor.kafka.receiver.internals.ConsumerFactory +import reactor.kafka.receiver.internals.DefaultKafkaReceiver +import java.util.* + + +@Service +class AivenKafkaClientCreator( + @Value("\${KAFKA_BROKERS}") + private val kafkaBrokers: String, + @Value("\${KAFKA_TRUSTSTORE_PATH}") + private val kafkaTruststorePath: String, + @Value("\${KAFKA_CREDSTORE_PASSWORD}") + private val kafkaCredstorePassword: String, + @Value("\${KAFKA_KEYSTORE_PATH}") + private val kafkaKeystorePath: String, + @Value("\${INTERNAL_BEHANDLING_EVENT_TOPIC}") + private val internalBehandlingEventTopicV1: String, + @Value("\${INTERNAL_IDENTITY_EVENT_TOPIC}") + private val internalIdentityEventTopicV1: String, +) { + + companion object { + @Suppress("JAVA_CLASS_ON_COMPANION") + private val logger = getLogger(javaClass.enclosingClass) + private val secureLogger = getSecureLogger() + } + + fun getNewKafkaInternalBehandlingEventReceiver(): KafkaReceiver { + return defaultKafkaReceiver(topic = internalBehandlingEventTopicV1, eventType = "behandling") + } + + fun getNewKafkaInternalIdentityEventReceiver(): KafkaReceiver { + return defaultKafkaReceiver(topic = internalIdentityEventTopicV1, eventType = "identity") + } + + private fun defaultKafkaReceiver(topic: String, eventType: String): DefaultKafkaReceiver { + val uniqueIdPerInstance = UUID.randomUUID().toString() + val config = mapOf( + ConsumerConfig.GROUP_ID_CONFIG to "kabal-api-$eventType-event-consumer-$uniqueIdPerInstance", + ConsumerConfig.CLIENT_ID_CONFIG to "kabal-api-$eventType-event-client-$uniqueIdPerInstance", + ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG to true, + ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG to StringDeserializer::class.java, + ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG to StringDeserializer::class.java, + ) + commonConfig() + + return DefaultKafkaReceiver( + ConsumerFactory.INSTANCE, + ReceiverOptions.create(config).subscription(listOf(topic)) + ) + } + + //Common + private fun commonConfig() = mapOf( + BOOTSTRAP_SERVERS_CONFIG to kafkaBrokers + ) + securityConfig() + + private fun securityConfig() = mapOf( + CommonClientConfigs.SECURITY_PROTOCOL_CONFIG to "SSL", + SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG to "", // Disable server host name verification + SslConfigs.SSL_TRUSTSTORE_TYPE_CONFIG to "JKS", + SslConfigs.SSL_KEYSTORE_TYPE_CONFIG to "PKCS12", + SslConfigs.SSL_TRUSTSTORE_LOCATION_CONFIG to kafkaTruststorePath, + SslConfigs.SSL_TRUSTSTORE_PASSWORD_CONFIG to kafkaCredstorePassword, + SslConfigs.SSL_KEYSTORE_LOCATION_CONFIG to kafkaKeystorePath, + SslConfigs.SSL_KEYSTORE_PASSWORD_CONFIG to kafkaCredstorePassword, + SslConfigs.SSL_KEY_PASSWORD_CONFIG to kafkaCredstorePassword, + ) + +} \ No newline at end of file diff --git a/src/main/kotlin/no/nav/klage/oppgave/service/BehandlingService.kt b/src/main/kotlin/no/nav/klage/oppgave/service/BehandlingService.kt index b818dad5a..29067ee28 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/service/BehandlingService.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/service/BehandlingService.kt @@ -1,5 +1,6 @@ package no.nav.klage.oppgave.service +import com.fasterxml.jackson.databind.ObjectMapper import no.nav.klage.dokument.api.view.JournalfoertDokumentReference import no.nav.klage.dokument.repositories.DokumentUnderArbeidRepository import no.nav.klage.kodeverk.* @@ -17,6 +18,13 @@ import no.nav.klage.oppgave.clients.klagefssproxy.KlageFssProxyClient import no.nav.klage.oppgave.clients.klagefssproxy.domain.HandledInKabalInput import no.nav.klage.oppgave.clients.klagefssproxy.domain.SakAssignedInput import no.nav.klage.oppgave.domain.events.BehandlingEndretEvent +import no.nav.klage.oppgave.domain.kafka.* +import no.nav.klage.oppgave.domain.kafka.BaseEvent +import no.nav.klage.oppgave.domain.kafka.FullmektigEvent +import no.nav.klage.oppgave.domain.kafka.KlagerEvent +import no.nav.klage.oppgave.domain.kafka.MedunderskriverEvent +import no.nav.klage.oppgave.domain.kafka.Part +import no.nav.klage.oppgave.domain.kafka.RolEvent import no.nav.klage.oppgave.domain.klage.* import no.nav.klage.oppgave.domain.klage.AnkeITrygderettenbehandlingSetters.setKjennelseMottatt import no.nav.klage.oppgave.domain.klage.AnkeITrygderettenbehandlingSetters.setNyAnkebehandlingKA @@ -46,6 +54,7 @@ import no.nav.klage.oppgave.repositories.BehandlingRepository import no.nav.klage.oppgave.repositories.SaksbehandlerRepository import no.nav.klage.oppgave.util.getLogger import no.nav.klage.oppgave.util.getPartIdFromIdentifikator +import no.nav.klage.oppgave.util.ourJacksonObjectMapper import org.springframework.beans.factory.annotation.Value import org.springframework.context.ApplicationEventPublisher import org.springframework.stereotype.Service @@ -73,11 +82,15 @@ class BehandlingService( private val saksbehandlerService: SaksbehandlerService, private val behandlingMapper: BehandlingMapper, private val historyService: HistoryService, + private val kafkaInternalEventService: KafkaInternalEventService, + private val partSearchService: PartSearchService, @Value("\${SYSTEMBRUKER_IDENT}") private val systembrukerIdent: String, ) { companion object { @Suppress("JAVA_CLASS_ON_COMPANION") private val logger = getLogger(javaClass.enclosingClass) + + private val objectMapper: ObjectMapper = ourJacksonObjectMapper() } @@ -575,6 +588,23 @@ class BehandlingService( utfoerendeIdent = systembrukerIdent, ) applicationEventPublisher.publishEvent(medunderskriverIdentEvent) + + publishInternalEvent( + data = objectMapper.writeValueAsString( + MedunderskriverEvent( + actor = BaseEvent.Actor( + navIdent = systembrukerIdent, + name = systembrukerIdent, + ), + timestamp = behandling.modified, + navIdent = null, + name = null, + flowState = FlowState.NOT_SENT, + ) + ), + behandlingId = behandlingId, + type = InternalEventType.MEDUNDERSKRIVER, + ) } fun setRolToNullInSystemContext( @@ -600,13 +630,30 @@ class BehandlingService( } } - val rolIdentEvent = behandling.setROLIdent( newROLIdent = null, utfoerendeIdent = systembrukerIdent ) applicationEventPublisher.publishEvent(rolIdentEvent) + + publishInternalEvent( + data = objectMapper.writeValueAsString( + RolEvent( + actor = BaseEvent.Actor( + navIdent = systembrukerIdent, + name = systembrukerIdent, + ), + timestamp = behandling.modified, + navIdent = null, + name = null, + flowState = FlowState.NOT_SENT, + returnDate = null, + ) + ), + behandlingId = behandlingId, + type = InternalEventType.ROL, + ) } @@ -721,6 +768,22 @@ class BehandlingService( val event = behandling.setMottattVedtaksinstans(date, utfoerendeSaksbehandlerIdent) applicationEventPublisher.publishEvent(event) + + publishInternalEvent( + data = objectMapper.writeValueAsString( + MottattVedtaksinstansEvent( + actor = BaseEvent.Actor( + navIdent = utfoerendeSaksbehandlerIdent, + name = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), + ), + timestamp = behandling.modified, + mottattVedtaksinstans = behandling.mottattVedtaksinstans, + ) + ), + behandlingId = behandlingId, + type = InternalEventType.MOTTATT_VEDTAKSINSTANS, + ) + return behandling.modified } else throw IllegalOperation("Dette feltet kan bare settes i klagesaker") } @@ -796,6 +859,22 @@ class BehandlingService( utfoerendeSaksbehandlerIdent ) applicationEventPublisher.publishEvent(event) + + publishInternalEvent( + data = objectMapper.writeValueAsString( + InnsendingshjemlerEvent( + actor = BaseEvent.Actor( + navIdent = utfoerendeSaksbehandlerIdent, + name = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), + ), + timestamp = behandling.modified, + hjemmelIdSet = behandling.hjemler.map { it.id }.toSet(), + ) + ), + behandlingId = behandlingId, + type = InternalEventType.INNSENDINGSHJEMLER, + ) + return behandling.modified } @@ -820,6 +899,35 @@ class BehandlingService( utfoerendeSaksbehandlerIdent ) applicationEventPublisher.publishEvent(event) + + val partView = if (behandling.klager.prosessfullmektig == null) { + null + } else { + partSearchService.searchPart(identifikator = behandling.klager.prosessfullmektig?.partId?.value!!, skipAccessControl = true) + } + + publishInternalEvent( + data = objectMapper.writeValueAsString( + FullmektigEvent( + actor = BaseEvent.Actor( + navIdent = utfoerendeSaksbehandlerIdent, + name = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), + ), + timestamp = behandling.modified, + part = partView?.let { + Part( + id = partView.id, + type = partView.type, + name = partView.name, + statusList = partView.statusList, + ) + }, + ) + ), + behandlingId = behandlingId, + type = InternalEventType.FULLMEKTIG, + ) + return behandling.modified } @@ -838,6 +946,30 @@ class BehandlingService( utfoerendeIdent = utfoerendeSaksbehandlerIdent ) applicationEventPublisher.publishEvent(event) + + val partView = + partSearchService.searchPart(identifikator = behandling.klager.partId.value, skipAccessControl = true) + + publishInternalEvent( + data = objectMapper.writeValueAsString( + KlagerEvent( + actor = BaseEvent.Actor( + navIdent = utfoerendeSaksbehandlerIdent, + name = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), + ), + timestamp = behandling.modified, + part = Part( + id = partView.id, + type = partView.type, + name = partView.name, + statusList = partView.statusList, + ), + ) + ), + behandlingId = behandlingId, + type = InternalEventType.KLAGER, + ) + return behandling.modified } @@ -857,7 +989,29 @@ class BehandlingService( utfoerendeIdent = utfoerendeSaksbehandlerIdent ) applicationEventPublisher.publishEvent(event) - return behandlingMapper.mapToMedunderskriverWrapped(behandling) + + val medunderskriverWrapped = behandlingMapper.mapToMedunderskriverWrapped(behandling) + + publishInternalEvent( + data = objectMapper.writeValueAsString( + MedunderskriverEvent( + actor = BaseEvent.Actor( + navIdent = utfoerendeSaksbehandlerIdent, + name = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), + ), + timestamp = medunderskriverWrapped.modified, + navIdent = medunderskriverWrapped.navIdent, + name = if (medunderskriverWrapped.navIdent != null) saksbehandlerService.getNameForIdent( + medunderskriverWrapped.navIdent + ) else null, + flowState = medunderskriverWrapped.flowState, + ) + ), + behandlingId = behandlingId, + type = InternalEventType.MEDUNDERSKRIVER, + ) + + return medunderskriverWrapped } fun setMedunderskriverNavIdent( @@ -890,7 +1044,39 @@ class BehandlingService( utfoerendeIdent = utfoerendeSaksbehandlerIdent ) applicationEventPublisher.publishEvent(event) - return behandlingMapper.mapToMedunderskriverWrapped(behandling) + + val medunderskriverWrapped = behandlingMapper.mapToMedunderskriverWrapped(behandling) + + publishInternalEvent( + data = objectMapper.writeValueAsString( + MedunderskriverEvent( + actor = BaseEvent.Actor( + navIdent = utfoerendeSaksbehandlerIdent, + name = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), + ), + timestamp = medunderskriverWrapped.modified, + navIdent = medunderskriverWrapped.navIdent, + name = if (medunderskriverWrapped.navIdent != null) saksbehandlerService.getNameForIdent( + medunderskriverWrapped.navIdent + ) else null, + flowState = medunderskriverWrapped.flowState, + ) + ), + behandlingId = behandlingId, + type = InternalEventType.MEDUNDERSKRIVER, + ) + + return medunderskriverWrapped + } + + private fun publishInternalEvent(data: String, behandlingId: UUID, type: InternalEventType) { + kafkaInternalEventService.publishInternalBehandlingEvent( + InternalBehandlingEvent( + behandlingId = behandlingId.toString(), + type = type, + data = data, + ) + ) } fun fetchDokumentlisteForBehandling( @@ -1259,6 +1445,21 @@ class BehandlingService( ) applicationEventPublisher.publishEvent(groupedEvent) + publishInternalEvent( + data = objectMapper.writeValueAsString( + UtfallEvent( + actor = BaseEvent.Actor( + navIdent = utfoerendeSaksbehandlerIdent, + name = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), + ), + timestamp = behandling.modified, + utfallId = behandling.utfall?.id, + ) + ), + behandlingId = behandlingId, + type = InternalEventType.UTFALL, + ) + return behandling } @@ -1281,6 +1482,22 @@ class BehandlingService( saksbehandlerident = utfoerendeSaksbehandlerIdent ) applicationEventPublisher.publishEvent(event) + + publishInternalEvent( + data = objectMapper.writeValueAsString( + ExtraUtfallEvent( + actor = BaseEvent.Actor( + navIdent = utfoerendeSaksbehandlerIdent, + name = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), + ), + timestamp = behandling.modified, + utfallIdList = behandling.extraUtfallSet.map { it.id }, + ) + ), + behandlingId = behandlingId, + type = InternalEventType.EXTRA_UTFALL, + ) + return behandling } @@ -1298,6 +1515,22 @@ class BehandlingService( val event = behandling.setRegistreringshjemler(registreringshjemler, utfoerendeSaksbehandlerIdent) applicationEventPublisher.publishEvent(event) + + publishInternalEvent( + data = objectMapper.writeValueAsString( + RegistreringshjemlerEvent( + actor = BaseEvent.Actor( + navIdent = utfoerendeSaksbehandlerIdent, + name = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), + ), + timestamp = behandling.modified, + hjemmelIdSet = behandling.registreringshjemler.map { it.id }.toSet(), + ) + ), + behandlingId = behandlingId, + type = InternalEventType.REGISTRERINGSHJEMLER, + ) + return behandling } @@ -1306,7 +1539,7 @@ class BehandlingService( flowState: FlowState, utfoerendeSaksbehandlerIdent: String, systemUserContext: Boolean = false - ): Behandling { + ): RolView { val behandling = getBehandlingForWriteAllowROLAndMU( behandlingId = behandlingId, utfoerendeSaksbehandlerIdent = utfoerendeSaksbehandlerIdent, @@ -1326,7 +1559,27 @@ class BehandlingService( ) applicationEventPublisher.publishEvent(event2) - return behandling + val rolView = behandlingMapper.mapToRolView(behandling) + + publishInternalEvent( + data = objectMapper.writeValueAsString( + RolEvent( + actor = BaseEvent.Actor( + navIdent = utfoerendeSaksbehandlerIdent, + name = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), + ), + timestamp = rolView.modified, + navIdent = rolView.navIdent, + name = rolView.navn, + flowState = rolView.flowState, + returnDate = behandling.rolReturnedDate, + ) + ), + behandlingId = behandlingId, + type = InternalEventType.ROL, + ) + + return rolView } fun setROLIdent( @@ -1334,7 +1587,7 @@ class BehandlingService( rolIdent: String?, utfoerendeSaksbehandlerIdent: String, systemUserContext: Boolean = false - ): Behandling { + ): RolView { val behandlingForCheck = getBehandlingAndCheckLeseTilgangForPerson(behandlingId) val behandling = if (saksbehandlerRepository.isKROL(utfoerendeSaksbehandlerIdent)) { @@ -1371,7 +1624,28 @@ class BehandlingService( utfoerendeIdent = utfoerendeSaksbehandlerIdent ) applicationEventPublisher.publishEvent(event) - return behandling + + val rolView = behandlingMapper.mapToRolView(behandling) + + publishInternalEvent( + data = objectMapper.writeValueAsString( + RolEvent( + actor = BaseEvent.Actor( + navIdent = utfoerendeSaksbehandlerIdent, + name = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), + ), + timestamp = rolView.modified, + navIdent = rolView.navIdent, + name = rolView.navn, + flowState = rolView.flowState, + returnDate = behandling.rolReturnedDate, + ) + ), + behandlingId = behandlingId, + type = InternalEventType.ROL, + ) + + return rolView } fun findCompletedBehandlingById(behandlingId: UUID): CompletedBehandling { diff --git a/src/main/kotlin/no/nav/klage/oppgave/service/DokumentService.kt b/src/main/kotlin/no/nav/klage/oppgave/service/DokumentService.kt index 5b26dd8cc..197c555b2 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/service/DokumentService.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/service/DokumentService.kt @@ -1,5 +1,6 @@ package no.nav.klage.oppgave.service +import com.fasterxml.jackson.databind.ObjectMapper import no.nav.klage.dokument.api.mapper.DokumentMapper import no.nav.klage.dokument.api.view.JournalfoertDokumentReference import no.nav.klage.dokument.domain.FysiskDokument @@ -8,9 +9,11 @@ import no.nav.klage.oppgave.api.view.DokumentReferanse import no.nav.klage.oppgave.api.view.DokumenterResponse import no.nav.klage.oppgave.api.view.JournalfoertDokumentMetadata import no.nav.klage.oppgave.clients.kabaldocument.KabalDocumentGateway +import no.nav.klage.oppgave.clients.pdl.PdlFacade import no.nav.klage.oppgave.clients.saf.SafFacade import no.nav.klage.oppgave.clients.saf.graphql.* import no.nav.klage.oppgave.clients.saf.rest.SafRestClient +import no.nav.klage.oppgave.domain.kafka.* import no.nav.klage.oppgave.domain.klage.Behandling import no.nav.klage.oppgave.domain.klage.DocumentToMerge import no.nav.klage.oppgave.domain.klage.MergedDocument @@ -19,6 +22,7 @@ import no.nav.klage.oppgave.exceptions.JournalpostNotFoundException import no.nav.klage.oppgave.repositories.MergedDocumentRepository import no.nav.klage.oppgave.util.getLogger import no.nav.klage.oppgave.util.getSecureLogger +import no.nav.klage.oppgave.util.ourJacksonObjectMapper import org.apache.pdfbox.Loader import org.apache.pdfbox.io.MemoryUsageSetting import org.apache.pdfbox.io.RandomAccessReadBuffer @@ -48,11 +52,16 @@ class DokumentService( private val dokumentMapper: DokumentMapper, private val kabalDocumentGateway: KabalDocumentGateway, private val safFacade: SafFacade, + private val kafkaInternalEventService: KafkaInternalEventService, + private val innloggetSaksbehandlerService: InnloggetSaksbehandlerService, + private val saksbehandlerService: SaksbehandlerService, + private val pdlFacade: PdlFacade, ) { companion object { @Suppress("JAVA_CLASS_ON_COMPANION") private val logger = getLogger(javaClass.enclosingClass) private val secureLogger = getSecureLogger() + private val objectMapper: ObjectMapper = ourJacksonObjectMapper() } fun fetchDokumentlisteForBehandling( @@ -335,10 +344,45 @@ class DokumentService( dokumentInfoId: String, title: String ) { - return kabalDocumentGateway.updateDocumentTitle( + kabalDocumentGateway.updateDocumentTitle( journalpostId = journalpostId, dokumentInfoId = dokumentInfoId, title = title ) + + val journalpost = safFacade.getJournalpostAsSaksbehandler( + journalpostId = journalpostId, + ) + + val foedselsnummer = pdlFacade.getFoedselsnummerFromSomeIdent(journalpost.bruker.id) + + val innloggetIdent = innloggetSaksbehandlerService.getInnloggetIdent() + + publishInternalEvent( + data = objectMapper.writeValueAsString( + JournalfoertDocumentModified( + actor = BaseEvent.Actor( + navIdent = innloggetIdent, + name = saksbehandlerService.getNameForIdent(innloggetIdent), + ), + timestamp = LocalDateTime.now(), + journalpostId = journalpostId, + dokumentInfoId = dokumentInfoId, + tittel = title, + ) + ), + identifikator = foedselsnummer, + type = InternalEventType.JOURNALFOERT_DOCUMENT_MODIFIED, + ) + } + + private fun publishInternalEvent(data: String, identifikator: String, type: InternalEventType) { + kafkaInternalEventService.publishInternalIdentityEvent( + InternalIdentityEvent( + identifikator = identifikator, + type = type, + data = data, + ) + ) } } diff --git a/src/main/kotlin/no/nav/klage/oppgave/service/KafkaInternalEventService.kt b/src/main/kotlin/no/nav/klage/oppgave/service/KafkaInternalEventService.kt index 4bf04ac51..ad44e118c 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/service/KafkaInternalEventService.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/service/KafkaInternalEventService.kt @@ -1,7 +1,8 @@ package no.nav.klage.oppgave.service import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import no.nav.klage.oppgave.domain.kafka.Event +import no.nav.klage.oppgave.domain.kafka.InternalBehandlingEvent +import no.nav.klage.oppgave.domain.kafka.InternalIdentityEvent import no.nav.klage.oppgave.util.getLogger import no.nav.klage.oppgave.util.getSecureLogger import org.springframework.beans.factory.annotation.Value @@ -11,8 +12,10 @@ import org.springframework.stereotype.Service @Service class KafkaInternalEventService( private val aivenKafkaTemplate: KafkaTemplate, - @Value("\${INTERNAL_EVENT_TOPIC}") - private val internalEventTopic: String, + @Value("\${INTERNAL_BEHANDLING_EVENT_TOPIC}") + private val internalBehandlingEventTopic: String, + @Value("\${INTERNAL_IDENTITY_EVENT_TOPIC}") + private val internalIdentityEventTopic: String, ) { companion object { @@ -21,17 +24,31 @@ class KafkaInternalEventService( private val secureLogger = getSecureLogger() } - fun publishEvent(event: Event) { + fun publishInternalBehandlingEvent(internalBehandlingEvent: InternalBehandlingEvent) { runCatching { - logger.debug("Publishing internal event to Kafka for subscribers: {}", event) + logger.debug("Publishing internalBehandlingEvent to Kafka for subscribers: {}", internalBehandlingEvent) val result = aivenKafkaTemplate.send( - internalEventTopic, - jacksonObjectMapper().writeValueAsString(event) + internalBehandlingEventTopic, + jacksonObjectMapper().writeValueAsString(internalBehandlingEvent) ).get() - logger.debug("Published internal event to Kafka for subscribers: {}", result) + logger.debug("Published internalBehandlingEvent to Kafka for subscribers: {}", result) }.onFailure { - logger.error("Could not publish internal event to subscribers", it) + logger.error("Could not publish internalBehandlingEvent to subscribers", it) + } + } + + fun publishInternalIdentityEvent(internalIdentityEvent: InternalIdentityEvent) { + runCatching { + logger.debug("Publishing internalIdentityEvent to Kafka for subscribers: {}", internalIdentityEvent) + + val result = aivenKafkaTemplate.send( + internalIdentityEventTopic, + jacksonObjectMapper().writeValueAsString(internalIdentityEvent) + ).get() + logger.debug("Published internalIdentityEvent to Kafka for subscribers: {}", result) + }.onFailure { + logger.error("Could not publish internalIdentityEvent to subscribers", it) } } } \ No newline at end of file diff --git a/src/main/kotlin/no/nav/klage/oppgave/service/MeldingService.kt b/src/main/kotlin/no/nav/klage/oppgave/service/MeldingService.kt index 48274b5e7..1d9ea6fe1 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/service/MeldingService.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/service/MeldingService.kt @@ -1,17 +1,18 @@ package no.nav.klage.oppgave.service import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import jakarta.persistence.EntityNotFoundException -import no.nav.klage.oppgave.api.mapper.MeldingMapper -import no.nav.klage.oppgave.domain.kafka.Event +import no.nav.klage.oppgave.domain.kafka.BaseEvent +import no.nav.klage.oppgave.domain.kafka.InternalBehandlingEvent +import no.nav.klage.oppgave.domain.kafka.InternalEventType +import no.nav.klage.oppgave.domain.kafka.MeldingEvent import no.nav.klage.oppgave.domain.klage.Melding import no.nav.klage.oppgave.exceptions.MeldingNotFoundException import no.nav.klage.oppgave.exceptions.MissingTilgangException import no.nav.klage.oppgave.repositories.BehandlingRepository import no.nav.klage.oppgave.repositories.MeldingRepository import no.nav.klage.oppgave.util.getLogger +import no.nav.klage.oppgave.util.ourJacksonObjectMapper import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import java.time.LocalDateTime @@ -23,14 +24,14 @@ class MeldingService( private val meldingRepository: MeldingRepository, private val behandlingRepository: BehandlingRepository, private val kafkaInternalEventService: KafkaInternalEventService, - private val meldingMapper: MeldingMapper, + private val saksbehandlerService: SaksbehandlerService, ) { companion object { @Suppress("JAVA_CLASS_ON_COMPANION") private val logger = getLogger(javaClass.enclosingClass) - val objectMapper: ObjectMapper = jacksonObjectMapper().registerModule(JavaTimeModule()) + val objectMapper: ObjectMapper = ourJacksonObjectMapper() } fun addMelding( @@ -39,16 +40,24 @@ class MeldingService( text: String ): Melding { logger.debug("saving new melding by $innloggetIdent") - return meldingRepository.save( + + val melding = meldingRepository.save( Melding( text = text, behandlingId = behandlingId, saksbehandlerident = innloggetIdent, - created = LocalDateTime.now() + created = LocalDateTime.now(), ) - ).also { - publishInternalEvent(melding = it, type = "message_added") - } + ) + + publishInternalEvent( + melding = melding, + utfoerendeIdent = innloggetIdent, + utfoerendeName = saksbehandlerService.getNameForIdent(innloggetIdent), + timestamp = melding.modified ?: melding.created, + ) + + return melding } fun deleteMelding( @@ -64,7 +73,7 @@ class MeldingService( logger.debug("melding ({}) deleted by {}", meldingId, innloggetIdent) - publishInternalEvent(melding = melding, type = "message_deleted") +// publishInternalEvent(melding = melding, type = "message_deleted") } catch (enfe: EntityNotFoundException) { throw MeldingNotFoundException("couldn't find melding with id $meldingId") } @@ -86,7 +95,7 @@ class MeldingService( meldingRepository.save(melding) logger.debug("melding ({}) modified by {}", meldingId, innloggetIdent) - publishInternalEvent(melding = melding, type = "message_modified") +// publishInternalEvent(melding = melding, type = "message_modified") return melding } catch (enfe: EntityNotFoundException) { @@ -117,13 +126,25 @@ class MeldingService( } } - private fun publishInternalEvent(melding: Melding, type: String) { - kafkaInternalEventService.publishEvent( - Event( + //TODO other types + private fun publishInternalEvent( + melding: Melding, + utfoerendeIdent: String, + utfoerendeName: String, + timestamp: LocalDateTime, + ) { + kafkaInternalEventService.publishInternalBehandlingEvent( + InternalBehandlingEvent( behandlingId = melding.behandlingId.toString(), - name = type, - id = melding.id.toString(), - data = objectMapper.writeValueAsString(meldingMapper.toMeldingView(melding)), + type = InternalEventType.MESSAGE, + data = objectMapper.writeValueAsString( + MeldingEvent( + actor = BaseEvent.Actor(navIdent = utfoerendeIdent, name = utfoerendeName), + timestamp = timestamp, + id = melding.id.toString(), + text = melding.text, + ) + ) ) ) } diff --git a/src/main/kotlin/no/nav/klage/oppgave/util/JacksonObjectMapper.kt b/src/main/kotlin/no/nav/klage/oppgave/util/JacksonObjectMapper.kt new file mode 100644 index 000000000..0b997a899 --- /dev/null +++ b/src/main/kotlin/no/nav/klage/oppgave/util/JacksonObjectMapper.kt @@ -0,0 +1,13 @@ +package no.nav.klage.oppgave.util + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import java.text.SimpleDateFormat + +fun ourJacksonObjectMapper(): ObjectMapper { + val jacksonObjectMapper = jacksonObjectMapper() + jacksonObjectMapper.registerModule(JavaTimeModule()) + jacksonObjectMapper.dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSS") + return jacksonObjectMapper +} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index a0745dce2..1afb10aa4 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -48,6 +48,11 @@ server: whitelabel: enabled: false shutdown: graceful + http2: + enabled: true + tomcat: + threads: + max: 600 navCallIdName: Nav-Callid @@ -244,7 +249,10 @@ MICROSOFT_GRAPH_URL: https://graph.microsoft.com/v1.0 EGENANSATT_KAFKA_TOPIC: nom.skjermede-personer-v1 PDL_PERSON_KAFKA_TOPIC: aapen-person-pdl-dokument-v1 -INTERNAL_EVENT_TOPIC: klage.internal-events.v1 +INTERNAL_BEHANDLING_EVENT_TOPIC: klage.internal-behandling-events.v1 +INTERNAL_IDENTITY_EVENT_TOPIC: klage.internal-identity-events.v1 + +JOARK_DOCUMENT_TOPIC: teamdokumenthandtering.aapen-dok-journalfoering-q1 SERVICE_USER_PASSWORD: itest diff --git a/src/main/resources/pdl/hentIdenter.graphql b/src/main/resources/pdl/hentIdenter.graphql new file mode 100644 index 000000000..2f7a80dc0 --- /dev/null +++ b/src/main/resources/pdl/hentIdenter.graphql @@ -0,0 +1,7 @@ +query($ident: ID!) { + hentIdenter(ident: $ident, grupper: [FOLKEREGISTERIDENT]) { + identer { + ident + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/no/nav/klage/dokument/api/controller/DokumentUnderArbeidControllerTest.kt b/src/test/kotlin/no/nav/klage/dokument/api/controller/DokumentUnderArbeidControllerTest.kt index d00a502e0..f265b3426 100644 --- a/src/test/kotlin/no/nav/klage/dokument/api/controller/DokumentUnderArbeidControllerTest.kt +++ b/src/test/kotlin/no/nav/klage/dokument/api/controller/DokumentUnderArbeidControllerTest.kt @@ -1,40 +1,20 @@ package no.nav.klage.dokument.api.controller -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.ninjasquad.springmockk.MockkBean import com.ninjasquad.springmockk.SpykBean -import io.mockk.every import no.nav.klage.dokument.api.mapper.DokumentInputMapper import no.nav.klage.dokument.api.mapper.DokumentMapper -import no.nav.klage.dokument.api.view.DokumentView -import no.nav.klage.dokument.api.view.SmartEditorDocumentView -import no.nav.klage.dokument.api.view.SmartHovedDokumentInput import no.nav.klage.dokument.clients.kabalsmarteditorapi.KabalSmartEditorApiClient -import no.nav.klage.dokument.clients.kabalsmarteditorapi.model.response.DocumentOutput -import no.nav.klage.dokument.domain.dokumenterunderarbeid.OpplastetDokumentUnderArbeidAsHoveddokument -import no.nav.klage.dokument.domain.dokumenterunderarbeid.SmartdokumentUnderArbeidAsHoveddokument import no.nav.klage.dokument.repositories.DokumentUnderArbeidRepository import no.nav.klage.dokument.service.DokumentUnderArbeidService -import no.nav.klage.kodeverk.DokumentType -import no.nav.klage.oppgave.clients.events.KafkaEventClient import no.nav.klage.oppgave.clients.saf.graphql.SafGraphQlClient -import no.nav.klage.oppgave.domain.klage.BehandlingRole import no.nav.klage.oppgave.service.BehandlingService import no.nav.klage.oppgave.service.InnloggetSaksbehandlerService -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test +import no.nav.klage.oppgave.util.ourJacksonObjectMapper import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest -import org.springframework.http.MediaType -import org.springframework.mock.web.MockMultipartFile import org.springframework.test.context.ActiveProfiles import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders -import org.springframework.test.web.servlet.result.MockMvcResultHandlers -import org.springframework.test.web.servlet.result.MockMvcResultMatchers -import java.time.LocalDateTime -import java.util.* @WebMvcTest(DokumentUnderArbeidController::class, SmartEditorController::class) @ActiveProfiles("local") @@ -52,9 +32,6 @@ internal class DokumentUnderArbeidControllerTest { @MockkBean private lateinit var kabalSmartEditorApiClient: KabalSmartEditorApiClient - @MockkBean - private lateinit var kafkaEventClient: KafkaEventClient - @MockkBean private lateinit var dokumentUnderArbeidService: DokumentUnderArbeidService @@ -70,121 +47,124 @@ internal class DokumentUnderArbeidControllerTest { @Autowired private lateinit var mockMvc: MockMvc - private val objectMapper = jacksonObjectMapper().registerModule(JavaTimeModule()) - - @Test - fun createAndUploadHoveddokument() { + private val objectMapper = ourJacksonObjectMapper() + /* + @Test + fun createAndUploadHoveddokument() { + + val behandlingId = UUID.randomUUID() + + every { innloggetSaksbehandlerService.getInnloggetIdent() } returns "IDENT" + every { + dokumentUnderArbeidService.createOpplastetDokumentUnderArbeid( + any(), + any(), + any(), + any(), + any(), + any(), + any(), + ) + } returns OpplastetDokumentUnderArbeidAsHoveddokument( + mellomlagerId = "mellomlagerId", + mellomlagretDate = LocalDateTime.now(), + size = 1001, + name = "vedtak.pdf", + behandlingId = behandlingId, + dokumentType = DokumentType.BREV, + markertFerdig = null, + ferdigstilt = null, + created = LocalDateTime.now(), + modified = LocalDateTime.now(), + id = UUID.randomUUID(), + creatorIdent = "null", + creatorRole = BehandlingRole.KABAL_SAKSBEHANDLING, + datoMottatt = null, + ) - val behandlingId = UUID.randomUUID() + val file = + MockMultipartFile("file", "file-name.pdf", "application/pdf", "whatever".toByteArray()) - every { innloggetSaksbehandlerService.getInnloggetIdent() } returns "IDENT" - every { - dokumentUnderArbeidService.createOpplastetDokumentUnderArbeid( - any(), - any(), - any(), - any(), - any(), - any(), - any(), + val json = mockMvc.perform( + MockMvcRequestBuilders.multipart("/behandlinger/$behandlingId/dokumenter/fil") + .file(file) + .contentType(MediaType.APPLICATION_JSON) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(MockMvcResultMatchers.status().isOk) + .andReturn().response.contentAsString + + val hovedDokumentView = objectMapper.readValue(json, DokumentView::class.java) + assertThat(hovedDokumentView).isNotNull + assertThat(hovedDokumentView.dokumentTypeId).isEqualTo(DokumentType.BREV.id) + } + + + + @Test + fun createSmartEditorHoveddokument() { + val behandlingId = UUID.randomUUID() + val smartEditorDocumentId = UUID.randomUUID() + + val smartHovedDokumentInput = + SmartHovedDokumentInput( + content = jacksonObjectMapper().readTree("{ \"json\": \"is cool\" }"), + tittel = "Tittel", + templateId = "template", + version = null, + parentId = null, + ) + + every { dokumentUnderArbeidService.getSmartEditorId(any(), any()) } returns smartEditorDocumentId + every { kabalSmartEditorApiClient.getDocument(smartEditorDocumentId) } returns DocumentOutput( + id = smartEditorDocumentId, + json = smartHovedDokumentInput.content.toString(), + created = LocalDateTime.now(), + modified = LocalDateTime.now(), ) - } returns OpplastetDokumentUnderArbeidAsHoveddokument( - mellomlagerId = "mellomlagerId", - mellomlagretDate = LocalDateTime.now(), - size = 1001, - name = "vedtak.pdf", - behandlingId = behandlingId, - dokumentType = DokumentType.BREV, - markertFerdig = null, - ferdigstilt = null, - created = LocalDateTime.now(), - modified = LocalDateTime.now(), - id = UUID.randomUUID(), - creatorIdent = "null", - creatorRole = BehandlingRole.KABAL_SAKSBEHANDLING, - datoMottatt = null, - ) - - val file = - MockMultipartFile("file", "file-name.pdf", "application/pdf", "whatever".toByteArray()) - - val json = mockMvc.perform( - MockMvcRequestBuilders.multipart("/behandlinger/$behandlingId/dokumenter/fil") - .file(file) - .contentType(MediaType.APPLICATION_JSON) - ) - .andDo(MockMvcResultHandlers.print()) - .andExpect(MockMvcResultMatchers.status().isOk) - .andReturn().response.contentAsString - - val hovedDokumentView = objectMapper.readValue(json, DokumentView::class.java) - assertThat(hovedDokumentView).isNotNull - assertThat(hovedDokumentView.dokumentTypeId).isEqualTo(DokumentType.BREV.id) - } - - @Test - fun createSmartEditorHoveddokument() { - val behandlingId = UUID.randomUUID() - val smartEditorDocumentId = UUID.randomUUID() - - val smartHovedDokumentInput = - SmartHovedDokumentInput( - content = jacksonObjectMapper().readTree("{ \"json\": \"is cool\" }"), - tittel = "Tittel", - templateId = "template", - version = null, - parentId = null, + every { innloggetSaksbehandlerService.getInnloggetIdent() } returns "IDENT" + + every { + dokumentUnderArbeidService.opprettSmartdokument( + any(), + any(), + any(), + any(), + any(), + any(), + any(), + ) + } returns SmartdokumentUnderArbeidAsHoveddokument( + mellomlagerId = "mellomlagerId", + mellomlagretDate = LocalDateTime.now(), + size = 1001, + name = "vedtak.pdf", + behandlingId = behandlingId, + smartEditorId = UUID.randomUUID(), + smartEditorTemplateId = "template", + dokumentType = DokumentType.BREV, + markertFerdig = null, + ferdigstilt = null, + created = LocalDateTime.now(), + modified = LocalDateTime.now(), + dokumentEnhetId = null, + id = UUID.randomUUID(), + creatorIdent = "null", + creatorRole = BehandlingRole.KABAL_SAKSBEHANDLING, ) - every { dokumentUnderArbeidService.getSmartEditorId(any(), any()) } returns smartEditorDocumentId - every { kabalSmartEditorApiClient.getDocument(smartEditorDocumentId) } returns DocumentOutput( - id = smartEditorDocumentId, - json = smartHovedDokumentInput.content.toString(), - created = LocalDateTime.now(), - modified = LocalDateTime.now(), - ) - every { innloggetSaksbehandlerService.getInnloggetIdent() } returns "IDENT" - - every { - dokumentUnderArbeidService.opprettSmartdokument( - any(), - any(), - any(), - any(), - any(), - any(), - any(), + val json = mockMvc.perform( + MockMvcRequestBuilders.post("/behandlinger/$behandlingId/smartdokumenter") + .content(objectMapper.writeValueAsString(smartHovedDokumentInput)) + .contentType(MediaType.APPLICATION_JSON) ) - } returns SmartdokumentUnderArbeidAsHoveddokument( - mellomlagerId = "mellomlagerId", - mellomlagretDate = LocalDateTime.now(), - size = 1001, - name = "vedtak.pdf", - behandlingId = behandlingId, - smartEditorId = UUID.randomUUID(), - smartEditorTemplateId = "template", - dokumentType = DokumentType.BREV, - markertFerdig = null, - ferdigstilt = null, - created = LocalDateTime.now(), - modified = LocalDateTime.now(), - dokumentEnhetId = null, - id = UUID.randomUUID(), - creatorIdent = "null", - creatorRole = BehandlingRole.KABAL_SAKSBEHANDLING, - ) - - val json = mockMvc.perform( - MockMvcRequestBuilders.post("/behandlinger/$behandlingId/smartdokumenter") - .content(objectMapper.writeValueAsString(smartHovedDokumentInput)) - .contentType(MediaType.APPLICATION_JSON) - ) - .andDo(MockMvcResultHandlers.print()) - .andExpect(MockMvcResultMatchers.status().isOk) - .andReturn().response.contentAsString - - val hovedDokumentView = objectMapper.readValue(json, SmartEditorDocumentView::class.java) - assertThat(hovedDokumentView).isNotNull - assertThat(hovedDokumentView.dokumentTypeId).isEqualTo(DokumentType.BREV.id) - } + .andDo(MockMvcResultHandlers.print()) + .andExpect(MockMvcResultMatchers.status().isOk) + .andReturn().response.contentAsString + + val hovedDokumentView = objectMapper.readValue(json, SmartEditorDocumentView::class.java) + assertThat(hovedDokumentView).isNotNull + assertThat(hovedDokumentView.dokumentTypeId).isEqualTo(DokumentType.BREV.id) + } + */ } \ No newline at end of file diff --git a/src/test/kotlin/no/nav/klage/oppgave/LayeredArchitectureTest.kt b/src/test/kotlin/no/nav/klage/oppgave/LayeredArchitectureTest.kt index 340d4a9da..2f33df091 100644 --- a/src/test/kotlin/no/nav/klage/oppgave/LayeredArchitectureTest.kt +++ b/src/test/kotlin/no/nav/klage/oppgave/LayeredArchitectureTest.kt @@ -34,7 +34,7 @@ class LayeredArchitectureTest { @ArchTest val layer_dependencies_are_respected_for_view: ArchRule = kabalApiLayeredArchitecture() - .whereLayer("View").mayOnlyBeAccessedByLayers("Controllers", "Services", "Config", "ApiMappers", "Exceptions", "Util") + .whereLayer("View").mayOnlyBeAccessedByLayers("Controllers", "Services", "Config", "ApiMappers", "Exceptions", "Util", "Domain") @ArchTest val layer_dependencies_are_respected_for_services: ArchRule = kabalApiLayeredArchitecture() diff --git a/src/test/kotlin/no/nav/klage/oppgave/domain/kafka/BehandlingEventsTilFoersteinstansTest.kt b/src/test/kotlin/no/nav/klage/oppgave/domain/kafka/BehandlingEventsTilFoersteinstansTest.kt index 55476747f..43fe21266 100644 --- a/src/test/kotlin/no/nav/klage/oppgave/domain/kafka/BehandlingEventsTilFoersteinstansTest.kt +++ b/src/test/kotlin/no/nav/klage/oppgave/domain/kafka/BehandlingEventsTilFoersteinstansTest.kt @@ -1,10 +1,9 @@ package no.nav.klage.oppgave.domain.kafka -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.kjetland.jackson.jsonSchema.JsonSchemaConfig import com.kjetland.jackson.jsonSchema.JsonSchemaDraft import com.kjetland.jackson.jsonSchema.JsonSchemaGenerator +import no.nav.klage.oppgave.util.ourJacksonObjectMapper import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import java.time.LocalDateTime @@ -15,7 +14,7 @@ internal class BehandlingEventsTilFoersteinstansTest { @Test @Disabled fun createJsonSchema() { - val objectMapper = jacksonObjectMapper().registerModule(JavaTimeModule()) + val objectMapper = ourJacksonObjectMapper() val config = JsonSchemaConfig.vanillaJsonSchemaDraft4().withJsonSchemaDraft(JsonSchemaDraft.DRAFT_07) diff --git a/src/test/kotlin/no/nav/klage/oppgave/domain/kafka/StatistikkTilDVHTest.kt b/src/test/kotlin/no/nav/klage/oppgave/domain/kafka/StatistikkTilDVHTest.kt index 6815e9ba5..d6832cb56 100644 --- a/src/test/kotlin/no/nav/klage/oppgave/domain/kafka/StatistikkTilDVHTest.kt +++ b/src/test/kotlin/no/nav/klage/oppgave/domain/kafka/StatistikkTilDVHTest.kt @@ -1,10 +1,9 @@ package no.nav.klage.oppgave.domain.kafka -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.kjetland.jackson.jsonSchema.JsonSchemaConfig import com.kjetland.jackson.jsonSchema.JsonSchemaDraft import com.kjetland.jackson.jsonSchema.JsonSchemaGenerator +import no.nav.klage.oppgave.util.ourJacksonObjectMapper import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import java.time.LocalDate @@ -16,7 +15,7 @@ internal class StatistikkTilDVHTest { @Test @Disabled fun createJsonSchema() { - val objectMapper = jacksonObjectMapper().registerModule(JavaTimeModule()) + val objectMapper = ourJacksonObjectMapper() val config = JsonSchemaConfig.vanillaJsonSchemaDraft4().withJsonSchemaDraft(JsonSchemaDraft.DRAFT_07) diff --git a/src/test/kotlin/no/nav/klage/oppgave/service/BehandlingServiceTest.kt b/src/test/kotlin/no/nav/klage/oppgave/service/BehandlingServiceTest.kt index 01c6efdf0..9f3c5da65 100644 --- a/src/test/kotlin/no/nav/klage/oppgave/service/BehandlingServiceTest.kt +++ b/src/test/kotlin/no/nav/klage/oppgave/service/BehandlingServiceTest.kt @@ -140,6 +140,8 @@ class BehandlingServiceTest { behandlingMapper = behandlingMapper, historyService = mockk(), systembrukerIdent = "SYSTEMBRUKER", + kafkaInternalEventService = mockk(), + partSearchService = mockk(), ) every { tilgangService.verifyInnloggetSaksbehandlersSkrivetilgang(behandling) } returns Unit every { innloggetSaksbehandlerService.getInnloggetIdent() } returns SAKSBEHANDLER_IDENT diff --git a/src/test/kotlin/no/nav/klage/oppgave/service/DokumentUnderArbeidServiceTest.kt b/src/test/kotlin/no/nav/klage/oppgave/service/DokumentUnderArbeidServiceTest.kt index 27cf89347..95b168316 100644 --- a/src/test/kotlin/no/nav/klage/oppgave/service/DokumentUnderArbeidServiceTest.kt +++ b/src/test/kotlin/no/nav/klage/oppgave/service/DokumentUnderArbeidServiceTest.kt @@ -1,6 +1,7 @@ package no.nav.klage.oppgave.service import com.ninjasquad.springmockk.MockkBean +import io.mockk.mockk import no.nav.klage.dokument.api.mapper.DokumentMapper import no.nav.klage.dokument.clients.kabaljsontopdf.KabalJsonToPdfClient import no.nav.klage.dokument.clients.kabalsmarteditorapi.DefaultKabalSmartEditorApiGateway @@ -12,7 +13,6 @@ import no.nav.klage.oppgave.clients.ereg.EregClient import no.nav.klage.oppgave.clients.kabaldocument.KabalDocumentGateway import no.nav.klage.oppgave.clients.kabaldocument.KabalDocumentMapper import no.nav.klage.oppgave.clients.saf.SafFacade -import no.nav.klage.oppgave.clients.saf.graphql.SafGraphQlClient import no.nav.klage.oppgave.db.TestPostgresqlContainer import no.nav.klage.oppgave.domain.klage.BehandlingRole.KABAL_SAKSBEHANDLING import org.junit.jupiter.api.BeforeEach @@ -111,6 +111,9 @@ class DokumentUnderArbeidServiceTest { safFacade = safFacade, dokumentMapper = dokumentMapper, systembrukerIdent = "SYSTEMBRUKER", + kafkaInternalEventService = mockk(), + saksbehandlerService = mockk(), + kabalSmartEditorApiClient = mockk(), ) val behandlingId = UUID.randomUUID() diff --git a/src/test/kotlin/no/nav/klage/oppgave/service/distribusjon/BehandlingAvslutningServiceTest.kt b/src/test/kotlin/no/nav/klage/oppgave/service/distribusjon/BehandlingAvslutningServiceTest.kt index 0079dee60..2396aec83 100644 --- a/src/test/kotlin/no/nav/klage/oppgave/service/distribusjon/BehandlingAvslutningServiceTest.kt +++ b/src/test/kotlin/no/nav/klage/oppgave/service/distribusjon/BehandlingAvslutningServiceTest.kt @@ -106,6 +106,12 @@ internal class BehandlingAvslutningServiceTest { @MockkBean(relaxed = true) lateinit var historyService: HistoryService + + @MockkBean(relaxed = true) + lateinit var kafkaInternalEventService: KafkaInternalEventService + + @MockkBean(relaxed = true) + lateinit var partSearchService: PartSearchService } @Autowired From a1b174af8ea559d8342822d88331b7995b318c54 Mon Sep 17 00:00:00 2001 From: Andreas Jonsson Date: Tue, 16 Jan 2024 11:52:15 +0100 Subject: [PATCH 2/4] Removed unused test. --- .../DokumentUnderArbeidControllerTest.kt | 170 ------------------ 1 file changed, 170 deletions(-) delete mode 100644 src/test/kotlin/no/nav/klage/dokument/api/controller/DokumentUnderArbeidControllerTest.kt diff --git a/src/test/kotlin/no/nav/klage/dokument/api/controller/DokumentUnderArbeidControllerTest.kt b/src/test/kotlin/no/nav/klage/dokument/api/controller/DokumentUnderArbeidControllerTest.kt deleted file mode 100644 index f265b3426..000000000 --- a/src/test/kotlin/no/nav/klage/dokument/api/controller/DokumentUnderArbeidControllerTest.kt +++ /dev/null @@ -1,170 +0,0 @@ -package no.nav.klage.dokument.api.controller - -import com.ninjasquad.springmockk.MockkBean -import com.ninjasquad.springmockk.SpykBean -import no.nav.klage.dokument.api.mapper.DokumentInputMapper -import no.nav.klage.dokument.api.mapper.DokumentMapper -import no.nav.klage.dokument.clients.kabalsmarteditorapi.KabalSmartEditorApiClient -import no.nav.klage.dokument.repositories.DokumentUnderArbeidRepository -import no.nav.klage.dokument.service.DokumentUnderArbeidService -import no.nav.klage.oppgave.clients.saf.graphql.SafGraphQlClient -import no.nav.klage.oppgave.service.BehandlingService -import no.nav.klage.oppgave.service.InnloggetSaksbehandlerService -import no.nav.klage.oppgave.util.ourJacksonObjectMapper -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest -import org.springframework.test.context.ActiveProfiles -import org.springframework.test.web.servlet.MockMvc - -@WebMvcTest(DokumentUnderArbeidController::class, SmartEditorController::class) -@ActiveProfiles("local") -internal class DokumentUnderArbeidControllerTest { - - @MockkBean - private lateinit var innloggetSaksbehandlerService: InnloggetSaksbehandlerService - - @MockkBean - private lateinit var dokumentUnderArbeidRepository: DokumentUnderArbeidRepository - - @MockkBean - private lateinit var behandlingService: BehandlingService - - @MockkBean - private lateinit var kabalSmartEditorApiClient: KabalSmartEditorApiClient - - @MockkBean - private lateinit var dokumentUnderArbeidService: DokumentUnderArbeidService - - @MockkBean - private lateinit var safClient: SafGraphQlClient - - @SpykBean - private lateinit var dokumentMapper: DokumentMapper - - @SpykBean - private lateinit var dokumentInputMapper: DokumentInputMapper - - @Autowired - private lateinit var mockMvc: MockMvc - - private val objectMapper = ourJacksonObjectMapper() - /* - @Test - fun createAndUploadHoveddokument() { - - val behandlingId = UUID.randomUUID() - - every { innloggetSaksbehandlerService.getInnloggetIdent() } returns "IDENT" - every { - dokumentUnderArbeidService.createOpplastetDokumentUnderArbeid( - any(), - any(), - any(), - any(), - any(), - any(), - any(), - ) - } returns OpplastetDokumentUnderArbeidAsHoveddokument( - mellomlagerId = "mellomlagerId", - mellomlagretDate = LocalDateTime.now(), - size = 1001, - name = "vedtak.pdf", - behandlingId = behandlingId, - dokumentType = DokumentType.BREV, - markertFerdig = null, - ferdigstilt = null, - created = LocalDateTime.now(), - modified = LocalDateTime.now(), - id = UUID.randomUUID(), - creatorIdent = "null", - creatorRole = BehandlingRole.KABAL_SAKSBEHANDLING, - datoMottatt = null, - ) - - val file = - MockMultipartFile("file", "file-name.pdf", "application/pdf", "whatever".toByteArray()) - - val json = mockMvc.perform( - MockMvcRequestBuilders.multipart("/behandlinger/$behandlingId/dokumenter/fil") - .file(file) - .contentType(MediaType.APPLICATION_JSON) - ) - .andDo(MockMvcResultHandlers.print()) - .andExpect(MockMvcResultMatchers.status().isOk) - .andReturn().response.contentAsString - - val hovedDokumentView = objectMapper.readValue(json, DokumentView::class.java) - assertThat(hovedDokumentView).isNotNull - assertThat(hovedDokumentView.dokumentTypeId).isEqualTo(DokumentType.BREV.id) - } - - - - @Test - fun createSmartEditorHoveddokument() { - val behandlingId = UUID.randomUUID() - val smartEditorDocumentId = UUID.randomUUID() - - val smartHovedDokumentInput = - SmartHovedDokumentInput( - content = jacksonObjectMapper().readTree("{ \"json\": \"is cool\" }"), - tittel = "Tittel", - templateId = "template", - version = null, - parentId = null, - ) - - every { dokumentUnderArbeidService.getSmartEditorId(any(), any()) } returns smartEditorDocumentId - every { kabalSmartEditorApiClient.getDocument(smartEditorDocumentId) } returns DocumentOutput( - id = smartEditorDocumentId, - json = smartHovedDokumentInput.content.toString(), - created = LocalDateTime.now(), - modified = LocalDateTime.now(), - ) - every { innloggetSaksbehandlerService.getInnloggetIdent() } returns "IDENT" - - every { - dokumentUnderArbeidService.opprettSmartdokument( - any(), - any(), - any(), - any(), - any(), - any(), - any(), - ) - } returns SmartdokumentUnderArbeidAsHoveddokument( - mellomlagerId = "mellomlagerId", - mellomlagretDate = LocalDateTime.now(), - size = 1001, - name = "vedtak.pdf", - behandlingId = behandlingId, - smartEditorId = UUID.randomUUID(), - smartEditorTemplateId = "template", - dokumentType = DokumentType.BREV, - markertFerdig = null, - ferdigstilt = null, - created = LocalDateTime.now(), - modified = LocalDateTime.now(), - dokumentEnhetId = null, - id = UUID.randomUUID(), - creatorIdent = "null", - creatorRole = BehandlingRole.KABAL_SAKSBEHANDLING, - ) - - val json = mockMvc.perform( - MockMvcRequestBuilders.post("/behandlinger/$behandlingId/smartdokumenter") - .content(objectMapper.writeValueAsString(smartHovedDokumentInput)) - .contentType(MediaType.APPLICATION_JSON) - ) - .andDo(MockMvcResultHandlers.print()) - .andExpect(MockMvcResultMatchers.status().isOk) - .andReturn().response.contentAsString - - val hovedDokumentView = objectMapper.readValue(json, SmartEditorDocumentView::class.java) - assertThat(hovedDokumentView).isNotNull - assertThat(hovedDokumentView.dokumentTypeId).isEqualTo(DokumentType.BREV.id) - } - */ -} \ No newline at end of file From 73c1ff062bc9c928b60488a6cecd1ce0bb52ae21 Mon Sep 17 00:00:00 2001 From: Andreas Jonsson Date: Tue, 16 Jan 2024 13:46:58 +0100 Subject: [PATCH 3/4] Added events for "avsluttet av saksbehandler" and "feilregistrert". `name` to `navn` for now. --- .../service/DokumentUnderArbeidService.kt | 18 ++--- .../service/FerdigstillDokumentService.kt | 2 +- .../domain/kafka/InternalBehandlingEvent.kt | 18 ++++- .../oppgave/service/BehandlingService.kt | 65 +++++++++++++++---- .../klage/oppgave/service/DokumentService.kt | 2 +- .../klage/oppgave/service/MeldingService.kt | 2 +- 6 files changed, 80 insertions(+), 27 deletions(-) diff --git a/src/main/kotlin/no/nav/klage/dokument/service/DokumentUnderArbeidService.kt b/src/main/kotlin/no/nav/klage/dokument/service/DokumentUnderArbeidService.kt index f834b62a1..495175f6c 100644 --- a/src/main/kotlin/no/nav/klage/dokument/service/DokumentUnderArbeidService.kt +++ b/src/main/kotlin/no/nav/klage/dokument/service/DokumentUnderArbeidService.kt @@ -163,7 +163,7 @@ class DokumentUnderArbeidService( DocumentsAddedEvent( actor = BaseEvent.Actor( navIdent = innloggetIdent, - name = saksbehandlerService.getNameForIdent(innloggetIdent), + navn = saksbehandlerService.getNameForIdent(innloggetIdent), ), timestamp = LocalDateTime.now(), documents = listOf( @@ -218,7 +218,7 @@ class DokumentUnderArbeidService( DocumentsChangedEvent( actor = BaseEvent.Actor( navIdent = innloggetIdent, - name = saksbehandlerService.getNameForIdent(innloggetIdent), + navn = saksbehandlerService.getNameForIdent(innloggetIdent), ), timestamp = LocalDateTime.now(), documents = dokumentUnderArbeidList.map { @@ -334,7 +334,7 @@ class DokumentUnderArbeidService( DocumentsAddedEvent( actor = BaseEvent.Actor( navIdent = innloggetIdent, - name = saksbehandlerService.getNameForIdent(innloggetIdent), + navn = saksbehandlerService.getNameForIdent(innloggetIdent), ), timestamp = LocalDateTime.now(), documents = listOf( @@ -388,7 +388,7 @@ class DokumentUnderArbeidService( DocumentsAddedEvent( actor = BaseEvent.Actor( navIdent = innloggetIdent, - name = saksbehandlerService.getNameForIdent(innloggetIdent), + navn = saksbehandlerService.getNameForIdent(innloggetIdent), ), timestamp = LocalDateTime.now(), documents = addedJournalfoerteDokumenter, @@ -592,7 +592,7 @@ class DokumentUnderArbeidService( DocumentsChangedEvent( actor = BaseEvent.Actor( navIdent = innloggetIdent, - name = saksbehandlerService.getNameForIdent(innloggetIdent), + navn = saksbehandlerService.getNameForIdent(innloggetIdent), ), timestamp = LocalDateTime.now(), documents = listOf( @@ -656,7 +656,7 @@ class DokumentUnderArbeidService( DocumentsChangedEvent( actor = BaseEvent.Actor( navIdent = innloggetIdent, - name = saksbehandlerService.getNameForIdent(innloggetIdent), + navn = saksbehandlerService.getNameForIdent(innloggetIdent), ), timestamp = LocalDateTime.now(), documents = listOf( @@ -725,7 +725,7 @@ class DokumentUnderArbeidService( DocumentsChangedEvent( actor = BaseEvent.Actor( navIdent = innloggetIdent, - name = saksbehandlerService.getNameForIdent(innloggetIdent), + navn = saksbehandlerService.getNameForIdent(innloggetIdent), ), timestamp = LocalDateTime.now(), documents = listOf( @@ -940,7 +940,7 @@ class DokumentUnderArbeidService( DocumentsChangedEvent( actor = BaseEvent.Actor( navIdent = innloggetIdent, - name = saksbehandlerService.getNameForIdent(innloggetIdent), + navn = saksbehandlerService.getNameForIdent(innloggetIdent), ), timestamp = LocalDateTime.now(), documents = vedlegg.map { @@ -1143,7 +1143,7 @@ class DokumentUnderArbeidService( DocumentsRemovedEvent( actor = BaseEvent.Actor( navIdent = innloggetIdent, - name = saksbehandlerService.getNameForIdent(innloggetIdent), + navn = saksbehandlerService.getNameForIdent(innloggetIdent), ), timestamp = LocalDateTime.now(), idList = vedlegg.map { it.id.toString() } + document.id.toString(), diff --git a/src/main/kotlin/no/nav/klage/dokument/service/FerdigstillDokumentService.kt b/src/main/kotlin/no/nav/klage/dokument/service/FerdigstillDokumentService.kt index 848435b39..835fc91af 100644 --- a/src/main/kotlin/no/nav/klage/dokument/service/FerdigstillDokumentService.kt +++ b/src/main/kotlin/no/nav/klage/dokument/service/FerdigstillDokumentService.kt @@ -83,7 +83,7 @@ class FerdigstillDokumentService( DocumentFinishedEvent( actor = BaseEvent.Actor( navIdent = updatedDokument.markertFerdigBy!!, - name = saksbehandlerService.getNameForIdent(updatedDokument.markertFerdigBy!!), + navn = saksbehandlerService.getNameForIdent(updatedDokument.markertFerdigBy!!), ), timestamp = updatedDokument.ferdigstilt!!, id = updatedDokument.id.toString(), diff --git a/src/main/kotlin/no/nav/klage/oppgave/domain/kafka/InternalBehandlingEvent.kt b/src/main/kotlin/no/nav/klage/oppgave/domain/kafka/InternalBehandlingEvent.kt index e23a49723..60664ad29 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/domain/kafka/InternalBehandlingEvent.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/domain/kafka/InternalBehandlingEvent.kt @@ -35,6 +35,8 @@ enum class InternalEventType { REGISTRERINGSHJEMLER, MOTTATT_VEDTAKSINSTANS, JOURNALFOERT_DOCUMENT_MODIFIED, + FEILREGISTRERING, + FERDIGSTILT, } abstract class BaseEvent( @@ -43,7 +45,7 @@ abstract class BaseEvent( ) { data class Actor( val navIdent: String, - val name: String, + val navn: String, ) } @@ -160,3 +162,17 @@ data class JournalfoertDocumentModified( val dokumentInfoId: String, val tittel: String, ) : BaseEvent(actor = actor, timestamp = timestamp) + +data class FeilregistreringEvent( + override val actor: Actor, + override val timestamp: LocalDateTime, + val registered: LocalDateTime, + val reason: String, + val fagsystemId: String, +) : BaseEvent(actor = actor, timestamp = timestamp) + +data class BehandlingFerdigstiltEvent( + override val actor: Actor, + override val timestamp: LocalDateTime, + val avsluttetAvSaksbehandlerDate: LocalDateTime, +) : BaseEvent(actor = actor, timestamp = timestamp) diff --git a/src/main/kotlin/no/nav/klage/oppgave/service/BehandlingService.kt b/src/main/kotlin/no/nav/klage/oppgave/service/BehandlingService.kt index 29067ee28..b95c9937f 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/service/BehandlingService.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/service/BehandlingService.kt @@ -122,6 +122,22 @@ class BehandlingService( ): Behandling { val event = behandling.setAvsluttetAvSaksbehandler(innloggetIdent) applicationEventPublisher.publishEvent(event) + + publishInternalEvent( + data = objectMapper.writeValueAsString( + BehandlingFerdigstiltEvent( + actor = BaseEvent.Actor( + navIdent = innloggetIdent, + navn = saksbehandlerService.getNameForIdent(innloggetIdent), + ), + timestamp = behandling.modified, + avsluttetAvSaksbehandlerDate = behandling.avsluttetAvSaksbehandler!!, + ) + ), + behandlingId = behandling.id, + type = InternalEventType.FERDIGSTILT, + ) + return behandling } @@ -594,7 +610,7 @@ class BehandlingService( MedunderskriverEvent( actor = BaseEvent.Actor( navIdent = systembrukerIdent, - name = systembrukerIdent, + navn = systembrukerIdent, ), timestamp = behandling.modified, navIdent = null, @@ -642,7 +658,7 @@ class BehandlingService( RolEvent( actor = BaseEvent.Actor( navIdent = systembrukerIdent, - name = systembrukerIdent, + navn = systembrukerIdent, ), timestamp = behandling.modified, navIdent = null, @@ -774,7 +790,7 @@ class BehandlingService( MottattVedtaksinstansEvent( actor = BaseEvent.Actor( navIdent = utfoerendeSaksbehandlerIdent, - name = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), + navn = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), ), timestamp = behandling.modified, mottattVedtaksinstans = behandling.mottattVedtaksinstans, @@ -865,7 +881,7 @@ class BehandlingService( InnsendingshjemlerEvent( actor = BaseEvent.Actor( navIdent = utfoerendeSaksbehandlerIdent, - name = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), + navn = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), ), timestamp = behandling.modified, hjemmelIdSet = behandling.hjemler.map { it.id }.toSet(), @@ -903,7 +919,10 @@ class BehandlingService( val partView = if (behandling.klager.prosessfullmektig == null) { null } else { - partSearchService.searchPart(identifikator = behandling.klager.prosessfullmektig?.partId?.value!!, skipAccessControl = true) + partSearchService.searchPart( + identifikator = behandling.klager.prosessfullmektig?.partId?.value!!, + skipAccessControl = true + ) } publishInternalEvent( @@ -911,7 +930,7 @@ class BehandlingService( FullmektigEvent( actor = BaseEvent.Actor( navIdent = utfoerendeSaksbehandlerIdent, - name = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), + navn = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), ), timestamp = behandling.modified, part = partView?.let { @@ -955,7 +974,7 @@ class BehandlingService( KlagerEvent( actor = BaseEvent.Actor( navIdent = utfoerendeSaksbehandlerIdent, - name = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), + navn = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), ), timestamp = behandling.modified, part = Part( @@ -997,7 +1016,7 @@ class BehandlingService( MedunderskriverEvent( actor = BaseEvent.Actor( navIdent = utfoerendeSaksbehandlerIdent, - name = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), + navn = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), ), timestamp = medunderskriverWrapped.modified, navIdent = medunderskriverWrapped.navIdent, @@ -1052,7 +1071,7 @@ class BehandlingService( MedunderskriverEvent( actor = BaseEvent.Actor( navIdent = utfoerendeSaksbehandlerIdent, - name = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), + navn = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), ), timestamp = medunderskriverWrapped.modified, navIdent = medunderskriverWrapped.navIdent, @@ -1407,6 +1426,24 @@ class BehandlingService( saksbehandlerident = navIdent, ) applicationEventPublisher.publishEvent(event) + + publishInternalEvent( + data = objectMapper.writeValueAsString( + FeilregistreringEvent( + actor = BaseEvent.Actor( + navIdent = navIdent, + navn = saksbehandlerService.getNameForIdent(navIdent), + ), + timestamp = behandling.modified, + registered = behandling.feilregistrering!!.registered, + reason = behandling.feilregistrering!!.reason, + fagsystemId = behandling.feilregistrering!!.fagsystem.id, + ), + ), + behandlingId = behandling.id, + type = InternalEventType.FEILREGISTRERING, + ) + return behandling } @@ -1450,7 +1487,7 @@ class BehandlingService( UtfallEvent( actor = BaseEvent.Actor( navIdent = utfoerendeSaksbehandlerIdent, - name = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), + navn = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), ), timestamp = behandling.modified, utfallId = behandling.utfall?.id, @@ -1488,7 +1525,7 @@ class BehandlingService( ExtraUtfallEvent( actor = BaseEvent.Actor( navIdent = utfoerendeSaksbehandlerIdent, - name = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), + navn = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), ), timestamp = behandling.modified, utfallIdList = behandling.extraUtfallSet.map { it.id }, @@ -1521,7 +1558,7 @@ class BehandlingService( RegistreringshjemlerEvent( actor = BaseEvent.Actor( navIdent = utfoerendeSaksbehandlerIdent, - name = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), + navn = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), ), timestamp = behandling.modified, hjemmelIdSet = behandling.registreringshjemler.map { it.id }.toSet(), @@ -1566,7 +1603,7 @@ class BehandlingService( RolEvent( actor = BaseEvent.Actor( navIdent = utfoerendeSaksbehandlerIdent, - name = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), + navn = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), ), timestamp = rolView.modified, navIdent = rolView.navIdent, @@ -1632,7 +1669,7 @@ class BehandlingService( RolEvent( actor = BaseEvent.Actor( navIdent = utfoerendeSaksbehandlerIdent, - name = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), + navn = saksbehandlerService.getNameForIdent(utfoerendeSaksbehandlerIdent), ), timestamp = rolView.modified, navIdent = rolView.navIdent, diff --git a/src/main/kotlin/no/nav/klage/oppgave/service/DokumentService.kt b/src/main/kotlin/no/nav/klage/oppgave/service/DokumentService.kt index 197c555b2..93e2a19da 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/service/DokumentService.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/service/DokumentService.kt @@ -363,7 +363,7 @@ class DokumentService( JournalfoertDocumentModified( actor = BaseEvent.Actor( navIdent = innloggetIdent, - name = saksbehandlerService.getNameForIdent(innloggetIdent), + navn = saksbehandlerService.getNameForIdent(innloggetIdent), ), timestamp = LocalDateTime.now(), journalpostId = journalpostId, diff --git a/src/main/kotlin/no/nav/klage/oppgave/service/MeldingService.kt b/src/main/kotlin/no/nav/klage/oppgave/service/MeldingService.kt index 1d9ea6fe1..261420a77 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/service/MeldingService.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/service/MeldingService.kt @@ -139,7 +139,7 @@ class MeldingService( type = InternalEventType.MESSAGE, data = objectMapper.writeValueAsString( MeldingEvent( - actor = BaseEvent.Actor(navIdent = utfoerendeIdent, name = utfoerendeName), + actor = BaseEvent.Actor(navIdent = utfoerendeIdent, navn = utfoerendeName), timestamp = timestamp, id = melding.id.toString(), text = melding.text, From 6969c913de557873c13db44a65a2b1606fd24683 Mon Sep 17 00:00:00 2001 From: Andreas Jonsson Date: Tue, 16 Jan 2024 15:03:37 +0100 Subject: [PATCH 4/4] Event for sattPaaVent. --- .../domain/kafka/InternalBehandlingEvent.kt | 13 ++++++++ .../oppgave/service/BehandlingService.kt | 31 +++++++++++++++++-- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/no/nav/klage/oppgave/domain/kafka/InternalBehandlingEvent.kt b/src/main/kotlin/no/nav/klage/oppgave/domain/kafka/InternalBehandlingEvent.kt index 60664ad29..3c0b11835 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/domain/kafka/InternalBehandlingEvent.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/domain/kafka/InternalBehandlingEvent.kt @@ -37,6 +37,7 @@ enum class InternalEventType { JOURNALFOERT_DOCUMENT_MODIFIED, FEILREGISTRERING, FERDIGSTILT, + SATT_PAA_VENT, } abstract class BaseEvent( @@ -176,3 +177,15 @@ data class BehandlingFerdigstiltEvent( override val timestamp: LocalDateTime, val avsluttetAvSaksbehandlerDate: LocalDateTime, ) : BaseEvent(actor = actor, timestamp = timestamp) + +data class SattPaaVentEvent( + override val actor: Actor, + override val timestamp: LocalDateTime, + val sattPaaVent: SattPaaVent?, +) : BaseEvent(actor = actor, timestamp = timestamp) { + data class SattPaaVent( + val from: LocalDate, + val to: LocalDate, + val reason: String, + ) +} diff --git a/src/main/kotlin/no/nav/klage/oppgave/service/BehandlingService.kt b/src/main/kotlin/no/nav/klage/oppgave/service/BehandlingService.kt index b95c9937f..304f482ff 100644 --- a/src/main/kotlin/no/nav/klage/oppgave/service/BehandlingService.kt +++ b/src/main/kotlin/no/nav/klage/oppgave/service/BehandlingService.kt @@ -25,6 +25,7 @@ import no.nav.klage.oppgave.domain.kafka.KlagerEvent import no.nav.klage.oppgave.domain.kafka.MedunderskriverEvent import no.nav.klage.oppgave.domain.kafka.Part import no.nav.klage.oppgave.domain.kafka.RolEvent +import no.nav.klage.oppgave.domain.kafka.SattPaaVentEvent import no.nav.klage.oppgave.domain.klage.* import no.nav.klage.oppgave.domain.klage.AnkeITrygderettenbehandlingSetters.setKjennelseMottatt import no.nav.klage.oppgave.domain.klage.AnkeITrygderettenbehandlingSetters.setNyAnkebehandlingKA @@ -716,7 +717,7 @@ class BehandlingService( SattPaaVent( from = LocalDate.now(), to = input.to, - reason = input.reason + reason = input.reason, ) } else null @@ -727,9 +728,35 @@ class BehandlingService( val event = behandling.setSattPaaVent( sattPaaVent, - utfoerendeSaksbehandlerIdent + utfoerendeSaksbehandlerIdent, ) applicationEventPublisher.publishEvent(event) + + publishInternalEvent( + data = objectMapper.writeValueAsString( + SattPaaVentEvent( + actor = BaseEvent.Actor( + navIdent = utfoerendeSaksbehandlerIdent, + navn = if (utfoerendeSaksbehandlerIdent == systembrukerIdent) { + utfoerendeSaksbehandlerIdent + } else saksbehandlerService.getNameForIdent( + utfoerendeSaksbehandlerIdent + ), + ), + timestamp = behandling.modified, + sattPaaVent = behandling.sattPaaVent?.let { + SattPaaVentEvent.SattPaaVent( + from = it.from, + to = it.to, + reason = it.reason, + ) + } + ) + ), + behandlingId = behandlingId, + type = InternalEventType.SATT_PAA_VENT, + ) + return behandling.modified }