Skip to content

feat: RBAC - management app access #1569

Merged
riderx merged 21 commits into
mainfrom
Dalanir/gestion-droits-app
Feb 6, 2026
Merged

feat: RBAC - management app access #1569
riderx merged 21 commits into
mainfrom
Dalanir/gestion-droits-app

Conversation

@Dalanir
Copy link
Copy Markdown
Contributor

@Dalanir Dalanir commented Feb 3, 2026

Summary

This PR introduces reusable modal components to reduce code duplication and improves role display consistency across the application.

New Components Created

  1. SearchInput (src/components/forms/SearchInput.vue)

    • Reusable search input with icon
    • Supports v-model, disabled state, custom placeholder
    • Consistent styling across the app
  2. RoleSelect (src/components/forms/RoleSelect.vue)

    • Dropdown for role selection
    • Displays role descriptions
    • Fully typed with TypeScript
  3. RoleSelectionModal (src/components/modals/RoleSelectionModal.vue)

    • Complete modal for role/permission selection
    • Integrates SearchInput and RoleSelect
    • Handles validation and loading states

Refactoring & Fixes

  • AppAccess.vue: Migrated to use new components, removed ~40 lines of duplicate modal code
  • Members.vue: Migrated search and role selection to new components
  • AppTable.vue: Fixed role display to show human-friendly names (e.g., "app_admin" → "App Admin")

Benefits

  • -97 net lines of code (reduced duplication)
  • Consistent UI across all modals
  • Easier maintenance with reusable components
  • Better UX with proper role translations

Test Plan

  • Verify SearchInput works in AppAccess and Members modals
  • Test RoleSelect in all role assignment flows
  • Confirm RoleSelectionModal opens/closes correctly
  • Check role display in app list shows translated names
  • Verify no TypeScript errors (only 2 pre-existing errors in Members.vue unrelated to changes)

Screenshots

Capture d’écran 2026-02-03 à 19 30 10 Capture d’écran 2026-02-03 à 19 29 54 Capture d’écran 2026-02-03 à 19 29 44

Summary by CodeRabbit

  • New Features

    • App-level role management UI: assign and edit app roles with search, role picker, and an edit-role modal; member app-access management added.
  • Documentation

    • RBAC docs revised to reflect more restricted defaults for basic org members.
  • Chores

    • Removed obsolete "app role" hint across locales.
    • Stronger RBAC enforcement and access checks added in backend/migrations.
  • Tests

    • Better diagnostics for app-creation test failures.

Dalanir and others added 4 commits February 3, 2026 18:37
- Add SearchInput component with icon for consistent search UI
- Add RoleSelect component for role dropdown selection
- Both components support v-model and are fully typed
- Reduces code duplication across modals and forms

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Reusable modal for role/permission selection
- Integrates with RoleSelect component
- Handles validation and loading states
- Supports custom titles and descriptions
- Emits confirm/cancel events for flexible usage

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
AppAccess.vue:
- Replace search input with SearchInput component
- Replace role select with RoleSelect component
- Replace Edit Role Modal with RoleSelectionModal
- Remove duplicate modal code (~40 lines)
- Refactor updateRoleBinding to handleEditRoleConfirm

Members.vue:
- Replace search input in App Access modal with SearchInput
- Replace role select with RoleSelect component
- Remove unused selectedOrgRoleSummary computed property

Benefits:
- Reduced code duplication
- Consistent UI across modals
- Easier maintenance and testing

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Import getRbacRoleI18nKey for proper role translation
- Normalize role names (remove invite_ prefix)
- Use i18n translations when available
- Fallback to human-readable format (replace _ with spaces)
- Fixes display of roles like "app_admin" → "App Admin"

Applies the same display logic used in Members.vue and AppAccess.vue
for consistent user experience across the application.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 3, 2026

📝 Walkthrough

Walkthrough

This PR restricts default org_member RBAC (removing many app/channel/bundle grants), adds role-selection UI components and an app-access management flow, deletes the app-role-hint translation key across locales, broadens backend middleware routing, and introduces RBAC DB migrations/functions and seed updates.

Changes

