Skip to content

Dev/min side varsler #160

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions .github/workflows/min-side-varsler.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
name: Min Side Varsler

on:
push:
branches:
- main
- dev/*
paths:
- 'apps/min-side-varsler/**'
- 'lib/**'
- 'domain/**'
- '.github/workflows/min-side-varsler.yaml'
- 'gradle/**'
- 'settings.gradle.kts'
- 'gradle.properties'
- 'gradlew'
- 'gradlew.bat'

env:
MODULE: min-side-varsler
IMAGE: europe-north1-docker.pkg.dev/${{ vars.NAIS_MANAGEMENT_PROJECT_ID }}/paw/paw-arbeidssoekerregisteret-min-side-varsler
jobs:
build:
name: Build
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
id-token: write
packages: write
outputs:
image: ${{ steps.docker-build-push.outputs.image }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v4
with:
java-version: 21
distribution: temurin
cache: gradle
- name: Set version
run: echo "VERSION=$(date +'%y.%m.%d').${{ github.run_number }}-${{ github.run_attempt }}" >> $GITHUB_ENV
- name: Login GAR
uses: nais/login@v0
with:
project_id: ${{ vars.NAIS_MANAGEMENT_PROJECT_ID }}
identity_provider: ${{ secrets.NAIS_WORKLOAD_IDENTITY_PROVIDER }}
team: paw
- name: Build with Gradle
id: docker-build-push
working-directory: ./
run: |
echo "image=${{ env.IMAGE }}:${{ env.VERSION }}" >> $GITHUB_OUTPUT
echo -Pversion=${{ env.VERSION }} -Pimage=${{ env.IMAGE }} :apps:${{ env.MODULE }}:build :apps:${{ env.MODULE }}:jib
./gradlew -Pversion=${{ env.VERSION }} -Pimage=${{ env.IMAGE }} :apps:${{ env.MODULE }}:build :apps:${{ env.MODULE }}:jib
echo "DIGEST=$(cat apps/${{ env.MODULE }}/build/jib-image.digest)" >> $GITHUB_ENV
env:
ORG_GRADLE_PROJECT_githubPassword: ${{ secrets.GITHUB_TOKEN }}
- name: Attest and sign image
uses: nais/[email protected]
with:
image_ref: ${{ env.IMAGE }}@${{ env.DIGEST }}

deploy-dev:
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/dev')
name: Deploy to dev-gcp
needs:
- build
permissions:
contents: read
id-token: write
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Deploy to GCP
uses: nais/deploy/actions/deploy@v2
env:
CLUSTER: dev-gcp
RESOURCE: ./apps/${{ env.MODULE }}/nais/nais-dev.yaml
VAR: image=${{ needs.build.outputs.image }},kafka=nav-dev

# deploy-prod:
# if: github.ref == 'refs/heads/main'
# name: Deploy to prod-gcp
# needs:
# - build
# - deploy-dev
# permissions:
# contents: read
# id-token: write
# runs-on: ubuntu-latest
# steps:
# - name: Checkout
# uses: actions/checkout@v4
# - name: Deploy to GCP
# uses: nais/deploy/actions/deploy@v2
# env:
# TEAM: paw
# CLUSTER: prod-gcp
# RESOURCE: ./apps/${{ env.MODULE }}/nais/nais-prod.yaml
# VAR: image=${{ needs.build.outputs.image }},kafka=nav-prod
52 changes: 52 additions & 0 deletions apps/min-side-varsler/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Min Side Varsler

## Varsler
Team Min Side sin [system-dok](https://navikt.github.io/tms-dokumentasjon)

Varsler opprettes ved å sende en varselmelding på Kafka-topic `min-side.aapen-brukervarsel-v1`.

### Varseltyper
Det finnes tre typer varsler:
* **beskjed** - Enkel informasjon til brukere
* **oppgave** - Brukere må utføre noe
* **innboks** - Varsle om melding i digital innboks

### Varselhendelser
Når tilstand endres for et varsel hos Min Side så opprettes det en varselhendelsemelding på Kafka-topic
`min-side.aapen-varsel-hendelse-v1`.

Det finnes fire typer varselhendelser:
* **opprettet** - TMS har mottat varselmeldingen og lagret den i sin database
* **inaktivert** - Varsel er inaktivert av bruker eller produsent
* **slettet** - Varsel er slettet fra databasen til TMS
* **eksternStatusOppdatert** - Kommer kun om varsel også har blitt send som SMS/Epost, og status for utsending har endret seg

```mermaid
stateDiagram-v2
[*] --> opprettet
opprettet --> inaktivert
opprettet --> eksternStatusOppdatert
eksternStatusOppdatert --> inaktivert
inaktivert --> slettet
slettet --> [*]
```

Ved ekstern varsling via SMS/Epost så inneholder varselhendelsen også en status:
* **venter** - Eksternt varsel er klar for utsending
* **bestilt** - Eksternt varsel er oversendt til Altinn
* **sendt** - Eksternt varsel er bekreftet sendt
* **feilet** - Eksternt varsel feilet ved utsending
* **ferdigstilt** - Eksternt varsel er fullført

```mermaid
stateDiagram-v2
[*] --> venter
venter --> bestilt
bestilt --> sendt
sendt --> ferdigstilt
bestilt --> feilet
feilet --> feilet: renotifikasjon
feilet --> sendt
feilet --> ferdigstilt: inaktivert
ferdigstilt --> [*]
```
66 changes: 66 additions & 0 deletions apps/min-side-varsler/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
plugins {
kotlin("jvm")
id("jib-distroless")
}

val jvmMajorVersion: String by project

dependencies {
// Project
implementation(project(":lib:hoplite-config"))
implementation(project(":lib:logging"))
implementation(project(":lib:serialization"))
implementation(project(":lib:error-handling"))
implementation(project(":lib:metrics"))
implementation(project(":lib:database"))
implementation(project(":lib:kafka-streams"))
implementation(project(":domain:bekreftelse-interne-hendelser"))
implementation(project(":domain:main-avro-schema"))

// Ktor
implementation(libs.bundles.ktorServerWithNettyAndMicrometer)

// Jackson
implementation(libs.jackson.kotlin)
implementation(libs.jackson.datatypeJsr310)

// Logging
implementation(libs.logbackClassic)
implementation(libs.logstashLogbackEncoder)

// Observability
implementation(libs.micrometer.registryPrometheus)
implementation(libs.opentelemetry.api)
implementation(libs.opentelemetry.annotations)

// Database
implementation(libs.exposed.jdbc)
implementation(libs.exposed.javaTime)
implementation(libs.database.hikari.connectionPool)
implementation(libs.database.postgres.driver)
implementation(libs.database.flyway.postgres)

// Kafka
implementation(libs.kafka.clients)
implementation(libs.kafka.streams.core)
implementation(libs.avro.kafkaStreamsSerde)

// Utils
implementation(libs.arrow.core.core)
implementation(libs.hoplite.yaml)

// Nav
implementation(libs.nav.common.log)
implementation(libs.nav.tms.varsel.kotlinBuilder)

// Testing
testImplementation(project(":test:test-data-lib"))
testImplementation(project(":lib:kafka-key-generator-client"))
testImplementation(libs.bundles.testLibsWithUnitTesting)
testImplementation(libs.kafka.streams.test)
testImplementation(libs.database.h2)
}

tasks.withType<Test>().configureEach {
useJUnitPlatform()
}
59 changes: 59 additions & 0 deletions apps/min-side-varsler/nais/nais-dev.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
apiVersion: nais.io/v1alpha1
kind: Application
metadata:
name: paw-arbeidssoekerregisteret-min-side-varsler
namespace: paw
labels:
team: paw
spec:
image: {{ image }}
port: 8080
replicas:
min: 1
max: 1
resources:
limits:
memory: 1024Mi
requests:
cpu: 200m
memory: 256Mi
env:
- name: KAFKA_BEKREFTELSE_STREAM_SUFFIX
value: bekreftelse-beta-v2
- name: KAFKA_VARSEL_HENDELSE_STREAM_SUFFIX
value: varsel-hendelser-beta-v1
- name: KAFKA_PAW_ARBEIDSOKERPERIODE_TOPIC
value: paw.arbeidssokerperioder-v1
- name: KAFKA_PAW_ARBEIDSOKER_BEKREFTELSE_HENDELSESLOGG_TOPIC
value: paw.arbeidssoker-bekreftelse-hendelseslogg-beta-v2
- name: KAFKA_TMS_AAPEN_BRUKERVARSEL_TOPIC
value: min-side.aapen-brukervarsel-v1
- name: KAFKA_TMS_AAPEN_VARSEL_HENDELSE_TOPIC
value: min-side.aapen-varsel-hendelse-v1
- name: VARSEL_LINK
value: https://www.ansatt.dev.nav.no/arbeidssoekerregisteret/bekreftelse
liveness:
path: /internal/isAlive
initialDelay: 10
readiness:
path: /internal/isReady
initialDelay: 10
azure:
application:
enabled: true
prometheus:
enabled: true
path: /internal/metrics
observability:
autoInstrumentation:
enabled: true
runtime: java
kafka:
pool: {{ kafka }}
streams: true
gcp:
sqlInstances:
- type: POSTGRES_17
tier: db-f1-micro
databases:
- name: pawminsidevarsler
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package no.nav.paw.arbeidssoekerregisteret

import io.ktor.server.application.Application
import io.ktor.server.engine.addShutdownHook
import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty
import io.micrometer.core.instrument.binder.kafka.KafkaStreamsMetrics
import no.nav.paw.arbeidssoekerregisteret.context.ApplicationContext
import no.nav.paw.arbeidssoekerregisteret.plugin.configureRouting
import no.nav.paw.config.env.appNameOrDefaultForLocal
import no.nav.paw.database.plugin.installDatabasePlugin
import no.nav.paw.kafka.plugin.installKafkaStreamsPlugins
import no.nav.paw.logging.logger.buildApplicationLogger
import no.nav.paw.metrics.plugin.installMetricsPlugin

fun main() {
val logger = buildApplicationLogger

val applicationContext = ApplicationContext.build()
val appName = applicationContext.serverConfig.runtimeEnvironment.appNameOrDefaultForLocal()

with(applicationContext.serverConfig) {
logger.info("Starter $appName med hostname $host og port $port")

embeddedServer(factory = Netty, port = port) {
module(applicationContext)
}.apply {
addShutdownHook {
logger.info("Avslutter $appName")
stop(gracePeriodMillis, timeoutMillis)
}
start(wait = true)
}
}
}

fun Application.module(applicationContext: ApplicationContext) {
with(applicationContext) {
val additionalMeterBinders = kafkaStreamsList.map { KafkaStreamsMetrics(it) }
installMetricsPlugin(prometheusMeterRegistry, additionalMeterBinders)
installDatabasePlugin(dataSource)
installKafkaStreamsPlugins(kafkaStreamsList, kafkaStreamsShutdownTimeout)
configureRouting(healthIndicatorRepository, prometheusMeterRegistry)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package no.nav.paw.arbeidssoekerregisteret.config

import java.time.Duration

const val KAFKA_TOPICS_CONFIG = "kafka_topology_config.toml"

data class KafkaTopologyConfig(
val shutdownTimeout: Duration = Duration.ofSeconds(5),
val bekreftelseStreamSuffix: String,
val varselHendelseStreamSuffix: String,
val periodeTopic: String,
val bekreftelseHendelseTopic: String,
val tmsVarselTopic: String,
val tmsVarselHendelseTopic: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package no.nav.paw.arbeidssoekerregisteret.config

import no.nav.tms.varsel.action.EksternKanal
import java.net.URI

const val MIN_SIDE_VARSEL_CONFIG = "min_side_varsel_config.yaml"

data class MinSideVarselConfig(
val link: URI,
val prefererteKanaler: List<EksternKanal>,
val standardSpraak: Spraakkode,
val tekster: List<MinSideTekst>
) {
init {
requireNotNull(tekster.find { it.spraak == standardSpraak }) {
"Ingen av tekstene har språk $standardSpraak som er satt som standard språk"
}
require(prefererteKanaler.isNotEmpty()) { "Ingen kanaler konfigurert" }
}
}


data class Spraakkode(
/**
* Språkkode i henhold til ISO-639-1
*/
val kode: String
) {
init {
require(kode.length == 2) { "Språkkode må være to bokstaver (ISO-639-1)" }
}
}

data class MinSideTekst(
val spraak: Spraakkode,
val tekst: String
)
Loading
Loading