Skip to content

Commit

Permalink
implementer by-tiltakskode custom strategy for unleash
Browse files Browse the repository at this point in the history
  • Loading branch information
sondrele committed Oct 31, 2024
1 parent 88596ae commit 0fc7241
Show file tree
Hide file tree
Showing 13 changed files with 118 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const mockFeatures: Features = {
[Toggles.MULIGHETSROMMET_ADMIN_FLATE_ENABLE_DEBUGGER]: true,
[Toggles.MULIGHETSROMMET_ADMIN_FLATE_OPPRETT_TILSAGN]: true,
[Toggles.MULIGHETSROMMET_VEILEDERFLATE_VIS_TILBAKEMELDING]: true,
[Toggles.MULIGHETSROMMET_TILTAKSTYPE_MIGRERING_DELTAKER]: true,
};

export const featureToggleHandlers = [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { QueryKeys } from "./query-keys";
import { useQuery } from "@tanstack/react-query";
import { FeatureToggleService, Toggles } from "@mr/api-client";
import { FeatureToggleService, Tiltakskode, Toggles } from "@mr/api-client";

export type Features = Record<Toggles, boolean>;

/**
* Hook for å bruke en spesifikk feature toggle for å skjule eller vise funksjonalitet
* @param feature Navn på feature-toggle du vil bruke
* @param tiltakskoder Input til "by-tiltakskode"-stategi
* @returns true hvis toggle er skrudd på, eller false hvis ikke
*/
export function useFeatureToggle(feature: Toggles) {
export function useFeatureToggle(feature: Toggles, tiltakskoder: Tiltakskode[] = []) {
return useQuery({
queryKey: QueryKeys.features(feature),
queryFn: () => FeatureToggleService.getFeatureToggle({ feature }),
queryKey: ["feature", feature, tiltakskoder],
queryFn: () => FeatureToggleService.getFeatureToggle({ feature, tiltakskoder }),
throwOnError: false,
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ export const QueryKeys = {
tiltakById: (id: string) => ["tiltaksgjennomforing", id],
previewTiltakById: (id: string) => ["tiltaksgjennomforing", "preview", id],
},
features: (feature: string) => [feature, "feature"],
navEnheter: (statuser: NavEnhetStatus[], typer: NavEnhetType[]) => [
statuser,
typer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ import { useTiltakstyperSomStotterPameldingIModia } from "@/api/queries/useTilta
import { ModiaRoute, resolveModiaRoute } from "@/apps/modia/ModiaRoute";
import { useGetTiltaksgjennomforingIdFraUrl } from "@/hooks/useGetTiltaksgjennomforingIdFraUrl";
import {
GruppetiltakDeltakerStatus,
DeltakelseGruppetiltak,
GruppetiltakDeltakerStatus,
Toggles,
VeilederflateTiltakGruppe,
} from "@mr/api-client";
import { Alert, BodyShort, Button, Heading, VStack } from "@navikt/ds-react";
import { ReactNode } from "react";
import styles from "./PameldingForGruppetiltak.module.scss";
import { useHentDeltakelseForGjennomforing } from "@/api/queries/useHentDeltakelseForGjennomforing";
import { useFeatureToggle } from "@/api/feature-toggles";

interface PameldingProps {
brukerHarRettPaaValgtTiltak: boolean;
Expand All @@ -20,41 +22,43 @@ export function PameldingForGruppetiltak({
brukerHarRettPaaValgtTiltak,
tiltak,
}: PameldingProps): ReactNode {
const { data: aktivDeltakelse } = useHentDeltakelseForGjennomforing();
const { data: brukerDeltarPaaValgtTiltak } = useHentDeltakelseForGjennomforing();
const { data: stotterPameldingIModia = [] } = useTiltakstyperSomStotterPameldingIModia();
const gjennomforingId = useGetTiltaksgjennomforingIdFraUrl();

const skalVisePameldingslenke =
brukerHarRettPaaValgtTiltak &&
tiltak.tiltakstype.tiltakskode &&
stotterPameldingIModia.includes(tiltak.tiltakstype.tiltakskode) &&
!aktivDeltakelse;
const tiltakskoder = tiltak.tiltakstype.tiltakskode ? [tiltak.tiltakstype.tiltakskode] : [];
const { data: deltakelserErMigrert } = useFeatureToggle(
Toggles.MULIGHETSROMMET_TILTAKSTYPE_MIGRERING_DELTAKER,
tiltakskoder,
);

const opprettDeltakelseRoute = resolveModiaRoute({
route: ModiaRoute.ARBEIDSMARKEDSTILTAK_OPPRETT_DELTAKELSE,
gjennomforingId,
});
const brukerKanMeldesPaaValgtTiltak =
brukerHarRettPaaValgtTiltak &&
!brukerDeltarPaaValgtTiltak &&
(deltakelserErMigrert ||
(tiltak.tiltakstype.tiltakskode &&
stotterPameldingIModia.includes(tiltak.tiltakstype.tiltakskode)));

let vedtakRoute = null;
if (aktivDeltakelse) {
vedtakRoute = resolveModiaRoute({
route: ModiaRoute.ARBEIDSMARKEDSTILTAK_DELTAKELSE,
deltakerId: aktivDeltakelse.id,
if (brukerKanMeldesPaaValgtTiltak) {
const opprettDeltakelseRoute = resolveModiaRoute({
route: ModiaRoute.ARBEIDSMARKEDSTILTAK_OPPRETT_DELTAKELSE,
gjennomforingId,
});
}

if (!skalVisePameldingslenke && !aktivDeltakelse) {
return null;
}

if (skalVisePameldingslenke) {
return (
<Button variant={"primary"} onClick={opprettDeltakelseRoute.navigate}>
Start påmelding
</Button>
);
} else if (aktivDeltakelse) {
const tekster = utledTekster(aktivDeltakelse);
}

if (brukerDeltarPaaValgtTiltak) {
const vedtakRoute = resolveModiaRoute({
route: ModiaRoute.ARBEIDSMARKEDSTILTAK_DELTAKELSE,
deltakerId: brukerDeltarPaaValgtTiltak.id,
});

const tekster = utledTekster(brukerDeltarPaaValgtTiltak);
return (
<Alert variant={tekster.variant}>
<Heading level={"2"} size="small">
Expand All @@ -77,6 +81,8 @@ export function PameldingForGruppetiltak({
</Alert>
);
}

return null;
}

interface Tekst {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const mockFeatures: Features = {
[Toggles.MULIGHETSROMMET_ADMIN_FLATE_ENABLE_DEBUGGER]: true,
[Toggles.MULIGHETSROMMET_ADMIN_FLATE_OPPRETT_TILSAGN]: true,
[Toggles.MULIGHETSROMMET_VEILEDERFLATE_VIS_TILBAKEMELDING]: true,
[Toggles.MULIGHETSROMMET_TILTAKSTYPE_MIGRERING_DELTAKER]: true,
};

export const featureToggleHandlers = [
Expand Down
10 changes: 6 additions & 4 deletions mulighetsrommet-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,15 @@ Eksempel:
$ curl localhost:8080/api/v1/innsatsgrupper -H 'Authorization: Bearer <access_token>'
```

For arrangor-flate kreves det token generert fra [mock-oauth2-server sin tokenx side for debugging av tokens](http://localhost:8081/tokenx/debugger). Les mer i readme til arrangor-flate.
For arrangor-flate kreves det token generert
fra [mock-oauth2-server sin tokenx side for debugging av tokens](http://localhost:8081/tokenx/debugger). Les mer i
readme til arrangor-flate.

### Feature toggles

Vi administrerer en del feature toggles via [NAIS og Unleash](https://doc.nais.io/addons/unleash/).
Grensesnitt for å definere toggles finner du her: https://team-mulighetsrommet-unleash-web.nav.cloud.nais.io (logg inn
med @nav-brukeren din).
Grensesnitt for å definere toggles finner du her: https://team-mulighetsrommet-unleash-web.iap.nav.cloud.nais.io
(logg inn med @nav-brukeren din).

Standard oppsett er at Unleash blir [mocket lokalt](../README.md#mocks-via-wiremock), så husk gjerne å oppdatere mocken
med nye feature toggles etter hvert som de legges til.
Expand All @@ -152,7 +154,7 @@ Hvis du heller ønsker å peke lokal applikasjon direkte mot Unleash kan du gjø
1. Konfigurer miljøvariabelen `UNLEASH_SERVER_API_URL` med riktig
URL: https://team-mulighetsrommet-unleash-api.nav.cloud.nais.io
2. Opprett et
personlig [Unleash token](https://team-mulighetsrommet-unleash-web.nav.cloud.nais.io/profile/personal-api-tokens)
personlig [Unleash token](https://team-mulighetsrommet-unleash-web.iap.nav.cloud.nais.io/profile/personal-api-tokens)
og konfigurerer miljøvariabelen `UNLEASH_SERVER_API_TOKEN` med dette tokenet
3. Start applikasjonen

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,6 @@ import no.nav.mulighetsrommet.tokenprovider.CachedTokenProvider
import no.nav.mulighetsrommet.tokenprovider.M2MTokenProvider
import no.nav.mulighetsrommet.tokenprovider.createMaskinportenM2mTokenClient
import no.nav.mulighetsrommet.unleash.UnleashService
import no.nav.mulighetsrommet.unleash.strategies.ByEnhetStrategy
import no.nav.mulighetsrommet.unleash.strategies.ByNavIdentStrategy
import no.nav.mulighetsrommet.utdanning.client.UtdanningClient
import no.nav.mulighetsrommet.utdanning.db.UtdanningRepository
import no.nav.mulighetsrommet.utdanning.task.SynchronizeUtdanninger
Expand Down Expand Up @@ -350,11 +348,7 @@ private fun services(appConfig: AppConfig) = module {
single { NotificationService(get(), get(), get()) }
single { ArrangorService(get(), get()) }
single { RefusjonService(get(), get(), get(), get()) }
single {
val byEnhetStrategy = ByEnhetStrategy(get())
val byNavidentStrategy = ByNavIdentStrategy()
UnleashService(appConfig.unleash, byEnhetStrategy, byNavidentStrategy)
}
single { UnleashService(appConfig.unleash, get()) }
single<AxsysClient> {
AxsysV2ClientImpl(
appConfig.axsys.url,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import io.ktor.server.routing.*
import io.ktor.server.util.*
import no.nav.mulighetsrommet.api.AppConfig
import no.nav.mulighetsrommet.api.plugins.getNavIdent
import no.nav.mulighetsrommet.domain.Tiltakskode
import no.nav.mulighetsrommet.unleash.FeatureToggleContext
import no.nav.mulighetsrommet.unleash.UnleashService
import org.koin.ktor.ext.inject
Expand All @@ -24,12 +25,14 @@ fun Route.featureTogglesRoute(config: AppConfig) {

route("/features") {
get {
val feature: String = call.request.queryParameters.getOrFail("feature")
val feature: String by call.parameters
val tiltakskoder = call.parameters.getAll("tiltakskoder")?.map { Tiltakskode.valueOf(it) } ?: emptyList()

val context = FeatureToggleContext(
userId = getNavIdent().value,
sessionId = call.generateSessionId(),
remoteAddress = call.request.origin.remoteAddress,
tiltakskoder = tiltakskoder,
)

val isEnabled = unleashService.isEnabled(feature, context)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package no.nav.mulighetsrommet.unleash

import no.nav.mulighetsrommet.domain.Tiltakskode

data class FeatureToggleContext(
val userId: String,
val sessionId: String,
val remoteAddress: String,
val tiltakskoder: List<Tiltakskode>,
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ package no.nav.mulighetsrommet.unleash
import io.getunleash.DefaultUnleash
import io.getunleash.Unleash
import io.getunleash.UnleashContext
import io.getunleash.event.UnleashSubscriber
import io.getunleash.util.UnleashConfig
import no.nav.common.client.axsys.AxsysClient
import no.nav.mulighetsrommet.unleash.strategies.ByEnhetStrategy
import no.nav.mulighetsrommet.unleash.strategies.ByNavIdentStrategy
import no.nav.mulighetsrommet.unleash.strategies.ByTiltakskodeStrategy

class UnleashService(config: Config, byEnhetStrategy: ByEnhetStrategy, byNavidentStrategy: ByNavIdentStrategy) {
class UnleashService(config: Config, axsysClient: AxsysClient) {
private val unleash: Unleash

data class Config(
Expand All @@ -26,17 +29,25 @@ class UnleashService(config: Config, byEnhetStrategy: ByEnhetStrategy, byNaviden
.apiKey(config.token)
.environment(config.environment)
.build()
unleash = DefaultUnleash(unleashConfig, byEnhetStrategy, byNavidentStrategy)

unleash = DefaultUnleash(
unleashConfig,
ByEnhetStrategy(axsysClient),
ByNavIdentStrategy(),
ByTiltakskodeStrategy(),
)
}

fun isEnabled(feature: String, context: FeatureToggleContext): Boolean {
val ctx = UnleashContext.builder()
.userId(context.userId)
.sessionId(context.sessionId)
.remoteAddress(context.remoteAddress)
.addProperty(ByTiltakskodeStrategy.TILTAKSKODER_PARAM, context.tiltakskoder.joinToString(",") { it.name })
.build()
return unleash.isEnabled(feature, ctx)
}

fun isEnabled(feature: String): Boolean {
return unleash.isEnabled(feature)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package no.nav.mulighetsrommet.unleash.strategies

import io.getunleash.UnleashContext
import io.getunleash.strategy.Strategy
import no.nav.mulighetsrommet.domain.Tiltakskode

class ByTiltakskodeStrategy : Strategy {
companion object {
const val TILTAKSKODER_PARAM = "tiltakskoder"
}

override fun getName(): String {
return "by-tiltakskode"
}

override fun isEnabled(parameters: MutableMap<String, String>): Boolean {
return false
}

override fun isEnabled(parameters: MutableMap<String, String>, context: UnleashContext): Boolean {
val enabledTiltakskoder = parameters[TILTAKSKODER_PARAM]
.takeIf { !it.isNullOrBlank() }
?.let { parseTiltakskoder(it) }
?: return false

return context.getByName(TILTAKSKODER_PARAM)
.map { parseTiltakskoder(it) }
.map { requestedTiltakskoder ->
enabledTiltakskoder.containsAll(requestedTiltakskoder)
}
.orElse(false)
}
}

private fun parseTiltakskoder(it: String) = it.split(",").map { Tiltakskode.valueOf(it) }
8 changes: 8 additions & 0 deletions mulighetsrommet-api/src/main/resources/web/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1482,6 +1482,13 @@ paths:
required: true
schema:
$ref: "#/components/schemas/Toggles"

- in: query
name: tiltakskoder
schema:
type: array
items:
$ref: "#/components/schemas/Tiltakskode"
get:
tags:
- FeatureToggle
Expand Down Expand Up @@ -4106,6 +4113,7 @@ components:
- mulighetsrommet-veilederflate.vis-tilbakemelding
- mulighetsrommet.admin-flate.enableDebugger
- mulighetsrommet.admin-flate.opprett-tilsagn
- mulighetsrommet.tiltakstype.migrering.deltaker

PublisertRequest:
type: object
Expand Down
23 changes: 10 additions & 13 deletions mulighetsrommet-api/wiremock/mappings/unleash.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,6 @@
"jsonBody": {
"version": 1,
"features": [
{
"name": "mulighetsrommet.admin-flate.opprett-tilsagn",
"enabled": true,
"strategies": []
},
{
"name": "mulighetsrommet.admin-flate.enable-utdanningskategorier",
"enabled": true,
"strategies": []
},
{
"name": "mulighetsrommet-veilederflate.vis-tilbakemelding",
"enabled": true,
Expand All @@ -29,14 +19,21 @@
"strategies": []
},
{
"name": "mulighetsrommet.veilederflate.visDeltakerRegistrering",
"name": "mulighetsrommet.admin-flate.opprett-tilsagn",
"enabled": true,
"strategies": []
},
{
"name": "mulighetsrommet.veilederflate-arena-oppskrifter",
"name": "mulighetsrommet.tiltakstype.migrering.deltaker",
"enabled": true,
"strategies": []
"strategies": [
{
"name": "by-tiltakskode",
"parameters": {
"tiltakskoder": "AVKLARING,OPPFOLGING,GRUPPE_ARBEIDSMARKEDSOPPLAERING,JOBBKLUBB,DIGITALT_OPPFOLGINGSTILTAK,ARBEIDSFORBEREDENDE_TRENING,GRUPPE_FAG_OG_YRKESOPPLAERING,ARBEIDSRETTET_REHABILITERING,VARIG_TILRETTELAGT_ARBEID_SKJERMET"
}
}
]
}
]
},
Expand Down

0 comments on commit 0fc7241

Please sign in to comment.