Cohort / File(s) Summary
RBAC Documentation
RBAC_SYSTEM.md
Removed multiple app/channel/bundle permissions from org_member mappings and updated usage/use-case text to reflect no default app access.
Locale files (15)
messages/de.json, messages/en.json, messages/es.json, messages/fr.json, messages/hi.json, messages/id.json, messages/it.json, messages/ja.json, messages/ko.json, messages/pl.json, messages/pt-br.json, messages/ru.json, messages/tr.json, messages/vi.json, messages/zh-cn.json
Deleted the app-role-hint translation key in all listed locales.
Type Declarations
src/components.d.ts
Added global TS typings for new components: RoleSelect, RoleSelectionModal, and SearchInput.
New UI Components
src/components/forms/RoleSelect.vue, src/components/forms/SearchInput.vue, src/components/modals/RoleSelectionModal.vue
Added reusable RoleSelect, SearchInput (with icon), and RoleSelectionModal (validation, confirm/cancel emits).
App Access & Role Editing UI
src/components/dashboard/AppAccess.vue, src/components/tables/AppTable.vue, src/pages/settings/organization/Members.vue
Added role-edit modal and role-binding edit flow in dashboard; improved role i18n display; implemented app-access modal, app list/search, selection, binding create/update flows and related state/handlers in Members.vue.
Supabase Functions & Routing
supabase/functions/_backend/private/groups.ts, .../role_bindings.ts, .../roles.ts, supabase/functions/private/index.ts
Changed middleware mount paths from '/' to '*' so CORS/auth apply to all routes; registered new /roles private route.
Database Seed
supabase/seed.sql
Set use_new_rbac = true for Demo org and restricted org_member to org-level read permissions only.
DB Migration & RBAC Functions
supabase/migrations/20260203201308_rbac_org_member_no_app_access.sql
Added migration removing app/channel/bundle permissions from org_member; added check_org_user_privileges(), has_app_right_apikey(...), and invite_user_to_org(...) functions and updated RLS usage.
Backend change: app POST
supabase/functions/private/app/post.ts
Switched to explicit pg client usage with INSERT ... RETURNING, added try/catch/finally, and adjusted parameter naming.
Tests
tests/expose-metadata.test.ts
Added diagnostic logging of response body on failed app-creation requests.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant UI as Members.vue / AppAccess UI
    participant Modal as RoleSelectionModal
    participant API as Supabase Functions (HTTP)
    participant DB as PostgreSQL

    User->>UI: Open App-access modal
    UI->>API: GET apps & available roles
    API->>DB: Query apps, roles, role_bindings
    DB-->>API: Return data
    API-->>UI: Send apps, roles, bindings
    UI->>Modal: Show RoleSelect (roles)
    User->>Modal: Select apps + role
    User->>UI: Save
    UI->>API: POST/PUT role_bindings (create/update)
    API->>DB: INSERT/UPDATE role_bindings (RBAC checks)
    DB-->>API: Confirm
    API-->>UI: Success
    UI->>User: Show toast, refresh bindings
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • riderx

Poem

🐰 I hopped through docs and tightened a seam,

