Skip to content

refactor(server-nestjs): configuration to module definition - #2365

Closed
shikanime wants to merge 2 commits into
mainfrom
pr/shikanime-push-ktsssqsyoktz
Closed

refactor(server-nestjs): configuration to module definition#2365
shikanime wants to merge 2 commits into
mainfrom
pr/shikanime-push-ktsssqsyoktz

Conversation

@shikanime

Copy link
Copy Markdown
Member

Quel est le comportement actuel ?

La configuration des modules serveur-nestjs était dispersée : imports utilitaires non suffixés (crypto.ts, http-error.ts, iterable.ts) et pas de point d'entrée de définition de module dédié pour certains plugins (sonarqube, vault).

Quel est le nouveau comportement ?

  • Ajout des fichiers *.module-definition.ts pour sonarqube et vault (définition centralisée du module).
  • Renommage des utilitaires en .utils.ts (cryptocrypto.utils, http-errorhttp.utils, iterableiterable.utils) pour aligner avec les conventions du repo.
  • Ajout de dotenv.utils.ts (+ spec) et ajustement des imports dans les tests e2e.

Commits :

  • refactor: configuration to nestjs system
  • Refactor config to module definition

Cette PR introduit-elle un breaking change ?

Non (refactor interne, pas de changement d'API publique).

Autres informations

Testé via les specs unitaires colocalisées et les e2e existants.

@github-actions github-actions Bot added the built label Jul 25, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🤖 Hey !

The security scan report for the current pull request is available here.

@shikanime

Copy link
Copy Markdown
Member Author

Review: PR #2365 — refactor(server-nestjs): configuration to module definition

Reviewed branch shikanime/push-ktsssqsyoktz @ df02c683c9 vs main.
Scope: 147 files, +1819/-1128. Migrates the monolithic ConfigurationService to per-module
ConfigurableModuleBuilder config ownership + zod registerAs factories under config/*.config.ts.

Verdict: REQUEST CHANGES

The architecture is correct and idiomatic (ConfigurableModuleBuilder, gated modules via
ConditionalModule.registerWhen(<Module>.forRoot(<factory>.asProvider()), 'USE_X'), Keycloak gated
properly, config token re-exported from the module for a stable import surface). Tests on the branch
are green (lint 0 errors; unit suites pass). Two issues are blocking boot/behavior unless the
deployment environment always injects the vars.


Blocker B1 — ConfigModule.forRoot() drops the .env / .env.docker / .env.integ override chain at boot

main.module.ts:27 calls ConfigModule.forRoot() with no envFilePath, so the documented env
override precedence (.env < .env.docker < .env.integ) is silently lost at boot — even though
config/utils/dotenv.utils.ts (new) was written to compute those paths.

  • apps/server-nestjs/src/utils/dotenv.utils.ts defines getDotenvPaths() / getExistingDotenvPaths()
    but they are referenced only by their own spec — no caller wires them into ConfigModule.forRoot.
  • Every e2e spec in test/*.e2e-spec.ts correctly passes
    ConfigModule.forRoot({ envFilePath: [...], isGlobal: true, load: [baseConfigFactory] }), proving the
    intended mechanism. main.module.ts does not.

Effect: config factories run zod .parse(process.env) at module-init, so missing/empty values raise
immediately — but the .env.docker / .env.integ files that supply them in Docker/integration modes
are never loaded. The override chain the AGENTS.md documents ("DOCKER=true" / "INTEGRATION=true") is
dead at boot.

Fix: in main.module.ts,

ConfigModule.forRoot({ envFilePath: getExistingDotenvPaths(), isGlobal: true })

(or wire getExistingDotenvPaths() through bootstrap). Reuse the existing dotenv.utils — don't leave it as
dead code.

Blocker B2 — base.config makes HTTP_PROXY, HTTPS_PROXY, PROJECTS_ROOT_DIR required with no default, but .env-example does not document them

apps/server-nestjs/src/config/base.config.ts:19,21,22:

PROJECTS_ROOT_DIR: nonEmpty(z.string()),
HTTP_PROXY: nonEmpty(z.string().url()),
HTTPS_PROXY: nonEmpty(z.string().url()),
  • These are new required env vars (the old ConfigurationService only read PROJECTS_ROOT_DIR,
    and read HTTP_PROXY lazily in the service chain, never failed boot on it).
  • The PR's .env-example / .env.docker-example / .env.integ-example diffs add only
    OPENCDS_INTERNAL_URL — they do not add HTTP_PROXY, HTTPS_PROXY, or PROJECTS_ROOT_DIR
    (except PROJECTS_ROOT_DIR which already existed). A fresh deploy following the example .env will
    hit a hard zod Required/invalid_type failure at BaseModule.forRoot init.

Fix: add HTTP_PROXY / HTTPS_PROXY to the example env files (or give safe defaults if a proxy is
optional), and ensure B1's env loading can actually supply them. Note: if the proxy is genuinely
optional, model it as .optional() in base.config.ts rather than nonEmpty(...).


Warnings (non-blocking)

  • W1 — Dead code: csv() in apps/server-nestjs/src/config/config.utils.ts is never used (only
    json2csv exists as a separate util). Either wire it where a comma-list is parsed or remove it.
  • W2 — Config schema coverage gap: flag, nonEmpty, truthySchema in config/utils/config.utils.ts
    have no dedicated unit spec. The argocd.module.spec.ts exercises forRoot + USE_X gating
    (good), but the transform helpers that coerce env strings to booleans/urls are untested — the exact
    area where a regression like B2 hides. Add a config.utils.spec.ts.
  • W3 — Boot-time parse(process.env) throws opaque zod errors (no field context) if a var is missing.
    Consider a thin safeConfigFactory wrapper that logs the failing key, since misconfig now crashes the
    whole app at init rather than the previous lazy undefined.

Nits

  • N1 — apps/server-nestjs/src/main.ts:47 reads BASE_CONFIG before app.listen; fine, but worth a
    comment that this is what forces eager config validation (intentional crash-early behavior).
  • N2 — argocd.module.spec.ts:14 asserts omission under USE_GITLAB=false but the argocd
    module-definition gates on USE_ARGOCD (healthz.module.ts:32). The test label/condition is
    misleading (works only because USE_ARGOCD is unset → falsy); rename the second case.

Security / standards

No hardcoded secrets, no injection, JWT/client-binding unchanged (Keycloak config carries the same
aud/jwksTimeoutMs/jwksCacheTtlMs as before). Clean.

Verification done

  • Read full diff stat + core files: config/base.config.ts, config/config.utils.ts,
    config/keycloak.config.ts, modules/configurable-feature-module.ts, main.module.ts,
    main.ts, infrastructure/config/*, all *.module-definition.ts, deleted configuration.service.ts.
  • Confirmed wiring: every *.module-definition.ts forRoot is consumed (healthz/plugin/main); no
    orphaned config tokens.
  • Confirmed B1/B2 against main baseline (.env-example precedent, old ConfigurationService).
  • Lint + unit suites on the branch: green (per child task t_74ed9c2d).

@shikanime
shikanime force-pushed the pr/shikanime-push-ktsssqsyoktz branch from df02c68 to 9b067ad Compare July 25, 2026 14:56
@github-actions

Copy link
Copy Markdown
Contributor

🤖 Hey !

The security scan report for the current pull request is available here.

@shikanime
shikanime marked this pull request as draft July 25, 2026 15:23
@shikanime
shikanime force-pushed the pr/shikanime-push-ktsssqsyoktz branch from 9b067ad to dd57156 Compare July 27, 2026 09:09
Comment thread apps/server-nestjs/src/modules/argocd/argocd.module.ts Fixed
Comment thread apps/server-nestjs/src/modules/healthz/healthz.module.ts Fixed
Comment thread apps/server-nestjs/src/main.module.ts Fixed
Comment thread apps/server-nestjs/src/modules/argocd/argocd.module.ts Fixed
Comment thread apps/server-nestjs/src/modules/argocd/argocd.module.ts Fixed
Comment thread apps/server-nestjs/src/modules/argocd/argocd.module.ts Fixed
Comment thread apps/server-nestjs/src/modules/argocd/argocd.module.ts Fixed
Comment thread apps/server-nestjs/src/modules/gitlab/gitlab.module.ts Fixed
Comment thread apps/server-nestjs/src/modules/gitlab/gitlab.module.ts Fixed
Comment thread apps/server-nestjs/src/modules/gitlab/gitlab-client.service.ts Fixed
@shikanime
shikanime force-pushed the pr/shikanime-push-ktsssqsyoktz branch from dd57156 to cc10fe6 Compare July 27, 2026 09:17

@shikanime shikanime left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: #2365 — refactor(server-nestjs): configuration to module definition

Verdict: REQUEST CHANGES (CI red + correctness bug)

Findings

[blocker] CI is failing at head dd57156 — check-runs unit-tests / Unit tests, lint / Lint codebase, and build / server-nestjs are all FAILURE. Must be green before merge.

[blocker] apps/server-nestjs/src/modules/healthz/healthz.controller.ts:23-30urlKey uses raw env names ('KEYCLOAK_DOMAIN', 'GITLAB_URL', …), but configService.get(namespace) returns the transformed config (keys domain, url, …) from each factory's .transform(). So config?.[probe.urlKey] is always undefined → every optional probe is silently skipped. Fix: urlKey: 'url' (and 'domain' for keycloak), or expose a uniform probeUrl from each config factory.

[warning] apps/server-nestjs/src/main.module.ts:1,27getDotenvPaths is imported but never used (likely the lint failure). ConfigModule.forRoot() is given no envFilePath; it loads .env by default but the previous .env.integ conditional is gone. Either wire envFilePath: getDotenvPaths() or confirm integ loading is intentionally handled elsewhere.

[warning] main.module.ts:27 ConditionalModule.registerWhen(..., 'USE_KEYCLOAK') — treats an unset env var as false, but every config schema defaults USE_X to 'true' and no .env-example defines USE_*. Verify a default deploy still registers Keycloak/auth; prefer registerWhen(m, env => env.USE_KEYCLOAK !== 'false') to match schema defaults.

Compliance checklist

  • Conventional title / module-config-isolation pattern followed
  • No leaked secrets (env-examples are placeholders)
  • CI green (unit-tests + lint + server-nestjs build failing)
  • Healthz probes actually fire (urlKey mismatch)
  • Default-deploy module registration verified

@shikanime shikanime left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: REQUEST CHANGES

Reviewed the config → module-definition migration (142 files). Architecture is sound: zod-validated registerAs factories per module, token ownership via MODULE_OPTIONS_TOKEN, setExtras re-export, and deletion of the 179-line ConfigurationService monolith is a net win. Two blockers break runtime behaviour in prod/integ deployments and are posted as inline comments below.

BLOCKER 1 — healthz external probes never registered

configService.get('gitlab') returns the transformed object from registerAs (keys { url, internalUrl, enabled, … }), not the raw env var. So config?.['GITLAB_URL'] is always undefined, the gate is always false, and every external-service health check is silently skipped (endpoint reports green). Fix: use the transformed field name (url for gitlab/vault/nexus/registry/argocd/opencds, domain for keycloak).

BLOCKER 2 — .env.integ / .env.docker no longer loaded

ConfigModule.forRoot() with no envFilePath drops the integ/docker override chain that AGENTS.md documents as supported. utils/dotenv.utils.ts (getDotenvPaths()) reimplements the exact old chain but is never wired in — only its own spec references it. Fix: ConfigModule.forRoot({ envFilePath: getDotenvPaths() }).

WARNINGS

  • argocd.module.spec.ts gates ArgoCD on USE_GITLAB/USE_VAULT instead of USE_ARGOCD — the test asserts nothing about ArgoCD's actual gate.
  • keycloak-secret-provider.service.ts:85 dead ?? url.host/?? url.protocol fallback after the if (!domain) early-return above.

NITS

  • config.utils.ts csv() is unused dead code.
  • base.config.ts duplicates ci/isCI from the same raw.CI.

Positives: zod schemas centralise env validation; ConfigurableModuleClass correctly handles .asProvider() via the isConfigProvider branch; moduleRef.get(…, { strict: false }) is the right optional-probe pattern for conditionally-loaded services.

if (!instance) continue
const config = this.configService.get<Record<string, unknown>>(probe.namespace)
if (config?.[probe.urlKey]) checks.push(() => instance.check(probe.namespace))
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BLOCKER: config[probe.urlKey] is always undefined. configService.get('gitlab') returns the transformed object from registerAs — keys are { url, internalUrl, enabled, … }, NOT raw env vars (GITLAB_URL). So config?.['GITLAB_URL'] is always undefined and every external-service health check is silently skipped (endpoint reports green, no error).

Fix: change urlKey to the transformed field name — url for gitlab/vault/nexus/registry/argocd/opencds and domain for keycloak. config?.url is undefined when the env var is empty, so gating still works.

@Module({
imports: [
ConfigModule.forRoot(),
BaseModule.forRoot(baseConfigFactory.asProvider()),

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BLOCKER: ConfigModule.forRoot() loads only .env/.env.${NODE_ENV}. The old ConfigurationModule also loaded .env.integ (INTEGRATION=true) and .env.docker (DOCKER=true) per the AGENTS.md override chain. That chain is reimplemented in utils/dotenv.utils.ts (getDotenvPaths()) but never wired in — it's only referenced by its own spec, so integ/docker deployments lose their config.

Fix: ConfigModule.forRoot({ envFilePath: getDotenvPaths() }), or at minimum envFilePath: ['.env', '.env.integ', '.env.docker'].

vi.stubEnv('USE_GITLAB', 'false')
const module = await Test.createTestingModule({
imports: [ConfigModule.forRoot(), ConditionalModule.registerWhen(ArgoCDModule.forRoot(argocdConfigFactory.asProvider()), 'USE_GITLAB')],
}).compile()

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: this asserts ArgoCD is gated on USE_GITLAB, not USE_ARGOCD. ArgoCD is imported via 'USE_ARGOCD' everywhere else (healthz.module, main wiring). Disabling an unrelated flag makes the test pass regardless of ArgoCD gating; it would stay green even if ArgoCD loaded unconditionally.

Fix: use 'USE_ARGOCD' (both occurrences) so the test actually exercises ArgoCD's gate.

url.host = this.config.keycloakDomain ?? url.host
url.protocol = this.keycloakConfig.protocol ?? url.protocol
url.host = this.keycloakConfig.domain ?? url.host
this.logger.log(`Replacing JWKS URI domain: ${jwksUri} -> ${url.toString()}`)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: dead fallback. domain is already truthy here — replaceJwksUriDomainWithInternalDomain early-returns at the if (!this.keycloakConfig.domain) guard above. protocol defaults to 'https' and is always defined. The ?? url.host / ?? url.protocol branches are unreachable.

Fix: url.host = this.keycloakConfig.domain (drop the ??).

// Comma-separated string -> array of schema-validated, trimmed non-empty parts.
// Empty/missing -> []. Strictly takes a z string schema (applied per element).
export function csv<T extends z.ZodType<string, z.ZodTypeDef, unknown>>(schema: T) {
return z

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: csv() is never imported anywhere (only flag/nonEmpty/truthySchema are used). New dead code — remove (YAGNI).

isProd: raw.NODE_ENV === 'production',
integration: raw.INTEGRATION,
ci: raw.CI,
devSetup: raw.DEV_SETUP,

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: redundant field — both ci: raw.CI (here) and isCI: raw.CI (line 27) are mapped; the BaseConfig interface declares both. Harmless, but pick one name.

@shikanime
shikanime force-pushed the pr/shikanime-push-ktsssqsyoktz branch from cc10fe6 to 63440ac Compare July 27, 2026 13:17
@github-actions

Copy link
Copy Markdown
Contributor

🤖 Hey !

The security scan report for the current pull request is available here.

Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>
Change-Id: I6f501bd1633ceb14edaa9bf2a324ea026a6a6964

@shikanime shikanime left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: PR #2365 — configuration to module definition

Verdict: REQUEST CHANGES (posting as a comment-review because this is a self-authored PR and GitHub forbids the REQUEST_CHANGES review event on your own PR — the verdict still stands).

PR is otherwise well-structured and release-pipeline config needs no changes (release-please always-bump-minor, hotfix→patch; server-nestjs present in ci matrices; Helm bump job intact). Do not release until the two blockers below are resolved — both are release-breaking (silent health reports, dead env override chain).

Blockers (must fix before merge)

B1 — healthz external probes silently never register (false-healthy image). healthz.controller.ts gates each optional probe on config?.[probe.urlKey], but probe.urlKey is a raw env-var name (KEYCLOAK_DOMAIN, GITLAB_URL, …) while ConfigService.get(namespace) returns the transformed registerAs object, which only exposes url in every namespace. The gate is therefore always falsy → external probes are never registered, so the container's HEALTHCHECK reports healthy while upstreams (Keycloak, Vault, registry, …) are down.

  • Fix: drop urlKey from HealthProbe, gate on config?.url. (Already prepared in worktree wt/t_ca303e1c.)

B2 — .env.docker / .env.integ no longer loaded (documented override chain dead). main.module.ts uses ConfigModule.forRoot() with no envFilePath, leaving the existing getDotenvPaths() helper unwired. The .env < .env.docker < .env.integ chain from AGENTS.md is silently broken.

  • Fix: ConfigModule.forRoot({ isGlobal: true, envFilePath: getDotenvPaths() }). The isGlobal is also required — HealthzController injects ConfigService but HealthzModule never imports ConfigModule, so the token isn't injectable app-wide. (Already prepared in worktree wt/t_ca303e1c.)

Warnings (should fix)

W1 — argocd.module.spec.ts gates on the wrong flags. The two tests stub USE_GITLAB=false / USE_VAULT=false but assert ArgoCD omission, while the module is actually registerWhen(…, 'USE_ARGOCD'). The tests pass for the wrong reason and would pass even if USE_ARGOCD were ignored.

W2 — dead ?? fallback in keycloak-secret-provider.service.ts. url.protocol = this.keycloakConfig.protocol ?? url.protocol and url.host = this.keycloakConfig.domain ?? url.host — both protocol and domain are non-optional in the config schema, so the fallback is unreachable noise.

Notes

  • Nit re-check: csv() is now used (project.controller.ts:30) and isCI is a single field (no duplicate) — both prior nits are already resolved at this head, nothing to flag.
  • Follow-up (non-blocking): healthz.controller.ts:1 imports ConfigService as import type, which erases the DI token from design:paramtypes. The app boots because isGlobal supplies the token, but a value import is more robust. Consider changing to a value import.
  • Precedence detail (moot today): on a rare DOCKER+INTEGRATION combo, getDotenvPaths() returns [.env.integ, .env.docker] so .env.docker wins, while AGENTS.md documents .env.integ as strongest. Align if that combo is ever supported.

A ready-to-commit fix for B1+B2 lives in worktree branch wt/t_ca303e1c (typecheck + lint + dotenv unit tests green). Happy to rebase it onto the current PR head if you'd like the diff applied directly.

| undefined
if (!instance) continue
const config = this.configService.get<Record<string, unknown>>(probe.namespace)
if (config?.[probe.urlKey]) checks.push(() => instance.check(probe.namespace))

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

B1 (blocker): config?.[probe.urlKey] is always falsy — urlKey is a raw env-var name, but ConfigService.get(namespace) returns the transformed registerAs object which only exposes url. External probes never register, so the image reports healthy while upstreams are down. Gate on config?.url instead and drop urlKey from HealthProbe.

interface HealthProbe {
readonly service: new (...args: any[]) => { check: (name: string) => Promise<unknown> }
readonly namespace: string
readonly urlKey: string

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

B1 (blocker): remove readonly urlKey: string from the interface — it carries a raw env-var name that nothing in the transformed config exposes. The namespace's url field is the real signal.


@Module({
imports: [
ConfigModule.forRoot(),

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

B2 (blocker): ConfigModule.forRoot() leaves getDotenvPaths() unwired, so .env.docker / .env.integ are never loaded and the documented override chain is dead. Use ConfigModule.forRoot({ isGlobal: true, envFilePath: getDotenvPaths() }). isGlobal is also required so ConfigService is injectable into HealthzModule (which never imports ConfigModule).

it('omits ArgoCDService when USE_GITLAB=false', async () => {
vi.stubEnv('USE_GITLAB', 'false')
const module = await Test.createTestingModule({
imports: [ConfigModule.forRoot(), ConditionalModule.registerWhen(ArgoCDModule.forRoot(argocdConfigFactory.asProvider()), 'USE_GITLAB')],

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

W1 (warning): this test stubs USE_GITLAB=false but asserts ArgoCD omission, while the module is gated on 'USE_ARGOCD'. Stub USE_ARGOCD instead, or the test passes for the wrong reason.

const url = new URL(jwksUri)
url.protocol = this.config.keycloakProtocol ?? url.protocol
url.host = this.config.keycloakDomain ?? url.host
url.protocol = this.keycloakConfig.protocol ?? url.protocol

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

W2 (warning): dead ?? fallback — protocol and domain are non-optional in the config schema, so url.protocol/url.host never actually fall through. Drop the ?? url.* to avoid unreachable noise.

…attern

Consolidate the server-nestjs configuration into the module-definition
pattern introduced across feature modules. Each plugin now declares its
own config dependencies through a ConfigurableFeatureModule helper and a
dedicated module-definition file, replacing the previous global registerAs
object wiring performed in main.module. This keeps config co-located with
the module that consumes it and removes the shared config lookups that
read transformed env keys at runtime.

Signed-off-by: Shikanime Deva <william.phetsinorath@shikanime.studio>
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>
Change-Id: I1836ab5c8d5eba847539fc158634cfbf6a6a6964
@shikanime
shikanime force-pushed the pr/shikanime-push-ktsssqsyoktz branch from 63440ac to f76cb2c Compare July 27, 2026 13:32
@@ -0,0 +1,74 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import { ArgoCDHealthService } from '../argocd/argocd-health.service'
@@ -0,0 +1,74 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import { ArgoCDHealthService } from '../argocd/argocd-health.service'
import { DatabaseHealthService } from '../infrastructure/database/database-health.service'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { ArgoCDHealthService } from '../argocd/argocd-health.service'
import { DatabaseHealthService } from '../infrastructure/database/database-health.service'
import { KeycloakHealthService } from '../keycloak/keycloak-health.service'
@shikanime shikanime self-assigned this Jul 27, 2026
@shikanime shikanime added this to the 9.25.0 milestone Jul 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🤖 Hey !

The security scan report for the current pull request is available here.

@cloud-pi-native-sonarqube

Copy link
Copy Markdown

Failed Quality Gate failed

  • 0.00% Security Hotspots Reviewed on New Code (is less than 100.00%)

Project ID: cloud-pi-native_console_AYsa46O31ebDtzs-pYyZ

View in SonarQube

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant