mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-15 16:03:02 +00:00
feat: connect to a data table with every role of the job's viewer
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
9fa9b77926
commit
87c87facd2
Generated
+21
-2
@@ -1220,6 +1220,8 @@ parseS3Object(s3Object: S3Object): S3ObjectRecord
|
||||
* @param name - Database/datatable name (default: "main")
|
||||
* @param opts.role - Connect as this data table role instead of the data table's default one.
|
||||
* Only meaningful on a data table under roles, and only for a role you are a tenant of.
|
||||
* @param opts.as - \`"viewer"\` connects with every role of the job's viewer at once, so row-level
|
||||
* security policies see the user of the app rather than its publisher.
|
||||
* @returns SQL template function for building parameterized queries
|
||||
* @example
|
||||
* let sql = wmill.datatable()
|
||||
@@ -1232,6 +1234,9 @@ parseS3Object(s3Object: S3Object): S3ObjectRecord
|
||||
* @example
|
||||
* // Read through a restricted role
|
||||
* let sql = wmill.datatable("main", { role: "analytics" })
|
||||
* @example
|
||||
* // In an app's backend runnable: read what the app's viewer may read
|
||||
* let sql = wmill.datatable("main", { as: "viewer" })
|
||||
*/
|
||||
datatable(name: string = "main", opts?: DatatableOptions): DatatableSqlTemplateFunction
|
||||
|
||||
@@ -2063,6 +2068,8 @@ parseS3Object(s3Object: S3Object): S3ObjectRecord
|
||||
* @param name - Database/datatable name (default: "main")
|
||||
* @param opts.role - Connect as this data table role instead of the data table's default one.
|
||||
* Only meaningful on a data table under roles, and only for a role you are a tenant of.
|
||||
* @param opts.as - \`"viewer"\` connects with every role of the job's viewer at once, so row-level
|
||||
* security policies see the user of the app rather than its publisher.
|
||||
* @returns SQL template function for building parameterized queries
|
||||
* @example
|
||||
* let sql = wmill.datatable()
|
||||
@@ -2075,6 +2082,9 @@ parseS3Object(s3Object: S3Object): S3ObjectRecord
|
||||
* @example
|
||||
* // Read through a restricted role
|
||||
* let sql = wmill.datatable("main", { role: "analytics" })
|
||||
* @example
|
||||
* // In an app's backend runnable: read what the app's viewer may read
|
||||
* let sql = wmill.datatable("main", { as: "viewer" })
|
||||
*/
|
||||
datatable(name: string = "main", opts?: DatatableOptions): DatatableSqlTemplateFunction
|
||||
|
||||
@@ -3012,6 +3022,8 @@ parseS3Object(s3Object: S3Object): S3ObjectRecord
|
||||
* @param name - Database/datatable name (default: "main")
|
||||
* @param opts.role - Connect as this data table role instead of the data table's default one.
|
||||
* Only meaningful on a data table under roles, and only for a role you are a tenant of.
|
||||
* @param opts.as - \`"viewer"\` connects with every role of the job's viewer at once, so row-level
|
||||
* security policies see the user of the app rather than its publisher.
|
||||
* @returns SQL template function for building parameterized queries
|
||||
* @example
|
||||
* let sql = wmill.datatable()
|
||||
@@ -3024,6 +3036,9 @@ parseS3Object(s3Object: S3Object): S3ObjectRecord
|
||||
* @example
|
||||
* // Read through a restricted role
|
||||
* let sql = wmill.datatable("main", { role: "analytics" })
|
||||
* @example
|
||||
* // In an app's backend runnable: read what the app's viewer may read
|
||||
* let sql = wmill.datatable("main", { as: "viewer" })
|
||||
*/
|
||||
datatable(name: string = "main", opts?: DatatableOptions): DatatableSqlTemplateFunction
|
||||
|
||||
@@ -4892,10 +4907,13 @@ def send_teams_message(conversation_id: str, text: str, success: bool = True, ca
|
||||
# role: Connect as this data table role instead of the data table's default one.
|
||||
# Only meaningful on a data table under roles, and only for a role you are a
|
||||
# tenant of.
|
||||
# as_viewer: Connect with every role of the job's viewer at once: the user of the
|
||||
# app the job runs for, or whoever ran the script. The server resolves who that
|
||||
# is. Cannot be combined with role.
|
||||
#
|
||||
# Returns:
|
||||
# DataTableClient instance
|
||||
def datatable(name: str = 'main', *, role: Optional[str] = None)
|
||||
def datatable(name: str = 'main', *, role: Optional[str] = None, as_viewer: bool = False)
|
||||
|
||||
# Get a DuckLake client for DuckDB queries.
|
||||
#
|
||||
@@ -6522,7 +6540,7 @@ data:
|
||||
- \`<datatable>/<table>\` — Specific table in public schema
|
||||
- \`<datatable>/<schema>:<table>\` — Table in specific schema
|
||||
|
||||
**Roles:** when a datatable is under roles, its queries run as a role, which only reaches what it was granted. \`roles\` records the role the app uses each datatable through; the app's code must pass the same role: \`wmill.datatable('main', { role: 'analyst' })\` in TypeScript, \`wmill.datatable('main', role='analyst')\` in Python. A datatable without an entry is used as its default role.
|
||||
**Roles:** when a datatable is under roles, its queries run as a role, which only reaches what it was granted. \`roles\` records the role the app uses each datatable through; the app's code must pass the same role: \`wmill.datatable('main', { role: 'analyst' })\` in TypeScript, \`wmill.datatable('main', role='analyst')\` in Python. A datatable without an entry is used as its default role. To use a datatable with the roles of whoever is viewing the app rather than one fixed role, pass \`{ as: 'viewer' }\` (\`as_viewer=True\` in Python) instead of a role.
|
||||
|
||||
## SQL Migrations (sql_to_apply/)
|
||||
|
||||
@@ -6816,6 +6834,7 @@ Data tables are PostgreSQL databases managed by Windmill. Backend runnables quer
|
||||
3. **No DDL inside runnables**: runnables only read and write rows (SELECT, INSERT, UPDATE, DELETE) on existing tables. Never CREATE, ALTER or DROP a table from a runnable.
|
||||
4. **Qualify table names**: an unqualified name means the \`public\` schema, so write every other table as \`schema.table\`, in table creation and queries alike. The app's \`data\` config sets the default datatable and schema its tables go in; use them consistently across runnables.
|
||||
5. **Pass the role**: when the app's \`data.roles\` gives a data table a role, every \`wmill.datatable\` call on it passes that role (\`wmill.datatable('main', { role: 'analyst' })\` in TypeScript, \`wmill.datatable('main', role='analyst')\` in Python). The role only reaches what it was granted, so a query outside it fails with \`permission denied\`.
|
||||
6. **Per-viewer rows**: a runnable runs as the app's publisher, whoever is using the app. To have a data table under roles answer for the viewer instead, connect as the viewer: \`wmill.datatable('main', { as: 'viewer' })\` in TypeScript, \`wmill.datatable('main', as_viewer=True)\` in Python, \`datatable://main?as=viewer\` as a SQL runnable's database or in a DuckDB \`ATTACH\`. The connection then holds every role the viewer is a tenant of, so row-level security policies written as \`pg_has_role(current_user, '<role>', 'member')\` return that viewer's rows. The server works out who the viewer is; a viewer with no role on the data table, or an anonymous one, is refused. Never combine it with \`role\`.
|
||||
|
||||
\`wmill datatable list\` lists the workspace's data tables. Create or change tables with a migration in \`sql_to_apply/\` (see "SQL Migrations" above), then add them to \`data.tables\` in \`raw_app.yaml\`.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user