Hid app access like a cautious dream,
Built a modal to pick roles with care,
Checked bindings in DB, signed with flair,
Now roles and searches sparkle — carrot-share. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 3
❌ Failed checks (1 warning, 2 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check ❓ Inconclusive PR description does not explicitly reference any linked issues or ticket numbers that would establish traceability to requirements or work items. Consider adding issue references (e.g., 'Closes #XXXX' or 'Related to #XXXX') in the PR description or title to improve traceability.
Out of Scope Changes check ❓ Inconclusive The changeset includes a substantial database migration (rbac_org_member_no_app_access.sql) and permission restructuring alongside component additions, making it unclear whether all changes are cohesively scoped. Verify that the RBAC permission changes, API key validation logic, and org invitation workflows are essential to the component refactoring goal, or consider splitting into separate focused PRs.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'feat: RBAC - management app access' clearly and concisely summarizes the main change: adding RBAC management for app access through new reusable components.
Description check ✅ Passed The PR description comprehensively covers all required template sections: Summary with clear objectives, detailed New Components and Refactoring details, Test Plan with specific verification steps, and Screenshots demonstrating the changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch Dalanir/gestion-droits-app

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 SQLFluff (4.0.0)
supabase/migrations/20260203201308_rbac_org_member_no_app_access.sql

User Error: No dialect was specified. You must configure a dialect or specify one on the command line using --dialect after the command. Available dialects:
ansi, athena, bigquery, clickhouse, databricks, db2, doris, duckdb, exasol, flink, greenplum, hive, impala, mariadb, materialize, mysql, oracle, postgres, redshift, snowflake, soql, sparksql, sqlite, starrocks, teradata, trino, tsql, vertica


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Dalanir and others added 4 commits February 3, 2026 18:40
- Change middleware from '/' to '*' for useCors and middlewareAuth
- Ensures middleware applies to all routes in groups, role_bindings, and roles
- Fixes potential CORS and auth issues on nested routes

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Migration:
- Remove app, bundle, and channel scope permissions from org_member role
- Update role description to reflect org-only access
- Ensures org_member has no direct app access by default

Seed:
- Enable RBAC for test organization
- Remove unused variables (v_org, v_migration_result)

This enforces the principle that org_member should only have
org-level access and requires explicit app-level role assignments.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Remove app-role-hint key from all language files
- Key was not referenced in the codebase
- Cleanup to reduce translation maintenance burden

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Fix import order in AppAccess.vue (SearchInput before RoleSelectionModal)
- Fix static class in SearchInput.vue (separate static class attribute)
- Fix inconsistent quote-props in RoleSelectionModal.vue emit types
- Fix indentation errors in Members.vue template (79 errors total)

All errors auto-fixed with eslint --fix

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@Dalanir Dalanir changed the title feat: create reusable modal components and improve role display feat: RBAC - management app access Feb 3, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@src/components/dashboard/AppAccess.vue`:
- Around line 12-16: The import order violates the perfectionist/sort-imports
rule: move the SearchInput import to come before the RoleSelectionModal import
so imports are alphabetically ordered (e.g., ensure SearchInput is imported
before RoleSelectionModal among the top-level imports in AppAccess.vue where
IconWrench, DataTable, RoleSelect, RoleSelectionModal, and SearchInput are
declared).

In `@src/components/forms/SearchInput.vue`:
- Around line 29-35: The input element in SearchInput.vue mixes static classes
with a dynamic binding causing vue/prefer-separate-static-class; refactor the
element so static classes ("w-full pl-10 d-input") are placed in the plain class
attribute and the dynamic part remains in :class (e.g. bind only props.class or
an array containing props.class) while preserving v-model="localValue", type,
:placeholder="placeholder" and :disabled="disabled".

In `@src/components/modals/RoleSelectionModal.vue`:
- Around line 33-36: The emit keys in the defineEmits call are inconsistent with
quoting: update the remaining unquoted keys so they match the quoted style (wrap
"confirm" and "cancel" in quotes) in the defineEmits declaration (the emit
constant) to satisfy the quote-props lint rule; ensure the type signatures
remain unchanged (confirm: [role: string], cancel: []) and that only the keys'
quoting is modified.

In `@src/pages/settings/organization/Members.vue`:
- Around line 1675-1762: The Teleport app-access modal template has inconsistent
indentation causing vue/html-indent failures; reformat the block starting at the
Teleport element (contains SearchInput, the app list v-for, input checkbox,
getAppAccessLabel usage, and RoleSelect) to the project's standard indentation
(consistent 2-space nesting) so all child elements (divs, labels, inputs, spans,
and the RoleSelect component) are aligned properly; run the project's formatter
or eslint --fix after adjusting to ensure vue/html-indent passes.

Comment thread src/components/dashboard/AppAccess.vue
Comment thread src/components/forms/SearchInput.vue
Comment thread src/components/modals/RoleSelectionModal.vue Outdated
Comment thread src/pages/settings/organization/Members.vue
Remove invalid default string argument from t() calls in Members.vue.
The vue-i18n t() function expects (key, interpolationObject) not
(key, defaultString, interpolationObject).
@Dalanir Dalanir self-assigned this Feb 3, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@supabase/migrations/20260203201308_rbac_org_member_no_app_access.sql`:
- Around line 91-165: The has_app_right_apikey function accepts an unchecked
userid and never verifies it against the api key owner returned by
find_apikey_by_value, allowing privilege escalation; fix by validating
api_key.user_id matches the incoming "userid" (or derive userid from
api_key.user_id) right after fetching api_key in has_app_right_apikey, and on
mismatch log a denial (e.g., USERID_MISMATCH) with pg_log and return false
before any permission checks proceed.
🧹 Nitpick comments (3)
src/pages/settings/organization/Members.vue (3)

48-78: Consider extracting shared RBAC types.

The Role, OrgApp, and RoleBinding interfaces are duplicated between Members.vue and AppAccess.vue. Consider extracting them to a shared types file (e.g., ~/types/rbac.types.ts) to maintain consistency and reduce duplication.


1267-1294: Consider separating concerns in fetchMemberAppBindings.

Lines 1284-1288 mix data fetching with UI state management (auto-populating appAccessSelectedRole). This is already handled by the watcher on appAccessSelectedAppIds (lines 456-476), making this code potentially redundant or causing double-updates.

🔧 Proposed simplification
     appAccessBindings.value = (data || []).filter((binding: RoleBinding) => {
       return binding.scope_type === 'app'
         && binding.principal_type === 'user'
         && binding.principal_id === member.uid
     })
-
-    if (appAccessSelectedAppIds.value.length === 1 && !appAccessSelectedRole.value && !appAccessRoleTouched.value) {
-      const binding = appAccessBindings.value.find(b => b.app_id === appAccessSelectedAppIds.value[0])
-      if (binding)
-        appAccessSelectedRole.value = binding.role_name
-    }
   }

