From 3d2fa493584fbf62184256e7dc027ab792c40df2 Mon Sep 17 00:00:00 2001 From: Diego Imbert Date: Fri, 4 Sep 2026 15:21:10 +0200 Subject: [PATCH] fix(datatables): the role an app can use is the one it saves MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hiding the picker where there is one usable role meant saving none, and no role means the data table's configured default — which is not filtered by what the caller may use. A member whose only role is `admin` on a data table defaulting to `analyst` got an app whose queries are refused. The role is saved whenever it is not what the data table would resolve to anyway, and the schemas are asked for as that role whether or not the picker is shown. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01S5arH3G2Sa1Qqm32veJQ1n --- backend/ee-repo-ref.txt | 2 +- .../raw_apps/RawAppTemplatePicker.svelte | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt index 95b252bc6d..c76dfb5d71 100644 --- a/backend/ee-repo-ref.txt +++ b/backend/ee-repo-ref.txt @@ -1 +1 @@ -ad5a990e80d107b642265664d40af1aaa915e9a1 +4873f5575a59abf28e2f084b08ef2509000635bc diff --git a/frontend/src/lib/components/raw_apps/RawAppTemplatePicker.svelte b/frontend/src/lib/components/raw_apps/RawAppTemplatePicker.svelte index 89edb723ba..f16172fa13 100644 --- a/frontend/src/lib/components/raw_apps/RawAppTemplatePicker.svelte +++ b/frontend/src/lib/components/raw_apps/RawAppTemplatePicker.svelte @@ -96,6 +96,16 @@ selectedRole !== undefined && loadedRoles.includes(selectedRole) ? selectedRole : undefined ) + // Saved when it is not what the data table would resolve to anyway. Leaving it + // out means "whatever the default is", which is right while they agree and + // wrong when they do not: the default is the data table's, not filtered by + // what this caller may use, so the one usable role has to be named. + const roleToSave = $derived( + effectiveRole !== undefined && effectiveRole !== roles.current.defaultRole + ? effectiveRole + : undefined + ) + // The picked role belongs to the data table it was picked on, and the one it // defaults to is what the app gets without saying anything. Two data tables // can both define an `analyst` that means something different, so the name @@ -126,7 +136,10 @@ // is `undefined`, which the server reads as the data table's configured // default — a different question, and one whose answer says nothing about what // the role finally selected may create. - const accessRole = $derived(showRolePicker ? effectiveRole : undefined) + // Asked as the role the app will run as, whether or not the picker is shown: + // hiding a choice there is only one worth making is not the same as having no + // role, and what a role may create in is the question. + const accessRole = $derived(effectiveRole) // Read like the role list: only once it answers for what is selected now. At // mount it is the initial value, during a switch the previous data table's, // and in between the same data table asked as another role. @@ -221,7 +234,7 @@ type: 'database', resourceType: 'postgresql', resourcePath: `datatable://${selectedDatatable}`, - role: showRolePicker ? effectiveRole : undefined + role: roleToSave } }) await dbOps.onCreateSchema({ schema: newSchemaName }) @@ -238,7 +251,7 @@ tables: formattedTables, datatable: selectedDatatable, schema: effectiveSchema, - role: showRolePicker ? effectiveRole : undefined + role: roleToSave } : { tables: formattedTables, datatable: undefined, schema: undefined, role: undefined }