1388-1424: Sequential API calls may be slow for bulk app-role assignments.

When assigning roles to multiple apps, this loop makes sequential API calls which could be slow. Consider parallelizing with Promise.all for better UX, especially for organizations with many apps.

⚡ Proposed parallel execution
     const bindingMap = appAccessBindingByAppId.value
     let createdCount = 0
     let updatedCount = 0

-    for (const appId of appAccessSelectedAppIds.value) {
+    const operations = appAccessSelectedAppIds.value.map(async (appId) => {
       const existingBinding = bindingMap.get(appId)
       if (existingBinding && existingBinding.role_name === appAccessSelectedRole.value) {
-        continue
+        return { type: 'skipped' as const }
       }

       if (existingBinding) {
         const { error: updateError } = await supabase
           .from('role_bindings')
           .update({ role_id: roleData.id })
           .eq('id', existingBinding.id)

         if (updateError)
           throw updateError

-        updatedCount += 1
+        return { type: 'updated' as const }
       }
       else {
         const { error } = await supabase.functions.invoke('private/role_bindings', {
           method: 'POST',
           body: {
             principal_type: 'user',
             principal_id: appAccessMember.value.uid,
             role_name: appAccessSelectedRole.value,
             scope_type: 'app',
             org_id: currentOrganization.value.gid,
             app_id: appId,
             channel_id: null,
           },
         })

         if (error)
           throw error

-        createdCount += 1
+        return { type: 'created' as const }
       }
-    }
+    })
+
+    const results = await Promise.all(operations)
+    updatedCount = results.filter(r => r.type === 'updated').length
+    createdCount = results.filter(r => r.type === 'created').length

After removing app/channel/bundle permissions from org_member role,
the apps table INSERT policy was still checking for app-level admin
permissions. This caused RLS violations when creating apps since the
app_id doesn't exist yet during creation.

Updated the policy to check org-level write permissions instead,
which maps to org.update_settings in RBAC and aligns with the API
endpoint's permission check.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@supabase/migrations/20260203201308_rbac_org_member_no_app_access.sql`:
- Around line 32-89: The RBAC branches in check_org_user_privileges() and
invite_user_to_org() grant super-admin capabilities by inspecting role_bindings
but skip 2FA checks; update both RBAC paths so that after confirming the
role_binding-based super-admin match you also verify 2FA is enabled for the
caller and org enforcement is on (use public.has_2fa_enabled(auth.uid()) and the
org's enforcing_2fa flag or helper) and only set/allow super-admin actions when
both the role_binding check and the 2FA-enforcement check pass; do the same
pattern in invite_user_to_org() where role_bindings are used.

Dalanir and others added 3 commits February 3, 2026 22:10
Previous attempt modified an already-applied migration, which
doesn't get re-run in CI. This creates a new migration with a
current timestamp to properly fix the RLS policy for app creation.

Changes:
- Remove RLS policy fix from old migration (already applied)
- Create new migration 20260203220918 with the RLS policy fix
- Policy now checks org-level 'write' permissions instead of
  app-level 'admin' permissions for INSERT operations

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
When creating a new app, the app_id doesn't exist yet, so we can't use
get_identity_org_appid which expects an app_id parameter. Instead, use
get_identity_org_allowed which only needs the org_id.

This fixes the RLS policy to properly check org-level 'write' permissions
when creating apps, which admins and super_admins have through RBAC.

Also added debugging to expose-metadata test to help diagnose any remaining
issues with app creation.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@tests/expose-metadata.test.ts`:
- Around line 113-117: The error logging block that calls createResponse.json()
can throw if the response is not JSON; wrap the JSON parse in a try/catch around
the createResponse.json() call (in the test that checks createResponse.status)
and on failure fall back to await createResponse.text() and include both the
parsing error and the raw text in the process output (e.g., console.error) so
tests don't mask the original HTTP failure; locate the block using the
createResponse variable in tests/expose-metadata.test.ts and update the error
handling accordingly.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@supabase/functions/_backend/public/app/post.ts`:
- Around line 52-60: The post-insert read uses
supabase.from('apps').select().eq('app_id', body.app_id).single() which can
return a same app_id from another org; update the follow-up read to also filter
by owner_org by adding .eq('owner_org', body.owner_org) so the query scopes to
the inserted organization's record (keep the same supabase call and .single()
behavior).
- Around line 43-47: Replace the direct Supabase client usage
(supabaseApikey(...) and the .from('apps').insert(dataInsert) call) with the
Drizzle/PG client pattern: obtain a client via getPgClient() or
getDrizzleClient() from utils/pg.ts, then perform the insert using that client's
API (the Drizzle insert or PG query equivalent) and handle errors via the
returned result/error object; update any variable names and error handling
around the insert to match the new client (e.g., replace dbError handling) so
all DB operations in this handler use getPgClient()/getDrizzleClient() instead
of supabaseApikey.

Comment thread supabase/functions/_backend/public/app/post.ts Outdated
Comment thread supabase/functions/_backend/public/app/post.ts Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
supabase/functions/_backend/public/app/post.ts (1)

28-31: ⚠️ Potential issue | 🟠 Major

Missing validation for owner_org allows permission check bypass.

The condition body.owner_org && !(await checkPermission(...)) short-circuits when owner_org is falsy (empty string, null, undefined), skipping the permission check entirely. The subsequent insert will then either fail at the database level or create an app with invalid ownership.

Add explicit validation for owner_org before the permission check:

🛡️ Proposed fix
+  if (!body.owner_org) {
+    throw simpleError('missing_owner_org', 'Missing owner_org', { body })
+  }
+
   // Check if the user is allowed to create an app in this organization (auth context set by middlewareKey)
-  if (body.owner_org && !(await checkPermission(c, 'org.update_settings', { orgId: body.owner_org }))) {
+  if (!(await checkPermission(c, 'org.update_settings', { orgId: body.owner_org }))) {
     throw quickError(403, 'cannot_access_organization', 'You can\'t access this organization', { org_id: body.owner_org })
   }
🤖 Fix all issues with AI agents
In `@tests/expose-metadata.test.ts`:
- Around line 113-127: The response body is being consumed twice (first with
createResponse.json() then createResponse.text()), so instead read the body as
text once into a variable (e.g., const bodyText = await createResponse.text()),
then try to JSON.parse(bodyText) inside a try/catch to extract structured error
info; use createResponse.status and either the parsed object or the raw bodyText
in the processLogger/console.error message for the App creation failure handling
around the createResponse checks.

Comment on lines +113 to +127
if (createResponse.status !== 200) {
try {
const errorBody = await createResponse.json()
console.error('App creation failed:', createResponse.status, errorBody)
}
catch (error) {
const errorText = await createResponse.text()
console.error('App creation failed: non-JSON response', {
status: createResponse.status,
parseError: error,
text: errorText,
})
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Response body can only be consumed once; .text() will fail after .json() throws.

The Fetch API response body is a ReadableStream that can only be read once. When .json() throws due to a parse failure, the stream is already consumed, so the subsequent .text() call will return an empty string or throw.

Read the body as text first, then attempt to parse it as JSON:

🐛 Proposed fix
       if (createResponse.status !== 200) {
+        const errorText = await createResponse.text()
         try {
-          const errorBody = await createResponse.json()
+          const errorBody = JSON.parse(errorText)
           console.error('App creation failed:', createResponse.status, errorBody)
         }
-        catch (error) {
-          const errorText = await createResponse.text()
+        catch {
           console.error('App creation failed: non-JSON response', {
             status: createResponse.status,
-            parseError: error,
             text: errorText,
           })
         }
       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (createResponse.status !== 200) {
try {
const errorBody = await createResponse.json()
console.error('App creation failed:', createResponse.status, errorBody)
}
catch (error) {
const errorText = await createResponse.text()
console.error('App creation failed: non-JSON response', {
status: createResponse.status,
parseError: error,
text: errorText,
})
}
}
if (createResponse.status !== 200) {
const errorText = await createResponse.text()
try {
const errorBody = JSON.parse(errorText)
console.error('App creation failed:', createResponse.status, errorBody)
}
catch {
console.error('App creation failed: non-JSON response', {
status: createResponse.status,
text: errorText,
})
}
}
🤖 Prompt for AI Agents
In `@tests/expose-metadata.test.ts` around lines 113 - 127, The response body is
being consumed twice (first with createResponse.json() then
createResponse.text()), so instead read the body as text once into a variable
(e.g., const bodyText = await createResponse.text()), then try to
JSON.parse(bodyText) inside a try/catch to extract structured error info; use
createResponse.status and either the parsed object or the raw bodyText in the
processLogger/console.error message for the App creation failure handling around
the createResponse checks.

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Feb 5, 2026

@riderx riderx merged commit 6942d24 into main Feb 6, 2026
11 of 12 checks passed
@riderx riderx deleted the Dalanir/gestion-droits-app branch February 6, 2026 19:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants